chore: 重写初始提交(清空历史,整理后全量提交)
This commit is contained in:
@@ -0,0 +1,471 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | YwxApp [ WE CAN DO IT JUST THINK ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: ywxapp<admin@ywxapp.cn>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ywxapp\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
|
||||
/**
|
||||
* AppCommand 应用命令统一入口
|
||||
*
|
||||
* 将 ywxapp/command/app/ 目录下的全部命令(app 脚手架 / 生成器)逻辑内联到本类,
|
||||
* 绑定为单一入口 `php think app <子命令> [参数]`,不再保留独立的命令类文件。
|
||||
* 插件等其它命令(addon:* 系列)由各自独立注册,不在此处理。
|
||||
*
|
||||
* 子命令:
|
||||
* build 构建应用目录(原 build:app)
|
||||
* clear 清理运行时文件(原 clear)
|
||||
* controller 生成控制器(原 app:controller,支持 --api/--plain)
|
||||
* model 生成模型(原 app:model)
|
||||
* event 生成事件(原 app:event)
|
||||
* listener 生成监听器(原 make:listener)
|
||||
* middleware 生成中间件(原 make:middleware)
|
||||
* service 生成服务(原 make:service)
|
||||
* subscribe 生成订阅器(原 make:subscribe)
|
||||
* validate 生成验证器(原 make:validate)
|
||||
*
|
||||
* 用法:
|
||||
* php think app # 列出所有可用子命令
|
||||
* php think app controller app\index # 生成控制器
|
||||
* php think app controller app\index --api # 生成 api 风格控制器
|
||||
* php think app model app\demo # 生成模型
|
||||
* php think app clear --cache # 清理缓存
|
||||
* php think app build myapp # 构建应用目录
|
||||
*/
|
||||
class AppCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Make 型子命令配置
|
||||
* type : 类型标签(用于成功/存在提示)
|
||||
* stub : stub 文件名(不含 .stub 扩展)
|
||||
* ns : 命名空间末尾段(如 controller/model/event ...)
|
||||
* suffix : 类名后缀(仅 controller 在开启 controller_suffix 时追加 Controller)
|
||||
*/
|
||||
private const MAKE_TYPES = [
|
||||
'controller' => ['type' => 'Controller', 'stub' => 'controller', 'ns' => 'controller', 'suffix' => 'Controller'],
|
||||
'model' => ['type' => 'Model', 'stub' => 'model', 'ns' => 'model', 'suffix' => ''],
|
||||
'event' => ['type' => 'Event', 'stub' => 'event', 'ns' => 'event', 'suffix' => ''],
|
||||
'listener' => ['type' => 'Listener', 'stub' => 'listener', 'ns' => 'listener', 'suffix' => ''],
|
||||
'middleware' => ['type' => 'Middleware', 'stub' => 'middleware', 'ns' => 'middleware', 'suffix' => ''],
|
||||
'service' => ['type' => 'Service', 'stub' => 'service', 'ns' => 'service', 'suffix' => ''],
|
||||
'subscribe' => ['type' => 'Subscribe', 'stub' => 'subscribe', 'ns' => 'subscribe', 'suffix' => ''],
|
||||
'validate' => ['type' => 'Validate', 'stub' => 'validate', 'ns' => 'validate', 'suffix' => ''],
|
||||
];
|
||||
|
||||
/**
|
||||
* 应用基础目录(Build 使用)
|
||||
* @var string
|
||||
*/
|
||||
private $basePath = '';
|
||||
|
||||
/**
|
||||
* stub 模板目录(已从 ywxapp/command/app/stubs 上移至此)
|
||||
*/
|
||||
private function stubDir(): string
|
||||
{
|
||||
return __DIR__ . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置命令
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('app')
|
||||
->addOption('list', 'l', Option::VALUE_NONE, '列出所有可用子命令')
|
||||
->addOption('api', null, Option::VALUE_NONE, '生成 api 风格控制器(仅 controller)')
|
||||
->addOption('plain', null, Option::VALUE_NONE, '生成空控制器(仅 controller)')
|
||||
->addOption('cache', 'c', Option::VALUE_NONE, '清理缓存目录(仅 clear)')
|
||||
->addOption('log', null, Option::VALUE_NONE, '清理日志目录(仅 clear)')
|
||||
->addOption('dir', 'r', Option::VALUE_NONE, '清理空目录(仅 clear)')
|
||||
->addArgument('subcommand', Argument::OPTIONAL, '子命令名(build/clear/controller/model/event/listener/middleware/service/subscribe/validate)')
|
||||
->addArgument('name', Argument::OPTIONAL, '类名或 app 名(如 app\\index 或 myapp)')
|
||||
->setDescription('YwxApp 应用命令入口:聚合 ywxapp/command/app 下的命令,php think app <子命令> [参数]');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行命令
|
||||
*/
|
||||
protected function execute(Input $input, Output $output): int
|
||||
{
|
||||
if ($input->getOption('list') || empty($input->getArgument('subcommand'))) {
|
||||
$this->showList($output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sub = $input->getArgument('subcommand');
|
||||
$name = (string) $input->getArgument('name');
|
||||
|
||||
if (isset(self::MAKE_TYPES[$sub])) {
|
||||
return $this->runMake($sub, $name, $input, $output);
|
||||
}
|
||||
if ($sub === 'build') {
|
||||
return $this->runBuild($name, $output);
|
||||
}
|
||||
if ($sub === 'clear') {
|
||||
return $this->runClear($name, $input, $output);
|
||||
}
|
||||
|
||||
$output->writeln('<error>未知子命令: ' . $sub . '</error>');
|
||||
$this->showList($output);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有可用子命令
|
||||
*/
|
||||
private function showList(Output $output): void
|
||||
{
|
||||
$output->writeln('<info>YwxApp 应用命令(`php think app <子命令> [参数]`):</info>');
|
||||
$output->writeln('');
|
||||
$width = max(array_map('strlen', array_keys(self::MAKE_TYPES + ['build' => 1, 'clear' => 1])));
|
||||
foreach (array_keys(['build' => 1, 'clear' => 1] + self::MAKE_TYPES) as $name) {
|
||||
$desc = [
|
||||
'build' => '构建应用目录',
|
||||
'clear' => '清理运行时文件',
|
||||
'controller' => '生成控制器(--api/--plain)',
|
||||
'model' => '生成模型',
|
||||
'event' => '生成事件',
|
||||
'listener' => '生成监听器',
|
||||
'middleware' => '生成中间件',
|
||||
'service' => '生成服务',
|
||||
'subscribe' => '生成订阅器',
|
||||
'validate' => '生成验证器',
|
||||
][$name];
|
||||
$output->writeln(' ' . str_pad($name, $width) . ' ' . $desc);
|
||||
}
|
||||
$output->writeln('');
|
||||
$output->writeln('示例:php think app controller app\\index');
|
||||
$output->writeln(' php think app clear --cache');
|
||||
}
|
||||
|
||||
/* ===================== Make 型生成器 ===================== */
|
||||
|
||||
/**
|
||||
* 运行 Make 型子命令
|
||||
*/
|
||||
private function runMake(string $type, string $name, Input $input, Output $output): int
|
||||
{
|
||||
if ($name === '') {
|
||||
$output->writeln('<error>缺少类名参数,用法:php think app ' . $type . ' <类名></error>');
|
||||
return 1;
|
||||
}
|
||||
|
||||
$cfg = self::MAKE_TYPES[$type];
|
||||
$classname = $this->makeClassName($type, $name);
|
||||
$pathname = $this->makePathName($classname);
|
||||
|
||||
if (is_file($pathname)) {
|
||||
$output->writeln('<error>' . $cfg['type'] . ':' . $classname . ' already exists!</error>');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!is_dir(dirname($pathname))) {
|
||||
mkdir(dirname($pathname), 0755, true);
|
||||
}
|
||||
|
||||
file_put_contents($pathname, $this->buildClass($classname, $type, $input));
|
||||
|
||||
$output->writeln('<info>' . $cfg['type'] . ':' . $classname . ' created successfully.</info>');
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 stub 路径(controller 支持 --api/--plain)
|
||||
*/
|
||||
private function makeStub(string $type, Input $input): string
|
||||
{
|
||||
if ($type === 'controller') {
|
||||
if ($input->getOption('api')) {
|
||||
return $this->stubDir() . 'controller.api.stub';
|
||||
}
|
||||
if ($input->getOption('plain')) {
|
||||
return $this->stubDir() . 'controller.plain.stub';
|
||||
}
|
||||
return $this->stubDir() . 'controller.stub';
|
||||
}
|
||||
|
||||
return $this->stubDir() . self::MAKE_TYPES[$type]['stub'] . '.stub';
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析命名空间
|
||||
*/
|
||||
private function makeNamespace(string $type, string $app): string
|
||||
{
|
||||
return 'app' . ($app ? '\\' . $app : '') . '\\' . self::MAKE_TYPES[$type]['ns'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析完整类名(等价于 think\console\command\Make::getClassName + Controller 后缀)
|
||||
*/
|
||||
private function makeClassName(string $type, string $name): string
|
||||
{
|
||||
if (str_contains($name, '\\')) {
|
||||
$class = $name;
|
||||
} else {
|
||||
$app = '';
|
||||
if (str_contains($name, '@')) {
|
||||
[$app, $name] = explode('@', $name);
|
||||
}
|
||||
if (str_contains($name, '/')) {
|
||||
$name = str_replace('/', '\\', $name);
|
||||
}
|
||||
$class = $this->makeNamespace($type, $app) . '\\' . $name;
|
||||
}
|
||||
|
||||
$suffix = self::MAKE_TYPES[$type]['suffix'];
|
||||
if ($suffix !== '' && $this->app->config->get('route.controller_suffix')) {
|
||||
$class .= $suffix;
|
||||
}
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析文件路径(等价于各 Make 命令重写的 getPathName)
|
||||
*/
|
||||
private function makePathName(string $name): string
|
||||
{
|
||||
return $this->app->getRootPath() . ltrim(str_replace('\\', '/', $name), '/') . '.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* 用 stub 渲染类文件(等价于 think\console\command\Make::buildClass)
|
||||
*/
|
||||
private function buildClass(string $name, string $type, Input $input): string
|
||||
{
|
||||
$stub = file_get_contents($this->makeStub($type, $input));
|
||||
|
||||
$namespace = trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
|
||||
$class = str_replace($namespace . '\\', '', $name);
|
||||
|
||||
return str_replace(
|
||||
['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'],
|
||||
[
|
||||
$class,
|
||||
$this->app->config->get('route.action_suffix'),
|
||||
$namespace,
|
||||
$this->app->getNamespace(),
|
||||
],
|
||||
$stub
|
||||
);
|
||||
}
|
||||
|
||||
/* ===================== Build 命令 ===================== */
|
||||
|
||||
/**
|
||||
* 运行 build 子命令(原 build:app)
|
||||
*/
|
||||
private function runBuild(string $app, Output $output): int
|
||||
{
|
||||
$this->basePath = $this->app->getRootPath() . 'app' . DIRECTORY_SEPARATOR;
|
||||
$app = $app ?: '';
|
||||
|
||||
if (is_file($this->basePath . 'build.php')) {
|
||||
$list = include $this->basePath . 'build.php';
|
||||
} else {
|
||||
$list = [
|
||||
'__dir__' => ['controller', 'model', 'view'],
|
||||
];
|
||||
}
|
||||
|
||||
$this->buildApp($app, $list);
|
||||
$output->writeln('<info>Successed</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建应用
|
||||
*/
|
||||
private function buildApp(string $app, array $list = []): void
|
||||
{
|
||||
if (!is_dir($this->basePath . $app)) {
|
||||
mkdir($this->basePath . $app);
|
||||
}
|
||||
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
$namespace = 'app' . ($app ? '\\' . $app : '');
|
||||
|
||||
$this->buildCommon($app);
|
||||
$this->buildHello($app, $namespace);
|
||||
$this->buildapp1($app, $namespace);
|
||||
$this->buildInfo($app);
|
||||
|
||||
foreach ($list as $path => $file) {
|
||||
if ('__dir__' == $path) {
|
||||
foreach ($file as $dir) {
|
||||
$this->checkDirBuild($appPath . $dir);
|
||||
}
|
||||
} elseif ('__file__' == $path) {
|
||||
foreach ($file as $fname) {
|
||||
if (!is_file($appPath . $fname)) {
|
||||
file_put_contents($appPath . $fname, 'php' == pathinfo($fname, PATHINFO_EXTENSION) ? '<?php' . PHP_EOL : '');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($file as $val) {
|
||||
$val = trim($val);
|
||||
$filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . '.php';
|
||||
$space = $namespace . '\\' . $path;
|
||||
$class = $val;
|
||||
switch ($path) {
|
||||
case 'controller':
|
||||
if ($this->app->config->get('route.controller_suffix')) {
|
||||
$filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . 'Controller.php';
|
||||
$class = $val . 'Controller';
|
||||
}
|
||||
$content = '<?php' . PHP_EOL . 'namespace ' . $space . ';' . PHP_EOL . PHP_EOL . 'class ' . $class . PHP_EOL . '{' . PHP_EOL . PHP_EOL . '}';
|
||||
break;
|
||||
case 'model':
|
||||
$content = '<?php' . PHP_EOL . 'namespace ' . $space . ';' . PHP_EOL . PHP_EOL . 'use think\Model;' . PHP_EOL . PHP_EOL . 'class ' . $class . ' extends Model' . PHP_EOL . '{' . PHP_EOL . PHP_EOL . '}';
|
||||
break;
|
||||
case 'view':
|
||||
$filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . '.html';
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
$content = '';
|
||||
break;
|
||||
default:
|
||||
$content = '<?php' . PHP_EOL . 'namespace ' . $space . ';' . PHP_EOL . PHP_EOL . 'class ' . $class . PHP_EOL . '{' . PHP_EOL . PHP_EOL . '}';
|
||||
}
|
||||
|
||||
if (!is_file($filename)) {
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建应用的欢迎页面
|
||||
*/
|
||||
private function buildHello(string $app, string $namespace): void
|
||||
{
|
||||
$suffix = $this->app->config->get('route.controller_suffix') ? 'Controller' : '';
|
||||
$filename = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '') . 'controller' . DIRECTORY_SEPARATOR . 'Index' . $suffix . '.php';
|
||||
|
||||
if (!is_file($filename)) {
|
||||
$content = file_get_contents($this->stubDir() . 'controller.stub');
|
||||
$content = str_replace(
|
||||
['{%className%}', '{%actionSuffix%}', '{%namespace%}', '{%app_namespace%}'],
|
||||
['Index', $suffix, $namespace . '\\controller', $this->app->getNamespace()],
|
||||
$content
|
||||
);
|
||||
$this->checkDirBuild(dirname($filename));
|
||||
file_put_contents($filename, $content);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildapp1(string $app, string $namespace): void
|
||||
{
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
if (!is_file($appPath . 'app.php')) {
|
||||
$content = file_get_contents($this->stubDir() . 'app.stub');
|
||||
$content = str_replace(['{%namespace%}', '{%app%}', '{%className%}'], [$namespace, $app, 'app'], $content);
|
||||
file_put_contents($appPath . 'app.php', $content);
|
||||
}
|
||||
}
|
||||
|
||||
private function buildInfo(string $app): void
|
||||
{
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
if (!is_file($appPath . 'info.php')) {
|
||||
$array = [
|
||||
'name' => '',
|
||||
'title' => '',
|
||||
'intro' => '',
|
||||
'author' => '',
|
||||
'website' => '',
|
||||
'version' => '1.0.0',
|
||||
'state' => 1,
|
||||
'url' => '',
|
||||
'license' => '',
|
||||
'licenseto' => 0,
|
||||
];
|
||||
$array = array_merge($array, ['name' => $app, 'url' => '/' . $app]);
|
||||
$output = "<?php\n return " . var_export($array, true) . ";\n?>";
|
||||
file_put_contents($appPath . 'info.php', $output);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建应用的公共文件
|
||||
*/
|
||||
private function buildCommon(string $app): void
|
||||
{
|
||||
$appPath = $this->basePath . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
foreach (['event', 'middleware', 'common', 'config'] as $nm) {
|
||||
if (!is_file($appPath . $nm . '.php')) {
|
||||
file_put_contents($appPath . $nm . '.php', '<?php' . PHP_EOL . '// 这是系统自动生成的' . $nm . '定义文件' . PHP_EOL . 'return [' . PHP_EOL . PHP_EOL . '];' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建目录
|
||||
*/
|
||||
private function checkDirBuild(string $dirname): void
|
||||
{
|
||||
if (!is_dir($dirname)) {
|
||||
mkdir($dirname, 0755, true);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===================== Clear 命令 ===================== */
|
||||
|
||||
/**
|
||||
* 运行 clear 子命令(原 clear)
|
||||
*/
|
||||
private function runClear(string $app, Input $input, Output $output): int
|
||||
{
|
||||
$runtimePath = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . ($app ? $app . DIRECTORY_SEPARATOR : '');
|
||||
|
||||
if ($input->getOption('cache')) {
|
||||
$path = $runtimePath . 'cache';
|
||||
} elseif ($input->getOption('log')) {
|
||||
$path = $runtimePath . 'log';
|
||||
} else {
|
||||
$path = $runtimePath;
|
||||
}
|
||||
|
||||
$rmdir = $input->getOption('dir') ? true : false;
|
||||
$this->clearRuntime(rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR, $rmdir);
|
||||
|
||||
$output->writeln('<info>Clear Successed</info>');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归清理目录
|
||||
*/
|
||||
private function clearRuntime(string $path, bool $rmdir): void
|
||||
{
|
||||
$files = is_dir($path) ? scandir($path) : [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ('.' != $file && '..' != $file && is_dir($path . $file)) {
|
||||
array_map('unlink', glob($path . $file . DIRECTORY_SEPARATOR . '*.*'));
|
||||
if ($rmdir) {
|
||||
rmdir($path . $file);
|
||||
}
|
||||
} elseif ('.gitignore' != $file && is_file($path . $file)) {
|
||||
unlink($path . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user