- 18 张公共表更名(addon/attachment/configure/links/spider_log/spider_stat/sms/notice/ad/task/prop/medal/help/card -> common_*,member_wallets->member_wallet,score_rule/score_log -> member_*,addon_config->common_addonconf),模型全部对齐新表名,Db 直引用清零 - Attachment 模型补 表名绑定,修复富文本上传查 wxapp_attachment 1146 隐患 - install.sql + 迁移 SQL:backend_admin/backend_role delete_at 默认 0,修复软删除(NULL != 0)误过滤导致后台菜单为空 - AppService::boot() 支持插件 info.php 声明 commands 自动注册插件命令(psr-4 自动加载,坏类名自动跳过) - 各插件(haonav/mqttbroker/wxchat/articles/blog/forum 等)字段与配置同步调整 - 框架版本 1.1.0 -> 1.1.1
289 lines
12 KiB
PHP
289 lines
12 KiB
PHP
<?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;
|
||
|
||
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('<error>' . $this->type . ':' . $classname . ' already exists!</error>');
|
||
return false;
|
||
}
|
||
if (!is_dir(dirname($pathname))) {
|
||
mkdir(dirname($pathname), 0755, true);
|
||
}
|
||
file_put_contents($pathname, $this->buildClass($classname));
|
||
$output->writeln('<info>' . $this->type . ':' . $classname . ' created successfully.</info>');
|
||
}
|
||
|
||
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('<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 : '');
|
||
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 'route':
|
||
$content = "<?php" . PHP_EOL . PHP_EOL . "use think\\facade\Route;";
|
||
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 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);
|
||
}
|
||
}
|
||
}
|
||
}
|