// +---------------------------------------------------------------------- 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; use think\console\command\Make; /** * 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 Make { /** * 应用基础目录(Build 使用) * @var string */ private $basePath = ''; private $subCommon = ''; protected function getStub(): string { if ($this->input->getOption('api')) { return __DIR__ . DIRECTORY_SEPARATOR . 'appstubs' . DIRECTORY_SEPARATOR . $this->subCommon . '.api.stub'; } if ($this->input->getOption('plain')) { return __DIR__ . DIRECTORY_SEPARATOR . 'appstubs' . DIRECTORY_SEPARATOR . $this->subCommon . '.plain.stub'; } return __DIR__ . DIRECTORY_SEPARATOR . 'appstubs' . DIRECTORY_SEPARATOR . $this->subCommon . '.stub'; } protected function getNamespace(string $app): string { if ($this->subCommon === 'model') { return 'ywxapp\\model'; } return parent::getNamespace($app) . '\\' . $this->subCommon; } protected function getClassName(string $name): string { if ($this->subCommon == 'controller') { return parent::getClassName($name) . ($this->app->config->get('route.controller_suffix') ? 'Controller' : ''); } return parent::getClassName($name); } protected function getPathName(string $name): string { if ($this->subCommon == 'model') { $name = substr($name, 6); return $this->app->getRootPath() . 'ywxapp' . '/' . ltrim(str_replace('\\', '/', $name), '/') . '.php'; } $name = substr($name, 4); return $this->app->getBasePath() . ltrim(str_replace('\\', '/', $name), '/') . '.php'; } /** * 配置命令 */ 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) { $this->subCommon = $input->getArgument('subcommand'); $name = trim($input->getArgument('name')); if ($this->subCommon === 'build') { return $this->runBuild($name, $output); } if ($this->subCommon === 'clear') { return $this->runClear($name, $input, $output); } $classname = $this->getClassName($name); $pathname = $this->getPathName($classname); if (is_file($pathname)) { $output->writeln('' . $this->type . ':' . $classname . ' already exists!'); return false; } if (!is_dir(dirname($pathname))) { mkdir(dirname($pathname), 0755, true); } file_put_contents($pathname, $this->buildClass($classname)); $output->writeln('' . $this->type . ':' . $classname . ' created successfully.'); } protected function buildClass(string $name) { $stub = file_get_contents($this->getStub()); $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 = [ '__file__' => ['common.php', 'event.php', 'middleware.php', 'service.php'], '__dir__' => ['controller', 'model', 'view', 'route'], 'controller' => ['Index'], 'model' => [], 'view' => ['index/index'], 'route' => ['app'], ]; } $this->buildApp($app, $list); $output->writeln('Successed'); 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 : ''); 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) ? 'app->config->get('route.controller_suffix')) { $filename = $appPath . $path . DIRECTORY_SEPARATOR . $val . 'Controller.php'; $class = $val . 'Controller'; } $content = 'checkDirBuild(dirname($filename)); $content = ''; break; default: $content = '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('Clear Successed'); 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); } } } }