make commands lazily loaded

This commit is contained in:
sunxyw
2022-12-17 22:18:50 +08:00
parent 3806983bc2
commit 44c25f7fdd
8 changed files with 98 additions and 50 deletions

View File

@@ -13,6 +13,8 @@ use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'build', description: '将项目构建一个phar包')] #[AsCommand(name: 'build', description: '将项目构建一个phar包')]
class BuildCommand extends Command class BuildCommand extends Command
{ {
use NonPharLoadModeOnly;
/** /**
* 配置 * 配置
*/ */

View File

@@ -5,57 +5,52 @@ declare(strict_types=1);
namespace ZM\Command; namespace ZM\Command;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'check:config', description: '检查配置文件是否和框架当前版本有更新')] #[AsCommand(name: 'check:config', description: '检查配置文件是否和框架当前版本有更新')]
class CheckConfigCommand extends Command class CheckConfigCommand extends Command
{ {
use SourceLoadModeOnly;
private $need_update = false; private $need_update = false;
protected function execute(InputInterface $input, OutputInterface $output): int protected function handle(): int
{ {
if (LOAD_MODE !== 1) {
$output->writeln('<error>仅限在Composer依赖模式中使用此命令</error>');
return 1;
}
$current_cfg = getcwd() . '/config/'; $current_cfg = getcwd() . '/config/';
$remote_cfg = include_once FRAMEWORK_ROOT_DIR . '/config/global_old.php'; $remote_cfg = include FRAMEWORK_ROOT_DIR . '/config/global_old.php';
if (file_exists($current_cfg . 'global.php')) { if (file_exists($current_cfg . 'global.php')) {
$this->check($remote_cfg, 'global.php', $output); $this->check($remote_cfg, 'global.php');
} }
if (file_exists($current_cfg . 'global.development.php')) { if (file_exists($current_cfg . 'global.development.php')) {
$this->check($remote_cfg, 'global.development.php', $output); $this->check($remote_cfg, 'global.development.php');
} }
if (file_exists($current_cfg . 'global.staging.php')) { if (file_exists($current_cfg . 'global.staging.php')) {
$this->check($remote_cfg, 'global.staging.php', $output); $this->check($remote_cfg, 'global.staging.php');
} }
if (file_exists($current_cfg . 'global.production.php')) { if (file_exists($current_cfg . 'global.production.php')) {
$this->check($remote_cfg, 'global.production.php', $output); $this->check($remote_cfg, 'global.production.php');
} }
if ($this->need_update === true) { if ($this->need_update === true) {
$output->writeln('<comment>有配置文件需要更新,详情见文档 `https://framework.zhamao.xin/update/config`</comment>'); $this->comment('有配置文件需要更新,详情见文档 `https://framework.zhamao.xin/update/config`');
} else { } else {
$output->writeln('<info>配置文件暂无更新!</info>'); $this->info('配置文件暂无更新!');
} }
return 0; return self::SUCCESS;
} }
/** /**
* @param mixed $remote * @param mixed $remote
* @param mixed $local * @param mixed $local
*/ */
private function check($remote, $local, OutputInterface $out) private function check($remote, $local)
{ {
$local_file = include_once WORKING_DIR . '/config/' . $local; $local_file = include WORKING_DIR . '/config/' . $local;
if ($local_file === true) { if ($local_file === true) {
$local_file = config('global'); $local_file = config('global');
} }
foreach ($remote as $k => $v) { foreach ($remote as $k => $v) {
if (!isset($local_file[$k])) { if (!isset($local_file[$k])) {
$out->writeln('<comment>配置文件 ' . $local . " 需要更新!(当前配置文件缺少 `{$k}` 字段配置)</comment>"); $this->comment("配置文件 {$local} 需要更新!当前配置文件缺少 `{$k}` 字段配置");
$this->need_update = true; $this->need_update = true;
} }
} }

View File

@@ -32,7 +32,20 @@ abstract class Command extends \Symfony\Component\Console\Command\Command
{ {
$this->input = $input; $this->input = $input;
$this->output = $output; $this->output = $output;
return $this->handle(); if ($this->shouldExecute()) {
return $this->handle();
}
return self::SUCCESS;
}
/**
* 是否应该执行
*
* @return bool 返回 true 以继续执行,返回 false 以中断执行
*/
protected function shouldExecute(): bool
{
return true;
} }
/** /**

View File

@@ -13,6 +13,8 @@ use ZM\Exception\InitException;
#[AsCommand(name: 'init', description: '初始化框架运行的基础文件')] #[AsCommand(name: 'init', description: '初始化框架运行的基础文件')]
class InitCommand extends Command class InitCommand extends Command
{ {
use NonPharLoadModeOnly;
private string $base_path; private string $base_path;
private bool $force = false; private bool $force = false;

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace ZM\Command;
trait NonPharLoadModeOnly
{
protected function shouldExecute(): bool
{
if (\Phar::running() === '') {
return true;
}
if (method_exists($this, 'error')) {
$this->error('此命令只能在非 Phar 模式下使用!');
}
return false;
}
}

View File

@@ -14,7 +14,7 @@ use ZM\Exception\ZMKnownException;
use ZM\Framework; use ZM\Framework;
use ZM\Process\ProcessStateManager; use ZM\Process\ProcessStateManager;
#[AsCommand(name: 'server:start', description: '启动服务器', aliases: ['server'])] #[AsCommand(name: 'server', description: '启动服务器', aliases: ['server:start'])]
class ServerStartCommand extends ServerCommand class ServerStartCommand extends ServerCommand
{ {
public static function exportOptionArray(): array public static function exportOptionArray(): array

View File

@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace ZM\Command;
trait SourceLoadModeOnly
{
protected function shouldExecute(): bool
{
if (LOAD_MODE === LOAD_MODE_SRC) {
return true;
}
if (method_exists($this, 'error')) {
$this->error('此命令只能在源码模式下使用!');
}
return false;
}
}

View File

@@ -4,22 +4,13 @@ declare(strict_types=1);
namespace ZM; namespace ZM;
use Phar;
use Symfony\Component\Console\Application; use Symfony\Component\Console\Application;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\CommandLoader\FactoryCommandLoader;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use ZM\Command\BotCraft\BotCraftCommand;
use ZM\Command\BuildCommand;
use ZM\Command\CheckConfigCommand;
use ZM\Command\Generate\SystemdGenerateCommand;
use ZM\Command\InitCommand;
use ZM\Command\ProxyServerCommand;
use ZM\Command\ReplCommand;
use ZM\Command\Server\ServerReloadCommand;
use ZM\Command\Server\ServerStartCommand;
use ZM\Command\Server\ServerStatusCommand;
use ZM\Command\Server\ServerStopCommand;
use ZM\Exception\SingletonViolationException; use ZM\Exception\SingletonViolationException;
use ZM\Store\FileSystem;
/** /**
* 命令行启动的入口文件,用于初始化环境变量,并启动命令行应用 * 命令行启动的入口文件,用于初始化环境变量,并启动命令行应用
@@ -37,24 +28,29 @@ final class ConsoleApplication extends Application
} }
// 初始化命令 // 初始化命令
$this->add(new ServerStatusCommand()); // server运行状态 $command_classes = FileSystem::getClassesPsr4(zm_dir('src/ZM/Command'), 'ZM\\Command');
$this->add(new ServerReloadCommand()); // server重载 $commands = [];
$this->add(new ServerStopCommand()); // server停止 foreach ($command_classes as $command_class) {
$this->add(new ServerStartCommand()); // 运行主服务的指令控制器 try {
$this->add(new SystemdGenerateCommand()); // 生成systemd文件 $command_class_ref = new \ReflectionClass($command_class);
$this->add(new BotCraftCommand()); // 用于从命令行创建插件 } catch (\ReflectionException $e) {
$this->add(new ReplCommand()); // 交互式控制台 logger()->error("命令 {$command_class} 无法加载!反射失败:" . $e->getMessage());
$this->add(new ProxyServerCommand()); // HTTP 代理服务器 continue;
if (LOAD_MODE === 1) { // 如果是 Composer 模式加载的,那么可以输入 check:config 命令,检查配置文件是否需要更新 }
$this->add(new CheckConfigCommand()); if ($command_class_ref->isAbstract()) {
} continue;
if (\Phar::running() === '') { // 不是 Phar 模式的话,可以执行打包解包初始化命令 }
$this->add(new BuildCommand()); // 用于将整个应用打包为一个可执行的 phar // 从 AsCommand 注解中获取命令名称
$this->add(new InitCommand()); // 用于在 Composer 模式启动下,初始化脚手架文件 $attr = $command_class_ref->getAttributes(AsCommand::class);
// $this->add(new PluginPackCommand()); // 用于打包一个子模块为 phar 并进行分发 if (count($attr) > 0) {
// $this->add(new PluginListCommand()); // 用于列出已配置的子模块列表(存在 zm.json 文件的目录) $commands[$attr[0]->getArguments()['name']] = fn () => new $command_class();
// $this->add(new PluginUnpackCommand()); // 用于将打包好的 phar 模块解包到 src 目录中 } else {
logger()->warning("命令 {$command_class} 没有使用 AsCommand 注解,无法被加载");
}
} }
// 命令工厂,用于延迟加载命令
$command_loader = new FactoryCommandLoader($commands);
$this->setCommandLoader($command_loader);
self::$obj = $this; // 用于标记已经初始化完成 self::$obj = $this; // 用于标记已经初始化完成
parent::__construct($name, ZM_VERSION); parent::__construct($name, ZM_VERSION);