From bc76febadba76d9ec39c4ecbbffee88bf612105a Mon Sep 17 00:00:00 2001 From: sunxyw Date: Fri, 24 Feb 2023 16:49:24 +0800 Subject: [PATCH 1/6] refactor framework kernel --- src/ZM/Bootstrap/Bootstrapper.php | 12 ++ src/ZM/Bootstrap/HandleExceptions.php | 5 +- src/ZM/Bootstrap/LoadConfiguration.php | 67 ++-------- src/ZM/Bootstrap/LoadGlobalDefines.php | 6 +- src/ZM/Bootstrap/LoadPlugins.php | 5 +- src/ZM/Bootstrap/RegisterEventProvider.php | 5 +- src/ZM/Bootstrap/RegisterLogger.php | 9 +- src/ZM/Bootstrap/SetInternalTimezone.php | 6 +- src/ZM/Command/Server/ServerStartCommand.php | 19 ++- src/ZM/Config/ZMConfig.php | 47 +------ src/ZM/ConsoleApplication.php | 106 ++++++++++------ src/ZM/Container/ContainerHolder.php | 14 ++- src/ZM/Framework.php | 3 +- src/ZM/Kernel.php | 124 +++++++++++++++++++ 14 files changed, 266 insertions(+), 162 deletions(-) create mode 100644 src/ZM/Bootstrap/Bootstrapper.php create mode 100644 src/ZM/Kernel.php diff --git a/src/ZM/Bootstrap/Bootstrapper.php b/src/ZM/Bootstrap/Bootstrapper.php new file mode 100644 index 00000000..a258d2dd --- /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' => [$kernel->getConfigDir()], + ], + ]); } private function loadEnvVariables(EnvironmentInterface $env): void diff --git a/src/ZM/Bootstrap/LoadGlobalDefines.php b/src/ZM/Bootstrap/LoadGlobalDefines.php index 356a9c24..e06f671b 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\Kernel; + +class LoadGlobalDefines implements Bootstrapper { - public function bootstrap(array $config): void + public function bootstrap(Kernel $kernel): 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..d531d4eb 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\Kernel; use ZM\Plugin\PluginManager; -class LoadPlugins +class LoadPlugins implements Bootstrapper { - public function bootstrap(array $config): void + public function bootstrap(Kernel $kernel): 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..9976499a 100644 --- a/src/ZM/Bootstrap/RegisterEventProvider.php +++ b/src/ZM/Bootstrap/RegisterEventProvider.php @@ -5,10 +5,11 @@ declare(strict_types=1); namespace ZM\Bootstrap; use ZM\Event\EventProvider; +use ZM\Kernel; -class RegisterEventProvider +class RegisterEventProvider implements Bootstrapper { - public function bootstrap(array $config): void + public function bootstrap(Kernel $kernel): 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..6d57fad2 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\Kernel; use ZM\Logger\ConsoleLogger; -class RegisterLogger +class RegisterLogger implements Bootstrapper { - public function bootstrap(array $config): void + public function bootstrap(Kernel $kernel): 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($kernel->getLogLevel()); ob_logger_register($logger); } } diff --git a/src/ZM/Bootstrap/SetInternalTimezone.php b/src/ZM/Bootstrap/SetInternalTimezone.php index 5b327ca7..f03d1a20 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\Kernel; + +class SetInternalTimezone implements Bootstrapper { - public function bootstrap(array $config): void + public function bootstrap(Kernel $kernel): void { date_default_timezone_set(config('global.runtime.timezone', 'UTC')); } diff --git a/src/ZM/Command/Server/ServerStartCommand.php b/src/ZM/Command/Server/ServerStartCommand.php index 1ff05740..1e830448 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,6 +58,21 @@ 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(); return 0; diff --git a/src/ZM/Config/ZMConfig.php b/src/ZM/Config/ZMConfig.php index 812ec861..1c6d8fdc 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\Kernel; 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 (Kernel::getInstance()->environment($name_and_env[1])) { return true; } } diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index 4423bd87..42839559 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,67 @@ 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 Kernel $kernel */ + $kernel = Kernel::getInstance(); + $kernel->setConfigDir($input->getOption('config-dir')); + $kernel->setEnvironment($input->getOption('env')); + $kernel->setDebugMode($input->getOption('debug')); + $kernel->setLogLevel($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 +105,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 +128,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..5cc2f8d7 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\Kernel; class ContainerHolder { /** @var Container[] */ private static array $container = []; + private static array $config = []; + public static function getEventContainer(): Container { $cid = Adaptive::getCoroutine()?->getCid() ?? -1; @@ -34,13 +37,13 @@ class ContainerHolder $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 +52,15 @@ 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 + { + self::$config = require Kernel::getInstance()->getConfigDir() . '/container.php'; + } } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 712dc866..40fb6381 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -21,7 +21,6 @@ use OneBot\Driver\Workerman\Worker; use OneBot\Driver\Workerman\WorkermanDriver; use OneBot\Util\Singleton; use ZM\Command\Server\ServerStartCommand; -use ZM\Config\ZMConfig; use ZM\Container\ContainerBindingListener; use ZM\Event\Listener\HttpEventListener; use ZM\Event\Listener\ManagerEventListener; @@ -260,7 +259,7 @@ class Framework // 打印工作目录 $properties['working_dir'] = WORKING_DIR; // 打印环境信息 - $properties['environment'] = ZMConfig::getInstance()->getEnvironment(); + $properties['environment'] = Kernel::getInstance()->environment(); // 打印驱动 $properties['driver'] = config('global.driver'); // 打印logger显示等级 diff --git a/src/ZM/Kernel.php b/src/ZM/Kernel.php new file mode 100644 index 00000000..8dfa9a71 --- /dev/null +++ b/src/ZM/Kernel.php @@ -0,0 +1,124 @@ +registerBootstrappers([ + Bootstrap\LoadConfiguration::class, // 加载配置文件 + Bootstrap\LoadGlobalDefines::class, // 加载框架级别的全局常量声明 + Bootstrap\RegisterLogger::class, // 加载 Logger + Bootstrap\HandleExceptions::class, // 注册异常处理器 + Bootstrap\RegisterEventProvider::class, // 绑定框架的 EventProvider 到 libob 的 Driver 上 + Bootstrap\SetInternalTimezone::class, // 设置时区 + ]); + } + + /** + * 获取版本号 + */ + public function version(): string + { + return ZM_VERSION; + } + + /** + * 获取配置文件目录 + */ + public function getConfigDir(): string + { + return $this->config_dir; + } + + /** + * 设置配置文件目录 + */ + public function setConfigDir(string $config_dir): void + { + $this->config_dir = $config_dir; + } + + /** + * 获取或检查运行环境 + * + * @param array|string ...$environments + */ + public function environment(...$environments): string|bool + { + if (empty($environments)) { + return $this->environment; + } + + return in_array($this->environment, $environments, true); + } + + /** + * 设置运行环境 + */ + public function setEnvironment(string $environment): void + { + $this->environment = $environment; + } + + public function isDebugMode(): bool + { + return $this->debug_mode; + } + + public function setDebugMode(bool $debug_mode): void + { + $this->debug_mode = $debug_mode; + } + + public function getLogLevel(): string + { + return $this->isDebugMode() ? 'debug' : $this->log_level; + } + + public function setLogLevel(string $log_level): void + { + $this->log_level = $log_level; + } + + public function runningInConsole(): bool + { + return PHP_SAPI === 'cli'; + } + + public function runningUnitTests(): bool + { + return $this->environment('testing'); + } + + public function registerBootstrappers(array $bootstrappers): void + { + $this->bootstrappers = array_merge($this->bootstrappers, $bootstrappers); + } + + public function bootstrap(): void + { + foreach ($this->bootstrappers as $bootstrapper) { + /* @var Bootstrapper $bootstrapper */ + (new $bootstrapper())->bootstrap($this); + } + } +} From fa5b41c26d9f5dabbe4f29778911680f3b6b0ddd Mon Sep 17 00:00:00 2001 From: sunxyw Date: Fri, 24 Feb 2023 17:00:40 +0800 Subject: [PATCH 2/6] fix tests --- bin/phpunit-zm | 10 +++++----- src/ZM/Kernel.php | 11 +++++++++-- tests/ZM/Config/ZMConfigTest.php | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/bin/phpunit-zm b/bin/phpunit-zm index ae1717a2..64b01667 100755 --- a/bin/phpunit-zm +++ b/bin/phpunit-zm @@ -57,11 +57,11 @@ $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); -} +$kernel = \ZM\Kernel::getInstance(); +$kernel->setEnvironment('development'); +$kernel->setTestMode(true); +$kernel->setConfigDir(dirname(__DIR__) . '/config'); +$kernel->bootstrap(); try { (new Framework($options))->init()->start(); diff --git a/src/ZM/Kernel.php b/src/ZM/Kernel.php index 8dfa9a71..3a6445cd 100644 --- a/src/ZM/Kernel.php +++ b/src/ZM/Kernel.php @@ -17,10 +17,12 @@ class Kernel private string $environment; - private bool $debug_mode; + private bool $debug_mode = false; private string $log_level; + private bool $test_mode = false; + private function __construct() { $this->registerBootstrappers([ @@ -106,7 +108,12 @@ class Kernel public function runningUnitTests(): bool { - return $this->environment('testing'); + return $this->test_mode; + } + + public function setTestMode(bool $test_mode): void + { + $this->test_mode = $test_mode; } public function registerBootstrappers(array $bootstrappers): void 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()); } From d513495b3b0eb3dd48667d97e79d892a4090db87 Mon Sep 17 00:00:00 2001 From: sunxyw Date: Fri, 24 Feb 2023 17:02:27 +0800 Subject: [PATCH 3/6] fix container not loading config --- src/ZM/Container/ContainerHolder.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ZM/Container/ContainerHolder.php b/src/ZM/Container/ContainerHolder.php index 5cc2f8d7..a99abb83 100644 --- a/src/ZM/Container/ContainerHolder.php +++ b/src/ZM/Container/ContainerHolder.php @@ -34,6 +34,8 @@ class ContainerHolder private static function buildContainer(): Container { + self::loadConfig(); + $builder = new ContainerBuilder(); $builder->addDefinitions( new AliasDefinitionSource(), @@ -61,6 +63,9 @@ class ContainerHolder private static function loadConfig(): void { + if (self::$config) { + return; + } self::$config = require Kernel::getInstance()->getConfigDir() . '/container.php'; } } From 8f43012a5c57790b4830ffd0ff36252609ff09f5 Mon Sep 17 00:00:00 2001 From: sunxyw Date: Fri, 24 Feb 2023 23:09:37 +0800 Subject: [PATCH 4/6] fix properties not initialized --- config/container.php | 4 ++-- src/Module/Example/Hello123.php | 39 ++++++--------------------------- src/ZM/Kernel.php | 6 ++--- 3 files changed, 12 insertions(+), 37 deletions(-) diff --git a/config/container.php b/config/container.php index 9e7cc42c..099d2337 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\Kernel; /* * 这里是容器的配置文件,你可以在这里配置容器的绑定和其他一些参数。 @@ -37,7 +37,7 @@ return [ // 详细介绍请参阅:https://php-di.org/doc/performances.html#caching 'cache' => [ // 是否启用缓存,支持 bool、callable - 'enable' => fn () => ZMConfig::getInstance()->getEnvironment() === 'production', + 'enable' => fn () => Kernel::getInstance()->environment('production'), 'namespace' => 'zm', ], ]; 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/Kernel.php b/src/ZM/Kernel.php index 3a6445cd..352ee43b 100644 --- a/src/ZM/Kernel.php +++ b/src/ZM/Kernel.php @@ -13,13 +13,13 @@ class Kernel private array $bootstrappers = []; - private string $config_dir; + private string $config_dir = SOURCE_ROOT_DIR . '/config'; - private string $environment; + private string $environment = 'development'; private bool $debug_mode = false; - private string $log_level; + private string $log_level = 'info'; private bool $test_mode = false; From 772288b5172612e36698f7c7907fe3ce1081fddf Mon Sep 17 00:00:00 2001 From: sunxyw Date: Tue, 7 Mar 2023 23:07:52 +0800 Subject: [PATCH 5/6] merge framework and kernel --- bin/phpunit-zm | 12 +- config/container.php | 6 +- phpunit.xml.dist | 2 +- src/ZM/Bootstrap/Bootstrapper.php | 4 +- src/ZM/Bootstrap/HandleExceptions.php | 4 +- src/ZM/Bootstrap/LoadConfiguration.php | 6 +- src/ZM/Bootstrap/LoadGlobalDefines.php | 4 +- src/ZM/Bootstrap/LoadPlugins.php | 4 +- src/ZM/Bootstrap/RegisterEventProvider.php | 4 +- src/ZM/Bootstrap/RegisterLogger.php | 6 +- src/ZM/Bootstrap/SetInternalTimezone.php | 4 +- src/ZM/Command/Server/ServerStartCommand.php | 2 +- src/ZM/Config/ZMConfig.php | 4 +- src/ZM/ConsoleApplication.php | 4 +- src/ZM/Container/ContainerHolder.php | 4 +- src/ZM/Framework.php | 95 ++++++++++++-- src/ZM/HasRuntimeInfo.php | 31 +++++ src/ZM/Kernel.php | 131 ------------------- src/ZM/ZMApplication.php | 2 +- 19 files changed, 153 insertions(+), 176 deletions(-) create mode 100644 src/ZM/HasRuntimeInfo.php delete mode 100644 src/ZM/Kernel.php diff --git a/bin/phpunit-zm b/bin/phpunit-zm index 64b01667..6346a1b4 100755 --- a/bin/phpunit-zm +++ b/bin/phpunit-zm @@ -57,14 +57,12 @@ $options['driver'] = 'workerman'; $options['worker-num'] = 1; $options['private-mode'] = true; -$kernel = \ZM\Kernel::getInstance(); -$kernel->setEnvironment('development'); -$kernel->setTestMode(true); -$kernel->setConfigDir(dirname(__DIR__) . '/config'); -$kernel->bootstrap(); - try { - (new Framework($options))->init()->start(); + $framework = (new Framework($options)); + $framework->setEnvironment('development'); + $framework->setConfigDir(dirname(__DIR__) . '/config'); + $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 099d2337..c858cb47 100644 --- a/config/container.php +++ b/config/container.php @@ -8,7 +8,7 @@ use Psr\Log\LoggerInterface; use ZM\Config\Environment; use ZM\Config\EnvironmentInterface; use ZM\Framework; -use ZM\Kernel; +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 () => Kernel::getInstance()->environment('production'), + 'enable' => fn () => Framework::getInstance()->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/ZM/Bootstrap/Bootstrapper.php b/src/ZM/Bootstrap/Bootstrapper.php index a258d2dd..e5c32876 100644 --- a/src/ZM/Bootstrap/Bootstrapper.php +++ b/src/ZM/Bootstrap/Bootstrapper.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\Kernel; +use ZM\HasRuntimeInfo; interface Bootstrapper { - public function bootstrap(Kernel $kernel): void; + public function bootstrap(HasRuntimeInfo $runtime_info): void; } diff --git a/src/ZM/Bootstrap/HandleExceptions.php b/src/ZM/Bootstrap/HandleExceptions.php index 2f6734e0..f873a7ca 100644 --- a/src/ZM/Bootstrap/HandleExceptions.php +++ b/src/ZM/Bootstrap/HandleExceptions.php @@ -6,11 +6,11 @@ namespace ZM\Bootstrap; use OneBot\Exception\ExceptionHandler; use ZM\Exception\Handler; -use ZM\Kernel; +use ZM\HasRuntimeInfo; class HandleExceptions implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): void { // 注册全局错误处理器 set_error_handler(function ($error_no, $error_msg, $error_file, $error_line) { diff --git a/src/ZM/Bootstrap/LoadConfiguration.php b/src/ZM/Bootstrap/LoadConfiguration.php index bf1db263..98983505 100644 --- a/src/ZM/Bootstrap/LoadConfiguration.php +++ b/src/ZM/Bootstrap/LoadConfiguration.php @@ -8,11 +8,11 @@ use Dotenv\Dotenv; use ZM\Config\Environment; use ZM\Config\EnvironmentInterface; use ZM\Config\ZMConfig; -use ZM\Kernel; +use ZM\HasRuntimeInfo; class LoadConfiguration implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): void { // TODO: 重新思考容器绑定的加载方式,从而在此处使用 interface $env = resolve(Environment::class); @@ -20,7 +20,7 @@ class LoadConfiguration implements Bootstrapper new ZMConfig([ 'source' => [ - 'paths' => [$kernel->getConfigDir()], + 'paths' => [$runtime_info->getConfigDir()], ], ]); } diff --git a/src/ZM/Bootstrap/LoadGlobalDefines.php b/src/ZM/Bootstrap/LoadGlobalDefines.php index e06f671b..4152f43b 100644 --- a/src/ZM/Bootstrap/LoadGlobalDefines.php +++ b/src/ZM/Bootstrap/LoadGlobalDefines.php @@ -4,11 +4,11 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\Kernel; +use ZM\HasRuntimeInfo; class LoadGlobalDefines implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): 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 d531d4eb..d469e867 100644 --- a/src/ZM/Bootstrap/LoadPlugins.php +++ b/src/ZM/Bootstrap/LoadPlugins.php @@ -4,12 +4,12 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\Kernel; +use ZM\HasRuntimeInfo; use ZM\Plugin\PluginManager; class LoadPlugins implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): 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 9976499a..1b50bd7c 100644 --- a/src/ZM/Bootstrap/RegisterEventProvider.php +++ b/src/ZM/Bootstrap/RegisterEventProvider.php @@ -5,11 +5,11 @@ declare(strict_types=1); namespace ZM\Bootstrap; use ZM\Event\EventProvider; -use ZM\Kernel; +use ZM\HasRuntimeInfo; class RegisterEventProvider implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): 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 6d57fad2..3451e31c 100644 --- a/src/ZM/Bootstrap/RegisterLogger.php +++ b/src/ZM/Bootstrap/RegisterLogger.php @@ -4,17 +4,17 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\Kernel; +use ZM\HasRuntimeInfo; use ZM\Logger\ConsoleLogger; class RegisterLogger implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): void { // 初始化 Logger if (!ob_logger_registered()) { // 如果没有注册过 Logger,那么就初始化一个,在启动框架前注册的话,就不会初始化了,可替换为其他 Logger - $logger = new ConsoleLogger($kernel->getLogLevel()); + $logger = new ConsoleLogger($runtime_info->getLogLevel()); ob_logger_register($logger); } } diff --git a/src/ZM/Bootstrap/SetInternalTimezone.php b/src/ZM/Bootstrap/SetInternalTimezone.php index f03d1a20..f9bea253 100644 --- a/src/ZM/Bootstrap/SetInternalTimezone.php +++ b/src/ZM/Bootstrap/SetInternalTimezone.php @@ -4,11 +4,11 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\Kernel; +use ZM\HasRuntimeInfo; class SetInternalTimezone implements Bootstrapper { - public function bootstrap(Kernel $kernel): void + public function bootstrap(HasRuntimeInfo $runtime_info): void { date_default_timezone_set(config('global.runtime.timezone', 'UTC')); } diff --git a/src/ZM/Command/Server/ServerStartCommand.php b/src/ZM/Command/Server/ServerStartCommand.php index 1e830448..4980678d 100644 --- a/src/ZM/Command/Server/ServerStartCommand.php +++ b/src/ZM/Command/Server/ServerStartCommand.php @@ -74,7 +74,7 @@ class ServerStartCommand extends ServerCommand } // 框架启动的入口 - (new Framework($input->getOptions()))->init()->start(); + Framework::getInstance()->init($input->getOptions())->start(); return 0; } } diff --git a/src/ZM/Config/ZMConfig.php b/src/ZM/Config/ZMConfig.php index 1c6d8fdc..319e389b 100644 --- a/src/ZM/Config/ZMConfig.php +++ b/src/ZM/Config/ZMConfig.php @@ -8,7 +8,7 @@ use OneBot\Config\Config; use OneBot\Config\Loader\LoaderInterface; use OneBot\Util\Singleton; use ZM\Exception\ConfigException; -use ZM\Kernel; +use ZM\Framework; class ZMConfig { @@ -302,7 +302,7 @@ class ZMConfig } if ($type === 'environment') { $name_and_env = explode('.', $name); - if (Kernel::getInstance()->environment($name_and_env[1])) { + if (Framework::getInstance()->environment($name_and_env[1])) { return true; } } diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index 42839559..1d1d804e 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -52,8 +52,8 @@ final class ConsoleApplication extends Application $input = $event->getInput(); // 初始化内核 - /** @var Kernel $kernel */ - $kernel = Kernel::getInstance(); + /** @var Framework $kernel */ + $kernel = Framework::getInstance(); $kernel->setConfigDir($input->getOption('config-dir')); $kernel->setEnvironment($input->getOption('env')); $kernel->setDebugMode($input->getOption('debug')); diff --git a/src/ZM/Container/ContainerHolder.php b/src/ZM/Container/ContainerHolder.php index a99abb83..41022b6a 100644 --- a/src/ZM/Container/ContainerHolder.php +++ b/src/ZM/Container/ContainerHolder.php @@ -8,7 +8,7 @@ use DI; use DI\Container; use DI\ContainerBuilder; use OneBot\Driver\Coroutine\Adaptive; -use ZM\Kernel; +use ZM\Framework; class ContainerHolder { @@ -66,6 +66,6 @@ class ContainerHolder if (self::$config) { return; } - self::$config = require Kernel::getInstance()->getConfigDir() . '/container.php'; + self::$config = require Framework::getInstance()->getConfigDir() . '/container.php'; } } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 40fb6381..7758c31c 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -20,6 +20,7 @@ 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\Container\ContainerBindingListener; use ZM\Event\Listener\HttpEventListener; @@ -38,7 +39,7 @@ use ZM\Utils\EasterEgg; * 框架入口类 * @since 3.0 */ -class Framework +class Framework implements HasRuntimeInfo { use Singleton; @@ -67,31 +68,40 @@ class Framework Bootstrap\SetInternalTimezone::class, // 设置时区 ]; + protected string $environment = 'development'; + + protected bool $debug_mode = false; + + protected string $log_level = 'info'; + + protected string $config_dir = SOURCE_ROOT_DIR . '/config'; + /** * 框架初始化文件 - * - * @param array $argv 传入的参数(见 ServerStartCommand) * @throws \Exception */ - public function __construct(array $argv = []) + public function __construct() { // 单例化整个Framework类 if (self::$instance !== null) { throw new SingletonViolationException(self::class); } self::$instance = $this; - - // 初始化必需的args参数,如果没有传入的话,使用默认值 - $this->argv = empty($argv) ? ServerStartCommand::exportOptionArray() : $argv; } /** * 初始化框架 * + * @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(); @@ -250,6 +260,73 @@ class Framework } } + public function bootstrap(): void + { + foreach ($this->bootstrappers as $bootstrapper) { + /* @var Bootstrapper $bootstrapper */ + (new $bootstrapper())->bootstrap($this); + } + } + + public function environment(...$environments): string|bool + { + if (empty($environments)) { + return $this->environment; + } + + return in_array($this->environment, $environments, true); + } + + public function setEnvironment(string $environment): void + { + $this->environment = $environment; + } + + public function isDebugMode(): bool + { + return $this->debug_mode; + } + + public function setDebugMode(bool $debug_mode): void + { + $this->debug_mode = $debug_mode; + } + + public function getLogLevel(): string + { + return $this->isDebugMode() ? 'debug' : $this->log_level; + } + + public function setLogLevel(string $log_level): void + { + $this->log_level = $log_level; + } + + public function getConfigDir(): string + { + return $this->config_dir; + } + + public function setConfigDir(string $config_dir): void + { + $this->config_dir = $config_dir; + } + + 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'); + } + /** * 打印属性表格 */ @@ -259,7 +336,7 @@ class Framework // 打印工作目录 $properties['working_dir'] = WORKING_DIR; // 打印环境信息 - $properties['environment'] = Kernel::getInstance()->environment(); + $properties['environment'] = $this->environment(); // 打印驱动 $properties['driver'] = config('global.driver'); // 打印logger显示等级 diff --git a/src/ZM/HasRuntimeInfo.php b/src/ZM/HasRuntimeInfo.php new file mode 100644 index 00000000..9d415ba0 --- /dev/null +++ b/src/ZM/HasRuntimeInfo.php @@ -0,0 +1,31 @@ +registerBootstrappers([ - Bootstrap\LoadConfiguration::class, // 加载配置文件 - Bootstrap\LoadGlobalDefines::class, // 加载框架级别的全局常量声明 - Bootstrap\RegisterLogger::class, // 加载 Logger - Bootstrap\HandleExceptions::class, // 注册异常处理器 - Bootstrap\RegisterEventProvider::class, // 绑定框架的 EventProvider 到 libob 的 Driver 上 - Bootstrap\SetInternalTimezone::class, // 设置时区 - ]); - } - - /** - * 获取版本号 - */ - public function version(): string - { - return ZM_VERSION; - } - - /** - * 获取配置文件目录 - */ - public function getConfigDir(): string - { - return $this->config_dir; - } - - /** - * 设置配置文件目录 - */ - public function setConfigDir(string $config_dir): void - { - $this->config_dir = $config_dir; - } - - /** - * 获取或检查运行环境 - * - * @param array|string ...$environments - */ - public function environment(...$environments): string|bool - { - if (empty($environments)) { - return $this->environment; - } - - return in_array($this->environment, $environments, true); - } - - /** - * 设置运行环境 - */ - public function setEnvironment(string $environment): void - { - $this->environment = $environment; - } - - public function isDebugMode(): bool - { - return $this->debug_mode; - } - - public function setDebugMode(bool $debug_mode): void - { - $this->debug_mode = $debug_mode; - } - - public function getLogLevel(): string - { - return $this->isDebugMode() ? 'debug' : $this->log_level; - } - - public function setLogLevel(string $log_level): void - { - $this->log_level = $log_level; - } - - public function runningInConsole(): bool - { - return PHP_SAPI === 'cli'; - } - - public function runningUnitTests(): bool - { - return $this->test_mode; - } - - public function setTestMode(bool $test_mode): void - { - $this->test_mode = $test_mode; - } - - public function registerBootstrappers(array $bootstrappers): void - { - $this->bootstrappers = array_merge($this->bootstrappers, $bootstrappers); - } - - public function bootstrap(): void - { - foreach ($this->bootstrappers as $bootstrapper) { - /* @var Bootstrapper $bootstrapper */ - (new $bootstrapper())->bootstrap($this); - } - } -} 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(); } } From 510bb8dc30acbe6a0f4f57de7cc0532715000b13 Mon Sep 17 00:00:00 2001 From: sunxyw Date: Thu, 9 Mar 2023 22:36:20 +0800 Subject: [PATCH 6/6] split runtime info into runtime preferences --- bin/phpunit-zm | 7 +- config/container.php | 2 +- src/ZM/Bootstrap/Bootstrapper.php | 4 +- src/ZM/Bootstrap/HandleExceptions.php | 4 +- src/ZM/Bootstrap/LoadConfiguration.php | 6 +- src/ZM/Bootstrap/LoadGlobalDefines.php | 4 +- src/ZM/Bootstrap/LoadPlugins.php | 4 +- src/ZM/Bootstrap/RegisterEventProvider.php | 4 +- src/ZM/Bootstrap/RegisterLogger.php | 6 +- src/ZM/Bootstrap/SetInternalTimezone.php | 4 +- src/ZM/Command/Command.php | 5 -- src/ZM/Config/RuntimePreferences.php | 83 ++++++++++++++++++++++ src/ZM/Config/ZMConfig.php | 2 +- src/ZM/ConsoleApplication.php | 9 +-- src/ZM/Container/ContainerHolder.php | 2 +- src/ZM/Framework.php | 83 ++++------------------ src/ZM/HasRuntimeInfo.php | 31 -------- 17 files changed, 126 insertions(+), 134 deletions(-) create mode 100644 src/ZM/Config/RuntimePreferences.php delete mode 100644 src/ZM/HasRuntimeInfo.php diff --git a/bin/phpunit-zm b/bin/phpunit-zm index 6346a1b4..0b07a84a 100755 --- a/bin/phpunit-zm +++ b/bin/phpunit-zm @@ -58,9 +58,10 @@ $options['worker-num'] = 1; $options['private-mode'] = true; try { - $framework = (new Framework($options)); - $framework->setEnvironment('development'); - $framework->setConfigDir(dirname(__DIR__) . '/config'); + $framework = new Framework(); + $framework->runtime_preferences = $framework->runtime_preferences + ->withConfigDir(dirname(__DIR__) . '/config') + ->withEnvironment('development'); $framework->bootstrap(); $framework->init()->start(); exit($_swoole_atomic->get()); diff --git a/config/container.php b/config/container.php index c858cb47..684f31b0 100644 --- a/config/container.php +++ b/config/container.php @@ -39,7 +39,7 @@ return [ // 详细介绍请参阅:https://php-di.org/doc/performances.html#caching 'cache' => [ // 是否启用缓存,支持 bool、callable - 'enable' => fn () => Framework::getInstance()->environment('production'), + 'enable' => fn () => Framework::getInstance()->runtime_preferences->environment('production'), 'namespace' => 'zm', ], ]; diff --git a/src/ZM/Bootstrap/Bootstrapper.php b/src/ZM/Bootstrap/Bootstrapper.php index e5c32876..05f2290d 100644 --- a/src/ZM/Bootstrap/Bootstrapper.php +++ b/src/ZM/Bootstrap/Bootstrapper.php @@ -4,9 +4,9 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\HasRuntimeInfo; +use ZM\Config\RuntimePreferences; interface Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): void; + public function bootstrap(RuntimePreferences $preferences): void; } diff --git a/src/ZM/Bootstrap/HandleExceptions.php b/src/ZM/Bootstrap/HandleExceptions.php index f873a7ca..2e4fcec1 100644 --- a/src/ZM/Bootstrap/HandleExceptions.php +++ b/src/ZM/Bootstrap/HandleExceptions.php @@ -5,12 +5,12 @@ declare(strict_types=1); namespace ZM\Bootstrap; use OneBot\Exception\ExceptionHandler; +use ZM\Config\RuntimePreferences; use ZM\Exception\Handler; -use ZM\HasRuntimeInfo; class HandleExceptions implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): void + public function bootstrap(RuntimePreferences $preferences): void { // 注册全局错误处理器 set_error_handler(function ($error_no, $error_msg, $error_file, $error_line) { diff --git a/src/ZM/Bootstrap/LoadConfiguration.php b/src/ZM/Bootstrap/LoadConfiguration.php index 98983505..c5c3346e 100644 --- a/src/ZM/Bootstrap/LoadConfiguration.php +++ b/src/ZM/Bootstrap/LoadConfiguration.php @@ -7,12 +7,12 @@ namespace ZM\Bootstrap; use Dotenv\Dotenv; use ZM\Config\Environment; use ZM\Config\EnvironmentInterface; +use ZM\Config\RuntimePreferences; use ZM\Config\ZMConfig; -use ZM\HasRuntimeInfo; class LoadConfiguration implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): void + public function bootstrap(RuntimePreferences $preferences): void { // TODO: 重新思考容器绑定的加载方式,从而在此处使用 interface $env = resolve(Environment::class); @@ -20,7 +20,7 @@ class LoadConfiguration implements Bootstrapper new ZMConfig([ 'source' => [ - 'paths' => [$runtime_info->getConfigDir()], + 'paths' => [$preferences->getConfigDir()], ], ]); } diff --git a/src/ZM/Bootstrap/LoadGlobalDefines.php b/src/ZM/Bootstrap/LoadGlobalDefines.php index 4152f43b..e9cf67c5 100644 --- a/src/ZM/Bootstrap/LoadGlobalDefines.php +++ b/src/ZM/Bootstrap/LoadGlobalDefines.php @@ -4,11 +4,11 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\HasRuntimeInfo; +use ZM\Config\RuntimePreferences; class LoadGlobalDefines implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): 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 d469e867..79501be4 100644 --- a/src/ZM/Bootstrap/LoadPlugins.php +++ b/src/ZM/Bootstrap/LoadPlugins.php @@ -4,12 +4,12 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\HasRuntimeInfo; +use ZM\Config\RuntimePreferences; use ZM\Plugin\PluginManager; class LoadPlugins implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): 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 1b50bd7c..8bf53c71 100644 --- a/src/ZM/Bootstrap/RegisterEventProvider.php +++ b/src/ZM/Bootstrap/RegisterEventProvider.php @@ -4,12 +4,12 @@ declare(strict_types=1); namespace ZM\Bootstrap; +use ZM\Config\RuntimePreferences; use ZM\Event\EventProvider; -use ZM\HasRuntimeInfo; class RegisterEventProvider implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): 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 3451e31c..2bbf799a 100644 --- a/src/ZM/Bootstrap/RegisterLogger.php +++ b/src/ZM/Bootstrap/RegisterLogger.php @@ -4,17 +4,17 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\HasRuntimeInfo; +use ZM\Config\RuntimePreferences; use ZM\Logger\ConsoleLogger; class RegisterLogger implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): void + public function bootstrap(RuntimePreferences $preferences): void { // 初始化 Logger if (!ob_logger_registered()) { // 如果没有注册过 Logger,那么就初始化一个,在启动框架前注册的话,就不会初始化了,可替换为其他 Logger - $logger = new ConsoleLogger($runtime_info->getLogLevel()); + $logger = new ConsoleLogger($preferences->getLogLevel()); ob_logger_register($logger); } } diff --git a/src/ZM/Bootstrap/SetInternalTimezone.php b/src/ZM/Bootstrap/SetInternalTimezone.php index f9bea253..aa3a7f96 100644 --- a/src/ZM/Bootstrap/SetInternalTimezone.php +++ b/src/ZM/Bootstrap/SetInternalTimezone.php @@ -4,11 +4,11 @@ declare(strict_types=1); namespace ZM\Bootstrap; -use ZM\HasRuntimeInfo; +use ZM\Config\RuntimePreferences; class SetInternalTimezone implements Bootstrapper { - public function bootstrap(HasRuntimeInfo $runtime_info): 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/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 319e389b..38e44cc5 100644 --- a/src/ZM/Config/ZMConfig.php +++ b/src/ZM/Config/ZMConfig.php @@ -302,7 +302,7 @@ class ZMConfig } if ($type === 'environment') { $name_and_env = explode('.', $name); - if (Framework::getInstance()->environment($name_and_env[1])) { + 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 1d1d804e..f595b541 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -54,10 +54,11 @@ final class ConsoleApplication extends Application // 初始化内核 /** @var Framework $kernel */ $kernel = Framework::getInstance(); - $kernel->setConfigDir($input->getOption('config-dir')); - $kernel->setEnvironment($input->getOption('env')); - $kernel->setDebugMode($input->getOption('debug')); - $kernel->setLogLevel($input->getOption('log-level')); + $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(); }); diff --git a/src/ZM/Container/ContainerHolder.php b/src/ZM/Container/ContainerHolder.php index 41022b6a..ba4f7527 100644 --- a/src/ZM/Container/ContainerHolder.php +++ b/src/ZM/Container/ContainerHolder.php @@ -66,6 +66,6 @@ class ContainerHolder if (self::$config) { return; } - self::$config = require Framework::getInstance()->getConfigDir() . '/container.php'; + self::$config = require Framework::getInstance()->runtime_preferences->getConfigDir() . '/container.php'; } } diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 7758c31c..8df6bf46 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -22,6 +22,7 @@ use OneBot\Driver\Workerman\WorkermanDriver; use OneBot\Util\Singleton; use ZM\Bootstrap\Bootstrapper; use ZM\Command\Server\ServerStartCommand; +use ZM\Config\RuntimePreferences; use ZM\Container\ContainerBindingListener; use ZM\Event\Listener\HttpEventListener; use ZM\Event\Listener\ManagerEventListener; @@ -38,8 +39,10 @@ use ZM\Utils\EasterEgg; /** * 框架入口类 * @since 3.0 + * + * @method static Framework getInstance() */ -class Framework implements HasRuntimeInfo +class Framework { use Singleton; @@ -49,6 +52,11 @@ class Framework implements HasRuntimeInfo /** @var string 版本名称 */ public const VERSION = '3.1.1'; + /** + * @var RuntimePreferences 运行时偏好(环境信息&参数) + */ + public RuntimePreferences $runtime_preferences; + /** @var array 传入的参数 */ protected array $argv; @@ -68,14 +76,6 @@ class Framework implements HasRuntimeInfo Bootstrap\SetInternalTimezone::class, // 设置时区 ]; - protected string $environment = 'development'; - - protected bool $debug_mode = false; - - protected string $log_level = 'info'; - - protected string $config_dir = SOURCE_ROOT_DIR . '/config'; - /** * 框架初始化文件 * @throws \Exception @@ -87,6 +87,8 @@ class Framework implements HasRuntimeInfo throw new SingletonViolationException(self::class); } self::$instance = $this; + + $this->runtime_preferences = new RuntimePreferences(); } /** @@ -264,69 +266,10 @@ class Framework implements HasRuntimeInfo { foreach ($this->bootstrappers as $bootstrapper) { /* @var Bootstrapper $bootstrapper */ - (new $bootstrapper())->bootstrap($this); + (new $bootstrapper())->bootstrap($this->runtime_preferences); } } - public function environment(...$environments): string|bool - { - if (empty($environments)) { - return $this->environment; - } - - return in_array($this->environment, $environments, true); - } - - public function setEnvironment(string $environment): void - { - $this->environment = $environment; - } - - public function isDebugMode(): bool - { - return $this->debug_mode; - } - - public function setDebugMode(bool $debug_mode): void - { - $this->debug_mode = $debug_mode; - } - - public function getLogLevel(): string - { - return $this->isDebugMode() ? 'debug' : $this->log_level; - } - - public function setLogLevel(string $log_level): void - { - $this->log_level = $log_level; - } - - public function getConfigDir(): string - { - return $this->config_dir; - } - - public function setConfigDir(string $config_dir): void - { - $this->config_dir = $config_dir; - } - - 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'); - } - /** * 打印属性表格 */ @@ -336,7 +279,7 @@ class Framework implements HasRuntimeInfo // 打印工作目录 $properties['working_dir'] = WORKING_DIR; // 打印环境信息 - $properties['environment'] = $this->environment(); + $properties['environment'] = $this->runtime_preferences->environment(); // 打印驱动 $properties['driver'] = config('global.driver'); // 打印logger显示等级 diff --git a/src/ZM/HasRuntimeInfo.php b/src/ZM/HasRuntimeInfo.php deleted file mode 100644 index 9d415ba0..00000000 --- a/src/ZM/HasRuntimeInfo.php +++ /dev/null @@ -1,31 +0,0 @@ -