diff --git a/bin/phpunit-zm b/bin/phpunit-zm
index ae1717a2..0b07a84a 100755
--- a/bin/phpunit-zm
+++ b/bin/phpunit-zm
@@ -57,14 +57,13 @@ $options['driver'] = 'workerman';
$options['worker-num'] = 1;
$options['private-mode'] = true;
-// TODO: optimize this, maybe abstract the application (framework)
-$bootstrappers = new ReflectionProperty(\ZM\ConsoleApplication::class, 'bootstrappers');
-foreach ($bootstrappers->getDefaultValue() as $bootstrapper) {
- resolve($bootstrapper)->bootstrap($options);
-}
-
try {
- (new Framework($options))->init()->start();
+ $framework = new Framework();
+ $framework->runtime_preferences = $framework->runtime_preferences
+ ->withConfigDir(dirname(__DIR__) . '/config')
+ ->withEnvironment('development');
+ $framework->bootstrap();
+ $framework->init()->start();
exit($_swoole_atomic->get());
} catch (Throwable $e) {
echo $e->getMessage() . PHP_EOL;
diff --git a/config/container.php b/config/container.php
index 9e7cc42c..684f31b0 100644
--- a/config/container.php
+++ b/config/container.php
@@ -7,8 +7,8 @@ use OneBot\Driver\Process\ProcessManager;
use Psr\Log\LoggerInterface;
use ZM\Config\Environment;
use ZM\Config\EnvironmentInterface;
-use ZM\Config\ZMConfig;
use ZM\Framework;
+use ZM\HasRuntimeInfo;
/*
* 这里是容器的配置文件,你可以在这里配置容器的绑定和其他一些参数。
@@ -28,6 +28,8 @@ return [
Driver::class => fn () => Framework::getInstance()->getDriver(),
LoggerInterface::class => fn () => logger(),
EnvironmentInterface::class => Environment::class,
+
+ HasRuntimeInfo::class => Framework::class,
],
// 容器的缓存配置,默认情况下,只有在生产环境下才会启用缓存
@@ -37,7 +39,7 @@ return [
// 详细介绍请参阅:https://php-di.org/doc/performances.html#caching
'cache' => [
// 是否启用缓存,支持 bool、callable
- 'enable' => fn () => ZMConfig::getInstance()->getEnvironment() === 'production',
+ 'enable' => fn () => Framework::getInstance()->runtime_preferences->environment('production'),
'namespace' => 'zm',
],
];
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 7f81ee24..1ceb3339 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -28,6 +28,6 @@
-
+
diff --git a/src/Module/Example/Hello123.php b/src/Module/Example/Hello123.php
index 135d0b9e..94fd6381 100644
--- a/src/Module/Example/Hello123.php
+++ b/src/Module/Example/Hello123.php
@@ -5,15 +5,11 @@ declare(strict_types=1);
namespace Module\Example;
use Choir\Http\ServerRequest;
-use Choir\WebSocket\FrameInterface;
-use OneBot\Driver\Coroutine\Adaptive;
use OneBot\Driver\Event\WebSocket\WebSocketMessageEvent;
use ZM\Annotation\Framework\BindEvent;
-use ZM\Annotation\Framework\Cron;
use ZM\Annotation\Http\Route;
use ZM\Annotation\Middleware\Middleware;
use ZM\Annotation\OneBot\BotCommand;
-use ZM\Annotation\OneBot\BotEvent;
use ZM\Annotation\OneBot\CommandArgument;
use ZM\Annotation\OneBot\CommandHelp;
use ZM\Context\BotContext;
@@ -21,22 +17,22 @@ use ZM\Middleware\TimerMiddleware;
class Hello123
{
+ #[BindEvent(WebSocketMessageEvent::class, level: 5000)]
+ public function onMessage(WebSocketMessageEvent $event)
+ {
+ $Data = json_decode($event->getFrame()->getData(), true);
+ }
+
#[Route('/route', request_method: ['GET'])]
#[Route('/route/{id}', request_method: ['GET'])]
#[Middleware(TimerMiddleware::class)]
- public function route(array $params, ServerRequest $request, \HttpRequestEvent $event)
+ public function route(array $params, ServerRequest $request, \HttpRequestEvent $event, BotContext $ctx)
{
// 目前因内部实现限制,路由方法的参数必须按照这个顺序定义,可以省略,但是不能乱序
// 如果希望获取其他依赖,可以在现有参数后面继续添加
return 'Hello Zhamao!This is the first 3.0 page!' . ($params['id'] ?? '');
}
- #[BotEvent()]
- public function onOBEvent(WebSocketMessageEvent $messageEvent, \OneBotEvent $event): void
- {
- logger()->info("收到了 {$event->getType()}.{$event->getDetailType()} 事件");
- }
-
#[BotCommand('echo', 'echo')]
#[CommandArgument('text', '要回复的内容', required: true)]
#[CommandHelp('复读机', '只需要发送 echo+内容 即可自动复读', 'echo 你好 会回复 你好')]
@@ -44,25 +40,4 @@ class Hello123
{
$context->reply($event->getMessage());
}
-
- #[BindEvent(WebSocketMessageEvent::class)]
- public function onWSMessage(FrameInterface $frame, WebSocketMessageEvent $event): void
- {
- logger()->info('收到了 WebSocket 消息');
- }
-
- #[Cron('* * * * *', no_overlap: true)]
- public function logTime(): void
- {
- $time = date('Y-m-d H:i:s');
- logger()->info('我看到时间了,让我写下来');
- Adaptive::sleep(5);
- logger()->info('写好啦,时间是' . $time);
- }
-
- #[Cron('* * * * *')]
- public function logTime2(): void
- {
- logger()->info('我不需要等,但也不给你看时间');
- }
}
diff --git a/src/ZM/Bootstrap/Bootstrapper.php b/src/ZM/Bootstrap/Bootstrapper.php
new file mode 100644
index 00000000..05f2290d
--- /dev/null
+++ b/src/ZM/Bootstrap/Bootstrapper.php
@@ -0,0 +1,12 @@
+loadEnvVariables($env);
- $config_i = config();
- $config_i->addConfigPath($this->getConfigDir($config));
- $config_i->setEnvironment($this->getConfigEnvironment($config));
- $this->parseArgvToConfig($config, $config_i);
- }
-
- private function getConfigDir(array $config): string
- {
- $config_dir = $config['config-dir'] ?? null;
- // 默认配置文件目录
- $find_dir = [
- WORKING_DIR . '/config',
- SOURCE_ROOT_DIR . '/config',
- ];
- // 如果启动参数指定了配置文件目录,则优先使用
- if ($config_dir !== null) {
- array_unshift($find_dir, $config_dir);
- }
-
- // 遍历目录,找到第一个存在的目录
- foreach ($find_dir as $dir) {
- if (is_dir($dir)) {
- return $dir;
- }
- }
-
- // 如果没有找到目录,则抛出异常
- throw new \RuntimeException('No config directory found');
- }
-
- private function getConfigEnvironment(array $config): string
- {
- return $config['env'] ?? 'development';
- }
-
- private function parseArgvToConfig(array $argv, ZMConfig $config): void
- {
- foreach ($argv as $x => $y) {
- // 当值为 true/false 时,表示该参数为可选参数。当值为 null 时,表示该参数必定会有一个值,如果是 null,说明没指定
- if ($y === false || is_null($y)) {
- continue;
- }
- switch ($x) {
- case 'driver': // 动态设置驱动类型
- $config->set('global.driver', $y);
- break;
- case 'worker-num': // 动态设置 Worker 数量
- $config->set('global.swoole_options.swoole_set.worker_num', (int) $y);
- $config->set('global.workerman_options.workerman_worker_num', (int) $y);
- break;
- case 'daemon': // 启动为守护进程
- $config->set('global.swoole_options.swoole_set.daemonize', 1);
- Worker::$daemonize = true;
- break;
- }
- }
+ new ZMConfig([
+ 'source' => [
+ 'paths' => [$preferences->getConfigDir()],
+ ],
+ ]);
}
private function loadEnvVariables(EnvironmentInterface $env): void
diff --git a/src/ZM/Bootstrap/LoadGlobalDefines.php b/src/ZM/Bootstrap/LoadGlobalDefines.php
index 356a9c24..e9cf67c5 100644
--- a/src/ZM/Bootstrap/LoadGlobalDefines.php
+++ b/src/ZM/Bootstrap/LoadGlobalDefines.php
@@ -4,9 +4,11 @@ declare(strict_types=1);
namespace ZM\Bootstrap;
-class LoadGlobalDefines
+use ZM\Config\RuntimePreferences;
+
+class LoadGlobalDefines implements Bootstrapper
{
- public function bootstrap(array $config): void
+ public function bootstrap(RuntimePreferences $preferences): void
{
require FRAMEWORK_ROOT_DIR . '/src/Globals/global_defines_framework.php';
}
diff --git a/src/ZM/Bootstrap/LoadPlugins.php b/src/ZM/Bootstrap/LoadPlugins.php
index abe2b2be..79501be4 100644
--- a/src/ZM/Bootstrap/LoadPlugins.php
+++ b/src/ZM/Bootstrap/LoadPlugins.php
@@ -4,11 +4,12 @@ declare(strict_types=1);
namespace ZM\Bootstrap;
+use ZM\Config\RuntimePreferences;
use ZM\Plugin\PluginManager;
-class LoadPlugins
+class LoadPlugins implements Bootstrapper
{
- public function bootstrap(array $config): void
+ public function bootstrap(RuntimePreferences $preferences): void
{
// 先遍历下插件目录下是否有这个插件,没有这个插件则不能打包
$plugin_dir = config('global.plugin.load_dir', SOURCE_ROOT_DIR . '/plugins');
diff --git a/src/ZM/Bootstrap/RegisterEventProvider.php b/src/ZM/Bootstrap/RegisterEventProvider.php
index 005006fa..8bf53c71 100644
--- a/src/ZM/Bootstrap/RegisterEventProvider.php
+++ b/src/ZM/Bootstrap/RegisterEventProvider.php
@@ -4,11 +4,12 @@ declare(strict_types=1);
namespace ZM\Bootstrap;
+use ZM\Config\RuntimePreferences;
use ZM\Event\EventProvider;
-class RegisterEventProvider
+class RegisterEventProvider implements Bootstrapper
{
- public function bootstrap(array $config): void
+ public function bootstrap(RuntimePreferences $preferences): void
{
global $ob_event_provider;
$ob_event_provider = EventProvider::getInstance();
diff --git a/src/ZM/Bootstrap/RegisterLogger.php b/src/ZM/Bootstrap/RegisterLogger.php
index 19a79d18..2bbf799a 100644
--- a/src/ZM/Bootstrap/RegisterLogger.php
+++ b/src/ZM/Bootstrap/RegisterLogger.php
@@ -4,18 +4,17 @@ declare(strict_types=1);
namespace ZM\Bootstrap;
+use ZM\Config\RuntimePreferences;
use ZM\Logger\ConsoleLogger;
-class RegisterLogger
+class RegisterLogger implements Bootstrapper
{
- public function bootstrap(array $config): void
+ public function bootstrap(RuntimePreferences $preferences): void
{
// 初始化 Logger
if (!ob_logger_registered()) {
- $debug = $config['verbose'] ?? false;
- $debug = $debug ? 'debug' : null;
// 如果没有注册过 Logger,那么就初始化一个,在启动框架前注册的话,就不会初始化了,可替换为其他 Logger
- $logger = new ConsoleLogger($config['log-level'] ?? $debug ?? 'info');
+ $logger = new ConsoleLogger($preferences->getLogLevel());
ob_logger_register($logger);
}
}
diff --git a/src/ZM/Bootstrap/SetInternalTimezone.php b/src/ZM/Bootstrap/SetInternalTimezone.php
index 5b327ca7..aa3a7f96 100644
--- a/src/ZM/Bootstrap/SetInternalTimezone.php
+++ b/src/ZM/Bootstrap/SetInternalTimezone.php
@@ -4,9 +4,11 @@ declare(strict_types=1);
namespace ZM\Bootstrap;
-class SetInternalTimezone
+use ZM\Config\RuntimePreferences;
+
+class SetInternalTimezone implements Bootstrapper
{
- public function bootstrap(array $config): void
+ public function bootstrap(RuntimePreferences $preferences): void
{
date_default_timezone_set(config('global.runtime.timezone', 'UTC'));
}
diff --git a/src/ZM/Command/Command.php b/src/ZM/Command/Command.php
index 77f77191..066f2333 100644
--- a/src/ZM/Command/Command.php
+++ b/src/ZM/Command/Command.php
@@ -33,11 +33,6 @@ abstract class Command extends \Symfony\Component\Console\Command\Command implem
$this->input = $input;
$this->output = $output;
if ($this->shouldExecute()) {
- if (property_exists($this, 'bootstrappers')) {
- foreach ($this->bootstrappers as $bootstrapper) {
- (new $bootstrapper())->bootstrap($this->input->getOptions());
- }
- }
try {
return $this->handle();
} catch (\Throwable $e) {
diff --git a/src/ZM/Command/Server/ServerStartCommand.php b/src/ZM/Command/Server/ServerStartCommand.php
index 1ff05740..4980678d 100644
--- a/src/ZM/Command/Server/ServerStartCommand.php
+++ b/src/ZM/Command/Server/ServerStartCommand.php
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace ZM\Command\Server;
use OneBot\Driver\Process\ProcessManager;
+use OneBot\Driver\Workerman\Worker;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -26,13 +27,10 @@ class ServerStartCommand extends ServerCommand
protected function configure()
{
$this->setDefinition([
- new InputOption('config-dir', null, InputOption::VALUE_REQUIRED, '指定其他配置文件目录'),
new InputOption('driver', null, InputOption::VALUE_REQUIRED, '指定驱动类型'),
- new InputOption('log-level', null, InputOption::VALUE_REQUIRED, '调整消息等级到debug (log-level=4)'),
new InputOption('daemon', null, null, '以守护进程的方式运行框架'),
new InputOption('worker-num', null, InputOption::VALUE_REQUIRED, '启动框架时运行的 Worker 进程数量'),
new InputOption('watch', null, null, '监听 plugins/ 目录下各个插件的文件变化并热更新(还不能用)'),
- new InputOption('env', null, InputOption::VALUE_REQUIRED, '设置环境类型 (production, development, staging)'),
new InputOption('disable-safe-exit', null, null, '关闭安全退出(关闭后按CtrlC时直接杀死进程)'),
new InputOption('no-state-check', null, null, '关闭启动前框架运行状态检查'),
new InputOption('private-mode', null, null, '启动时隐藏MOTD和敏感信息'),
@@ -60,8 +58,23 @@ class ServerStartCommand extends ServerCommand
}
}
}
+
+ if ($input->getOption('driver')) {
+ config(['global.driver' => $input->getOption('driver')]);
+ }
+
+ if ($input->getOption('worker-num')) {
+ config(['global.swoole_options.swoole_set.worker_num' => (int) $input->getOption('worker-num')]);
+ config(['global.workerman_options.workerman_worker_num' => (int) $input->getOption('worker-num')]);
+ }
+
+ if ($input->getOption('daemon')) {
+ config(['global.swoole_options.swoole_set.daemonize' => 1]);
+ Worker::$daemonize = true;
+ }
+
// 框架启动的入口
- (new Framework($input->getOptions()))->init()->start();
+ Framework::getInstance()->init($input->getOptions())->start();
return 0;
}
}
diff --git a/src/ZM/Config/RuntimePreferences.php b/src/ZM/Config/RuntimePreferences.php
new file mode 100644
index 00000000..458f2377
--- /dev/null
+++ b/src/ZM/Config/RuntimePreferences.php
@@ -0,0 +1,83 @@
+environment;
+ }
+
+ return in_array($this->environment, $environments, true);
+ }
+
+ public function withEnvironment(string $environment): self
+ {
+ $copy = clone $this;
+ $copy->environment = $environment;
+ return $copy;
+ }
+
+ public function isDebugMode(): bool
+ {
+ return $this->debug_mode;
+ }
+
+ public function enableDebugMode(bool $debug_mode): self
+ {
+ $copy = clone $this;
+ $copy->debug_mode = $debug_mode;
+ return $copy;
+ }
+
+ public function getLogLevel(): string
+ {
+ return $this->isDebugMode() ? 'debug' : $this->log_level;
+ }
+
+ public function withLogLevel(string $log_level): self
+ {
+ $copy = clone $this;
+ $copy->log_level = $log_level;
+ return $copy;
+ }
+
+ public function getConfigDir(): string
+ {
+ return $this->config_dir;
+ }
+
+ public function withConfigDir(string $config_dir): self
+ {
+ $copy = clone $this;
+ $copy->config_dir = $config_dir;
+ return $copy;
+ }
+
+ public function runningInInteractiveTerminal(): bool
+ {
+ if (function_exists('posix_isatty')) {
+ return posix_isatty(STDIN) && posix_isatty(STDOUT);
+ }
+
+ // fallback to stream_isatty() if posix_isatty() is not available (e.g. on Windows)
+ return function_exists('stream_isatty') && stream_isatty(STDIN) && stream_isatty(STDOUT);
+ }
+
+ public function runningUnitTests(): bool
+ {
+ return defined('PHPUNIT_RUNNING') && constant('PHPUNIT_RUNNING');
+ }
+}
diff --git a/src/ZM/Config/ZMConfig.php b/src/ZM/Config/ZMConfig.php
index 812ec861..38e44cc5 100644
--- a/src/ZM/Config/ZMConfig.php
+++ b/src/ZM/Config/ZMConfig.php
@@ -8,6 +8,7 @@ use OneBot\Config\Config;
use OneBot\Config\Loader\LoaderInterface;
use OneBot\Util\Singleton;
use ZM\Exception\ConfigException;
+use ZM\Framework;
class ZMConfig
{
@@ -42,11 +43,6 @@ class ZMConfig
*/
private array $config_paths;
- /**
- * @var string 当前环境
- */
- private string $environment;
-
/**
* @var Config 内部配置容器
*/
@@ -66,18 +62,15 @@ class ZMConfig
/**
* 构造配置实例
*
- * @param string $environment 环境
- *
* @throws ConfigException 配置文件加载出错
*/
- public function __construct(string $environment = 'uninitiated', array $init_config = null)
+ public function __construct(array $init_config = null)
{
- $conf = $init_config ?: $this->loadInitConfig();
+ // 合并初始化配置,构造传入优先
+ $conf = array_merge_recursive($this->loadInitConfig(), $init_config ?? []);
$this->file_extensions = $conf['source']['extensions'];
$this->config_paths = $conf['source']['paths'];
- $this->environment = self::$environment_alias[$environment] ?? $environment;
-
// 初始化配置容器
$this->holder = new Config(
new ($conf['repository'][0])(...$conf['repository'][1]),
@@ -93,9 +86,7 @@ class ZMConfig
$this->tracer = null;
}
- if ($environment !== 'uninitiated') {
- $this->loadFiles();
- }
+ $this->loadFiles();
}
/**
@@ -207,32 +198,6 @@ class ZMConfig
}
}
- /**
- * 获取当前环境
- *
- * @return string 当前环境
- */
- public function getEnvironment(): string
- {
- return $this->environment;
- }
-
- /**
- * 设置当前环境
- *
- * 变更环境后,将会自动调用 `reload` 方法重载配置
- *
- * @param string $environment 目标环境
- */
- public function setEnvironment(string $environment): void
- {
- $target = self::$environment_alias[$environment] ?? $environment;
- if ($this->environment !== $target) {
- $this->environment = $target;
- $this->reload();
- }
- }
-
/**
* 重载配置文件
* 运行期间新增的配置文件不会被加载哟~
@@ -337,7 +302,7 @@ class ZMConfig
}
if ($type === 'environment') {
$name_and_env = explode('.', $name);
- if ($name_and_env[1] === $this->environment) {
+ if (Framework::getInstance()->runtime_preferences->environment($name_and_env[1])) {
return true;
}
}
diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php
index 4423bd87..f595b541 100644
--- a/src/ZM/ConsoleApplication.php
+++ b/src/ZM/ConsoleApplication.php
@@ -7,9 +7,11 @@ namespace ZM;
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\Output\OutputInterface;
-use ZM\Command\Server\ServerStartCommand;
+use Symfony\Component\Console\ConsoleEvents;
+use Symfony\Component\Console\Event\ConsoleCommandEvent;
+use Symfony\Component\Console\Event\ConsoleErrorEvent;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\EventDispatcher\EventDispatcher;
use ZM\Exception\SingletonViolationException;
use ZM\Store\FileSystem;
@@ -20,15 +22,6 @@ use ZM\Store\FileSystem;
*/
final class ConsoleApplication extends Application
{
- protected array $bootstrappers = [
- Bootstrap\LoadConfiguration::class, // 加载配置文件
- Bootstrap\LoadGlobalDefines::class, // 加载框架级别的全局常量声明
- Bootstrap\RegisterLogger::class, // 加载 Logger
- Bootstrap\HandleExceptions::class, // 注册异常处理器
- Bootstrap\RegisterEventProvider::class, // 绑定框架的 EventProvider 到 libob 的 Driver 上
- Bootstrap\SetInternalTimezone::class, // 设置时区
- ];
-
private static ?ConsoleApplication $obj = null;
public function __construct(string $name = 'zhamao-framework')
@@ -37,6 +30,68 @@ final class ConsoleApplication extends Application
throw new SingletonViolationException(self::class);
}
+ // 初始化 Composer 变量
+ if (file_exists(WORKING_DIR . '/runtime/composer.phar')) {
+ echo '* Using native composer' . PHP_EOL;
+ putenv('COMPOSER_EXECUTABLE=' . WORKING_DIR . '/runtime/composer.phar');
+ }
+
+ $this->registerCommandLoader();
+
+ // 执行父级初始化
+ parent::__construct($name, ZM_VERSION);
+
+ $this->registerGlobalOptions();
+
+ // 设置命令事件分发器
+ $dispatcher = new EventDispatcher();
+ $this->setDispatcher($dispatcher);
+
+ // 注册命令执行前监听器
+ $dispatcher->addListener(ConsoleEvents::COMMAND, function (ConsoleCommandEvent $event) {
+ $input = $event->getInput();
+
+ // 初始化内核
+ /** @var Framework $kernel */
+ $kernel = Framework::getInstance();
+ $kernel->runtime_preferences = $kernel->runtime_preferences
+ ->withConfigDir($input->getOption('config-dir'))
+ ->withEnvironment($input->getOption('env'))
+ ->enableDebugMode($input->getOption('debug'))
+ ->withLogLevel($input->getOption('log-level'));
+ $kernel->bootstrap();
+ });
+
+ // 注册命令执行错误监听器
+ $dispatcher->addListener(ConsoleEvents::ERROR, function (ConsoleErrorEvent $event) {
+ $e = $event->getError();
+ // 输出错误信息
+ echo zm_internal_errcode('E00005') . "{$e->getMessage()} at {$e->getFile()}({$e->getLine()})" . PHP_EOL;
+ exit(1);
+ });
+
+ // 设置单例,阻止后续实例化
+ self::$obj = $this;
+ }
+
+ /**
+ * 注册全局选项,应用到所有命令
+ */
+ public function registerGlobalOptions(): void
+ {
+ $this->getDefinition()->addOptions([
+ new InputOption('debug', 'd', InputOption::VALUE_NONE, '启用调试模式'),
+ new InputOption('env', 'e', InputOption::VALUE_REQUIRED, '指定运行环境', 'development'),
+ new InputOption('config-dir', 'c', InputOption::VALUE_REQUIRED, '指定配置文件目录', SOURCE_ROOT_DIR . '/config'),
+ new InputOption('log-level', 'l', InputOption::VALUE_REQUIRED, '指定日志等级', 'info'),
+ ]);
+ }
+
+ /**
+ * 注册命令加载器
+ */
+ private function registerCommandLoader(): void
+ {
// 初始化命令
$command_classes = [];
// 先加载框架内置命令
@@ -51,11 +106,7 @@ final class ConsoleApplication extends Application
FileSystem::getClassesPsr4(SOURCE_ROOT_DIR . '/src/Command', 'Command')
);
}
- // 初始化 Composer 变量
- if (file_exists(WORKING_DIR . '/runtime/composer.phar')) {
- echo '* Using native composer' . PHP_EOL;
- putenv('COMPOSER_EXECUTABLE=' . WORKING_DIR . '/runtime/composer.phar');
- }
+ // TODO: 加载插件命令,可以考虑自定义 CommandLoader
$commands = [];
foreach ($command_classes as $command_class) {
try {
@@ -78,27 +129,5 @@ final class ConsoleApplication extends Application
// 命令工厂,用于延迟加载命令
$command_loader = new FactoryCommandLoader($commands);
$this->setCommandLoader($command_loader);
-
- self::$obj = $this; // 用于标记已经初始化完成
- parent::__construct($name, ZM_VERSION);
- }
-
- /**
- * {@inheritdoc}
- */
- public function run(InputInterface $input = null, OutputInterface $output = null): int
- {
- // 注册 bootstrap
- $options = $input?->getOptions() ?? ServerStartCommand::exportOptionArray();
- foreach ($this->bootstrappers as $bootstrapper) {
- resolve($bootstrapper)->bootstrap($options);
- }
-
- try {
- return parent::run($input, $output);
- } catch (\Throwable $e) {
- echo zm_internal_errcode('E00005') . "{$e->getMessage()} at {$e->getFile()}({$e->getLine()})" . PHP_EOL;
- exit(1);
- }
}
}
diff --git a/src/ZM/Container/ContainerHolder.php b/src/ZM/Container/ContainerHolder.php
index 56fe1264..ba4f7527 100644
--- a/src/ZM/Container/ContainerHolder.php
+++ b/src/ZM/Container/ContainerHolder.php
@@ -8,12 +8,15 @@ use DI;
use DI\Container;
use DI\ContainerBuilder;
use OneBot\Driver\Coroutine\Adaptive;
+use ZM\Framework;
class ContainerHolder
{
/** @var Container[] */
private static array $container = [];
+ private static array $config = [];
+
public static function getEventContainer(): Container
{
$cid = Adaptive::getCoroutine()?->getCid() ?? -1;
@@ -31,16 +34,18 @@ class ContainerHolder
private static function buildContainer(): Container
{
+ self::loadConfig();
+
$builder = new ContainerBuilder();
$builder->addDefinitions(
new AliasDefinitionSource(),
- new DI\Definition\Source\DefinitionArray(config('container.definitions', [])),
+ new DI\Definition\Source\DefinitionArray(self::$config['definitions'] ?? []),
);
$builder->useAutowiring(true);
$builder->useAttributes(true);
// 容器缓存
- $enable_cache = config('container.cache.enable', false);
+ $enable_cache = self::$config['cache']['enable'] ?? false;
if (is_callable($enable_cache)) {
$enable_cache = $enable_cache();
}
@@ -49,10 +54,18 @@ class ContainerHolder
if (!extension_loaded('apcu')) {
logger()->warning('APCu 扩展未加载,容器缓存将不可用');
} else {
- $builder->enableDefinitionCache(config('container.cache.namespace', ''));
+ $builder->enableDefinitionCache(self::$config['cache']['namespace'] ?? '');
}
}
return $builder->build();
}
+
+ private static function loadConfig(): void
+ {
+ if (self::$config) {
+ return;
+ }
+ self::$config = require Framework::getInstance()->runtime_preferences->getConfigDir() . '/container.php';
+ }
}
diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php
index 712dc866..8df6bf46 100644
--- a/src/ZM/Framework.php
+++ b/src/ZM/Framework.php
@@ -20,8 +20,9 @@ use OneBot\Driver\Swoole\SwooleDriver;
use OneBot\Driver\Workerman\Worker;
use OneBot\Driver\Workerman\WorkermanDriver;
use OneBot\Util\Singleton;
+use ZM\Bootstrap\Bootstrapper;
use ZM\Command\Server\ServerStartCommand;
-use ZM\Config\ZMConfig;
+use ZM\Config\RuntimePreferences;
use ZM\Container\ContainerBindingListener;
use ZM\Event\Listener\HttpEventListener;
use ZM\Event\Listener\ManagerEventListener;
@@ -38,6 +39,8 @@ use ZM\Utils\EasterEgg;
/**
* 框架入口类
* @since 3.0
+ *
+ * @method static Framework getInstance()
*/
class Framework
{
@@ -49,6 +52,11 @@ class Framework
/** @var string 版本名称 */
public const VERSION = '3.1.1';
+ /**
+ * @var RuntimePreferences 运行时偏好(环境信息&参数)
+ */
+ public RuntimePreferences $runtime_preferences;
+
/** @var array 传入的参数 */
protected array $argv;
@@ -70,11 +78,9 @@ class Framework
/**
* 框架初始化文件
- *
- * @param array $argv 传入的参数(见 ServerStartCommand)
* @throws \Exception
*/
- public function __construct(array $argv = [])
+ public function __construct()
{
// 单例化整个Framework类
if (self::$instance !== null) {
@@ -82,17 +88,22 @@ class Framework
}
self::$instance = $this;
- // 初始化必需的args参数,如果没有传入的话,使用默认值
- $this->argv = empty($argv) ? ServerStartCommand::exportOptionArray() : $argv;
+ $this->runtime_preferences = new RuntimePreferences();
}
/**
* 初始化框架
*
+ * @param array $argv 传入的参数(见 ServerStartCommand)
+ *
* @throws \Exception
*/
- public function init(): Framework
+ public function init(array $argv = []): Framework
{
+ // TODO: discard argv
+ // 初始化必需的args参数,如果没有传入的话,使用默认值
+ $this->argv = empty($argv) ? ServerStartCommand::exportOptionArray() : $argv;
+
// 初始化 @OnSetup 事件
$this->initSetupAnnotations();
@@ -251,6 +262,14 @@ class Framework
}
}
+ public function bootstrap(): void
+ {
+ foreach ($this->bootstrappers as $bootstrapper) {
+ /* @var Bootstrapper $bootstrapper */
+ (new $bootstrapper())->bootstrap($this->runtime_preferences);
+ }
+ }
+
/**
* 打印属性表格
*/
@@ -260,7 +279,7 @@ class Framework
// 打印工作目录
$properties['working_dir'] = WORKING_DIR;
// 打印环境信息
- $properties['environment'] = ZMConfig::getInstance()->getEnvironment();
+ $properties['environment'] = $this->runtime_preferences->environment();
// 打印驱动
$properties['driver'] = config('global.driver');
// 打印logger显示等级
diff --git a/src/ZM/ZMApplication.php b/src/ZM/ZMApplication.php
index a1ed6bba..42e51f52 100644
--- a/src/ZM/ZMApplication.php
+++ b/src/ZM/ZMApplication.php
@@ -53,6 +53,6 @@ class ZMApplication extends ZMPlugin
$meta = new PluginMeta(name: 'native', plugin_type: ZM_PLUGIN_TYPE_NATIVE);
$meta->bindEntity($this);
PluginManager::addPlugin($meta);
- (new Framework($this->args))->init()->start();
+ Framework::getInstance()->init($this->args)->start();
}
}
diff --git a/tests/ZM/Config/ZMConfigTest.php b/tests/ZM/Config/ZMConfigTest.php
index fe32a653..9d46859e 100644
--- a/tests/ZM/Config/ZMConfigTest.php
+++ b/tests/ZM/Config/ZMConfigTest.php
@@ -58,7 +58,7 @@ class ZMConfigTest extends TestCase
try {
$init_conf = require SOURCE_ROOT_DIR . '/config/config.php';
$init_conf['source']['paths'] = [$this->vfs->url()];
- $config = new ZMConfig('development', $init_conf);
+ $config = new ZMConfig($init_conf);
} catch (ConfigException $e) {
$this->fail($e->getMessage());
}