// +----------------------------------------------------------------------
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);
}
}
}
}