diff --git a/config/global.php b/config/global.php index 355fd0c3..0ff752df 100644 --- a/config/global.php +++ b/config/global.php @@ -39,7 +39,10 @@ $config['swoole'] = [ $config['runtime'] = [ 'swoole_coroutine_hook_flags' => SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL), 'swoole_server_mode' => SWOOLE_PROCESS, - 'middleware_error_policy' => 1 + 'middleware_error_policy' => 1, + 'reload_kill_connect' => false, + 'global_middleware_binding' => [ + ] ]; /** 轻量字符串缓存,默认开启 */ @@ -120,7 +123,8 @@ $config['static_file_server'] = [ $config['onebot'] = [ 'status' => true, 'single_bot_mode' => false, - 'message_level' => 99999 + 'message_level' => 99, + 'message_format' => 'string' ]; /** 一个远程简易终端,使用nc直接连接即可,但是不建议开放host为0.0.0.0(远程连接) */ diff --git a/instant-test.php b/instant-test.php new file mode 100644 index 00000000..fa4d7abc --- /dev/null +++ b/instant-test.php @@ -0,0 +1,25 @@ +onEvent(OnOpenEvent::class, ['connect_type' => 'qq'], function(ConnectionObject $conn) { + Console::info("机器人 " . $conn->getOption("connect_id") . " 已连接!"); +}); + +$weather->onEvent(CQCommand::class, ['match' => '你好'], function() { + ctx()->reply("hello呀!"); +}); + +$app = new ZMServer("app-name"); +$app->addModule($weather); +$app->run(); \ No newline at end of file diff --git a/src/ZM/Command/RunServerCommand.php b/src/ZM/Command/RunServerCommand.php index fd32afcf..b62361e6 100644 --- a/src/ZM/Command/RunServerCommand.php +++ b/src/ZM/Command/RunServerCommand.php @@ -50,4 +50,10 @@ class RunServerCommand extends Command (new Framework($input->getOptions()))->start(); return 0; } + + public static function exportDefinition() { + $cmd = new self(); + $cmd->configure(); + return $cmd->getDefinition(); + } } diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index 9dc36130..7d59ddd6 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -21,15 +21,14 @@ use ZM\Command\RunServerCommand; use Symfony\Component\Console\Application; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use ZM\Console\Console; use ZM\Exception\InitException; class ConsoleApplication extends Application { private static $obj = null; - const VERSION_ID = 423; - const VERSION = "2.5.6"; + const VERSION_ID = 424; + const VERSION = "2.6.0"; /** * @throws InitException @@ -49,7 +48,8 @@ class ConsoleApplication extends Application */ public function initEnv($with_default_cmd = ""): ConsoleApplication { if (defined("WORKDING_DIR")) throw new InitException(); - $this->selfCheck(); + + _zm_env_check(); define("WORKING_DIR", getcwd()); if (Phar::running() !== "") { @@ -121,17 +121,4 @@ class ConsoleApplication extends Application die(zm_internal_errcode("E00005") . "{$e->getMessage()} at {$e->getFile()}({$e->getLine()})"); } } - - /** - * 启动炸毛前要做的环境检查项目 - */ - private function selfCheck(): void { - if (!extension_loaded("swoole")) die(zm_internal_errcode("E00001") . "Can not find swoole extension.\n"); - if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die(zm_internal_errcode("E00002") . "You must install swoole version >= 4.5.0 !"); - if (version_compare(PHP_VERSION, "7.2") == -1) die(zm_internal_errcode("E00003") . "PHP >= 7.2 required."); - if (version_compare(SWOOLE_VERSION, "4.6.7") < 0 && !extension_loaded("pcntl")) { - Console::error(zm_internal_errcode("E00004") . "Swoole 版本必须不低于 4.6.7 或 PHP 安装加载了 pcntl 扩展!"); - die(); - } - } } diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php index c76e3cf9..0bc02fd3 100644 --- a/src/ZM/Event/EventDispatcher.php +++ b/src/ZM/Event/EventDispatcher.php @@ -91,7 +91,7 @@ class EventDispatcher try { foreach ((EventManager::$events[$this->class] ?? []) as $v) { $this->dispatchEvent($v, $this->rule, ...$params); - if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 分发结束。"); + if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . (is_string($v->method) ? $v->method : "{closure}") . " 分发结束。"); if ($this->status == self::STATUS_BEFORE_FAILED || $this->status == self::STATUS_RULE_FAILED) continue; if (is_callable($this->return_func) && $this->status === self::STATUS_NORMAL) { if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 正在执行返回值处理函数 ..."); @@ -121,7 +121,7 @@ class EventDispatcher public function dispatchEvent($v, $rule_func = null, ...$params) { $q_c = $v->class; $q_f = $v->method; - if ($q_c === "" && ($q_f instanceof Closure)) { + if (($q_c ?? "") === "" && ($q_f instanceof Closure)) { if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数的事件触发过程!"); if ($rule_func !== null && !$rule_func($v)) { if ($this->log) Console::verbose("[事件分发{$this->eid}] 闭包函数下的 ruleFunc 判断为 false, 拒绝执行此方法。"); @@ -158,6 +158,7 @@ class EventDispatcher $r[$k]->class = $q_c; $r[$k]->method = $q_f; $r[$k]->middleware = $middleware; + $r[$k]->current_event = $v; if (isset($middleware_obj["before"])) { if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在前置事件,执行中 ..."); $rs = $middleware_obj["before"]; diff --git a/src/ZM/Event/EventManager.php b/src/ZM/Event/EventManager.php index 9d6af1cf..2580d42e 100644 --- a/src/ZM/Event/EventManager.php +++ b/src/ZM/Event/EventManager.php @@ -23,7 +23,11 @@ class EventManager public static $req_mapping = []; public static function addEvent($event_name, ?AnnotationBase $event_obj) { - Console::debug("Adding event $event_name at ".$event_obj->class.":".$event_obj->method); + if ($event_obj->method instanceof \Closure) { + Console::debug("Adding event $event_name at @Anonymous"); + } else { + Console::debug("Adding event $event_name at " . ($event_obj->class) . ":" . ($event_obj->method)); + } self::$events[$event_name][] = $event_obj; (new AnnotationParser())->sortByLevel(self::$events, $event_name); } diff --git a/src/ZM/Event/SwooleEvent/OnOpen.php b/src/ZM/Event/SwooleEvent/OnOpen.php index d2779d27..99e88a8a 100644 --- a/src/ZM/Event/SwooleEvent/OnOpen.php +++ b/src/ZM/Event/SwooleEvent/OnOpen.php @@ -70,7 +70,7 @@ class OnOpen implements SwooleEvent try { $obb_onebot = ZMConfig::get("global", "onebot") ?? ZMConfig::get("global", "modules")["onebot"] ?? - ["status" => true, "single_bot_mode" => false, "message_level" => 99999]; + ["status" => true, "single_bot_mode" => false, "message_level" => 99]; $onebot_status = $obb_onebot["status"]; if ($conn->getName() === 'qq' && $onebot_status === true) { if ($obb_onebot["single_bot_mode"]) { diff --git a/src/ZM/Event/SwooleEvent/OnWorkerStart.php b/src/ZM/Event/SwooleEvent/OnWorkerStart.php index 9bcdbfc4..9cbd57bb 100644 --- a/src/ZM/Event/SwooleEvent/OnWorkerStart.php +++ b/src/ZM/Event/SwooleEvent/OnWorkerStart.php @@ -132,7 +132,7 @@ class OnWorkerStart implements SwooleEvent * @throws Exception */ private function loadAnnotations() { - + if (Framework::$instant_mode) goto skip; //加载各个模块的注解类,以及反射 Console::debug("Mapping annotations"); $parser = new AnnotationParser(); @@ -146,20 +146,6 @@ class OnWorkerStart implements SwooleEvent } } - //加载自定义的全局函数 - Console::debug("Loading context class..."); - $context_class = ZMConfig::get("global", "context_class"); - if (!is_a($context_class, ContextInterface::class, true)) { - throw new ZMKnownException("E00032", "Context class must implemented from ContextInterface!"); - } - - //加载插件 - $obb_onebot = ZMConfig::get("global", "onebot") ?? - ZMConfig::get("global", "modules")["onebot"] ?? - ["status" => true, "single_bot_mode" => false, "message_level" => 99999]; - - - // 检查是否允许热加载phar模块,允许的话将遍历phar内的文件 $plugin_enable_hotload = ZMConfig::get("global", "module_loader")["enable_hotload"] ?? false; if ($plugin_enable_hotload) { @@ -176,12 +162,23 @@ class OnWorkerStart implements SwooleEvent $parser->registerMods(); EventManager::loadEventByParser($parser); //加载事件 + skip: + //加载自定义的全局函数 + Console::debug("Loading context class..."); + $context_class = ZMConfig::get("global", "context_class"); + if (!is_a($context_class, ContextInterface::class, true)) { + throw new ZMKnownException("E00032", "Context class must implemented from ContextInterface!"); + } + //加载插件 + $obb_onebot = ZMConfig::get("global", "onebot") ?? + ZMConfig::get("global", "modules")["onebot"] ?? + ["status" => true, "single_bot_mode" => false, "message_level" => 99999]; if ($obb_onebot["status"]) { Console::debug("OneBot support enabled, listening OneBot event(3)."); $obj = new OnMessageEvent(); $obj->class = QQBot::class; $obj->method = 'handleByEvent'; - $obj->level = $obb_onebot["message_level"] ?? 99999; + $obj->level = $obb_onebot["message_level"] ?? 99; $obj->rule = 'connectIsQQ()'; EventManager::addEvent(OnMessageEvent::class, $obj); if ($obb_onebot["single_bot_mode"]) { diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index fb26967e..3cd3213d 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -9,6 +9,7 @@ use Exception; use Phar; use Swoole\Server\Port; use Throwable; +use ZM\Annotation\CQ\CQCommand; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; use ZM\Console\TermColor; @@ -76,9 +77,11 @@ class Framework */ private $setup_events = []; - public function __construct($args = []) { - $tty_width = $this->getTtyWidth(); + public static $instant_mode = false; + public function __construct($args = [], $instant_mode = false) { + $tty_width = $this->getTtyWidth(); + self::$instant_mode = $instant_mode; self::$argv = $args; ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config'); ZMConfig::setEnv($args["env"] ?? ""); @@ -124,7 +127,9 @@ class Framework $this->server_set["log_level"] = SWOOLE_LOG_DEBUG; $add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false; - $this->loadServerEvents(); + if ($instant_mode) { + $this->loadServerEvents(); + } $this->parseCliArgs(self::$argv, $add_port); @@ -148,7 +153,7 @@ class Framework } else { $out["worker"] = $this->server_set["worker_num"]; } - $out["environment"] = $args["env"] === null ? "default" : $args["env"]; + $out["environment"] = ($args["env"] ?? null) === null ? "default" : $args["env"]; $out["log_level"] = Console::getLevel(); $out["version"] = ZM_VERSION . (LOAD_MODE == 0 ? (" (build " . ZM_VERSION_ID . ")") : ""); $out["master_pid"] = posix_getpid(); @@ -182,7 +187,7 @@ class Framework } self::printProps($out, $tty_width, $args["log-theme"] === null); - if ($args["preview"]) { + if ($args["preview"] ?? false) { exit(); } diff --git a/src/ZM/Module/InstantModule.php b/src/ZM/Module/InstantModule.php new file mode 100644 index 00000000..fddca2e0 --- /dev/null +++ b/src/ZM/Module/InstantModule.php @@ -0,0 +1,18 @@ + $v) { + if (is_string($k)) $class->$k = $v; + } + $class->method = $callable; + $this->events[] = $class; + } +} \ No newline at end of file diff --git a/src/ZM/Module/ModuleBase.php b/src/ZM/Module/ModuleBase.php new file mode 100644 index 00000000..cc9d24e5 --- /dev/null +++ b/src/ZM/Module/ModuleBase.php @@ -0,0 +1,31 @@ +module_name = $module_name; + } + + /** + * @return mixed + */ + public function getModuleName() { + return $this->module_name; + } + + /** + * @return array + */ + public function getEvents(): array { + return $this->events; + } +} \ No newline at end of file diff --git a/src/ZM/ZMServer.php b/src/ZM/ZMServer.php new file mode 100644 index 00000000..70104bd6 --- /dev/null +++ b/src/ZM/ZMServer.php @@ -0,0 +1,59 @@ +app_name = $app_name; + } + + public function addModule($module_class) { + $this->modules[] = $module_class; + } + + public function run() { + Console::setLevel(4); + foreach ($this->modules as $module_class) { + foreach ($module_class->getEvents() as $event) { + EventManager::addEvent(get_class($event), $event); + } + } + echo "Running...\n"; + if (defined("WORKDING_DIR")) throw new InitException(); + + _zm_env_check(); + + define("WORKING_DIR", getcwd()); + define("SOURCE_ROOT_DIR", WORKING_DIR); + define("LOAD_MODE", is_dir(SOURCE_ROOT_DIR . "/src/ZM") ? 0 : 1); + define("FRAMEWORK_ROOT_DIR", realpath(__DIR__ . "/../../")); + define("ZM_VERSION_ID", ConsoleApplication::VERSION_ID); + define("ZM_VERSION", ConsoleApplication::VERSION); + $options = array_map(function ($x) { + return $x->getDefault(); + }, RunServerCommand::exportDefinition()->getOptions()); + (new Framework($options, true))->start(); + } + + /** + * @return mixed + */ + public function getAppName() { + return $this->app_name; + } +} \ No newline at end of file diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php index 59828904..b03bc259 100644 --- a/src/ZM/global_functions.php +++ b/src/ZM/global_functions.php @@ -29,6 +29,19 @@ function getClassPath($class_name) { if (file_exists($dir2)) return $dir2; else return null; } +/** + * 检查炸毛框架运行的环境 + * @internal + */ +function _zm_env_check() { + if (!extension_loaded("swoole")) die(zm_internal_errcode("E00001") . "Can not find swoole extension.\n"); + if (version_compare(SWOOLE_VERSION, "4.5.0") == -1) die(zm_internal_errcode("E00002") . "You must install swoole version >= 4.5.0 !"); + if (version_compare(PHP_VERSION, "7.2") == -1) die(zm_internal_errcode("E00003") . "PHP >= 7.2 required."); + if (version_compare(SWOOLE_VERSION, "4.6.7") < 0 && !extension_loaded("pcntl")) { + Console::error(zm_internal_errcode("E00004") . "Swoole 版本必须不低于 4.6.7 或 PHP 安装加载了 pcntl 扩展!"); + die(); + } +} /** * 使用自己定义的万(san)能分割函数 @@ -331,19 +344,33 @@ function zm_dump($var, ...$moreVars) { return $var; } -function zm_info($obj) { Console::info($obj); } +function zm_info($obj) { + Console::info($obj); +} -function zm_warning($obj) { Console::warning($obj); } +function zm_warning($obj) { + Console::warning($obj); +} -function zm_success($obj) { Console::success($obj); } +function zm_success($obj) { + Console::success($obj); +} -function zm_debug($obj) { Console::debug($obj); } +function zm_debug($obj) { + Console::debug($obj); +} -function zm_verbose($obj) { Console::verbose($obj); } +function zm_verbose($obj) { + Console::verbose($obj); +} -function zm_error($obj) { Console::error($obj); } +function zm_error($obj) { + Console::error($obj); +} -function zm_config($name, $key = null) { return ZMConfig::get($name, $key); } +function zm_config($name, $key = null) { + return ZMConfig::get($name, $key); +} function quick_reply_closure($reply) { return function () use ($reply) {