From ba5b793db7d9bfbf8f7a00d586460574b1bda1d6 Mon Sep 17 00:00:00 2001 From: jerry Date: Mon, 14 Dec 2020 01:24:34 +0800 Subject: [PATCH] update to 2.0.0-b5 version set modules config to array add subdir index.html update Example of Hello.php add Exception tester for TimerMiddleware.php add keyword for @CQCommand rename OnWorkerStart.php to OnStart.php remove SwooleEventAfter.php rename HandleEvent.php to SwooleHandler.php set ZMRobot callback mode default to true add getNextArg() and getFullArg() add EventDispatcher.php logger set Exception all based from ZMException fix recursive bug for Response.php add single_bot_mode add SingletonTrait.php add bot() function --- composer.json | 8 +- config/global.php | 7 +- resources/html/subdir/index.html | 10 +++ src/Module/Example/Hello.php | 21 ++++- src/Module/Middleware/TimerMiddleware.php | 16 +++- src/ZM/API/ZMRobot.php | 5 +- src/ZM/Annotation/AnnotationParser.php | 6 +- src/ZM/Annotation/CQ/CQCommand.php | 2 + .../Http/{After.php => HandleAfter.php} | 6 +- .../Http/{Before.php => HandleBefore.php} | 7 +- .../Swoole/{ZMSetup.php => OnSetup.php} | 2 +- .../Swoole/{OnWorkerStart.php => OnStart.php} | 2 +- src/ZM/Annotation/Swoole/SwooleEventAfter.php | 76 ------------------- .../{HandleEvent.php => SwooleHandler.php} | 4 +- src/ZM/ConsoleApplication.php | 3 - src/ZM/Context/Context.php | 25 ++++-- src/ZM/Context/ContextInterface.php | 4 + src/ZM/Event/EventDispatcher.php | 68 +++++++++++++++-- src/ZM/Event/ServerEventHandler.php | 53 ++++++++----- src/ZM/Exception/DbException.php | 4 +- src/ZM/Exception/InterruptException.php | 2 - src/ZM/Exception/InvalidArgumentException.php | 4 +- src/ZM/Exception/NotInitializedException.php | 2 - src/ZM/Exception/RobotNotFoundException.php | 1 - src/ZM/Exception/WaitTimeoutException.php | 1 - src/ZM/Framework.php | 10 +-- src/ZM/Http/Response.php | 6 +- src/ZM/Module/QQBot.php | 7 +- src/ZM/Store/LightCache.php | 12 +-- src/ZM/Store/LightCacheInside.php | 19 +++-- src/ZM/Store/ZMAtomic.php | 1 + src/ZM/Utils/SingletonTrait.php | 24 ++++++ src/ZM/Utils/ZMUtil.php | 3 - src/ZM/global_functions.php | 32 ++++++-- .../AnnotationParserRegisterTest.php | 6 +- 35 files changed, 285 insertions(+), 174 deletions(-) create mode 100644 resources/html/subdir/index.html rename src/ZM/Annotation/Http/{After.php => HandleAfter.php} (80%) rename src/ZM/Annotation/Http/{Before.php => HandleBefore.php} (65%) rename src/ZM/Annotation/Swoole/{ZMSetup.php => OnSetup.php} (85%) rename src/ZM/Annotation/Swoole/{OnWorkerStart.php => OnStart.php} (86%) delete mode 100644 src/ZM/Annotation/Swoole/SwooleEventAfter.php rename src/ZM/Annotation/Swoole/{HandleEvent.php => SwooleHandler.php} (83%) create mode 100644 src/ZM/Utils/SingletonTrait.php diff --git a/composer.json b/composer.json index a043fc23..852f7116 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "High performance QQ robot and web server development framework", "minimum-stability": "stable", "license": "Apache-2.0", - "version": "2.0.0-b4", + "version": "2.0.0-b5", "extra": {}, "authors": [ { @@ -35,6 +35,10 @@ "symfony/routing": "^5.1", "symfony/polyfill-php80": "^1.20" }, + "suggest": { + "ext-ctype": "*", + "ext-mbstring": "*" + }, "autoload": { "psr-4": { "ZM\\": "src/ZM", @@ -57,4 +61,4 @@ "phpunit/phpunit": "^9.3", "swoole/ide-helper": "@dev" } -} \ No newline at end of file +} diff --git a/config/global.php b/config/global.php index eed3fef0..03832714 100644 --- a/config/global.php +++ b/config/global.php @@ -50,8 +50,6 @@ $config['sql_config'] = [ 'sql_username' => 'name', 'sql_database' => 'db_name', 'sql_password' => '', - 'sql_enable_cache' => true, - 'sql_reset_cache' => '0300', 'sql_options' => [ PDO::ATTR_STRINGIFY_FETCHES => false, PDO::ATTR_EMULATE_PREPARES => false @@ -115,7 +113,10 @@ $config['command_register_class'] = [ /** 服务器启用的外部第三方和内部插件 */ $config['modules'] = [ - 'onebot' => true, // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭 + 'onebot' => [ + 'status' => true, + 'single_bot_mode' => false + ], // QQ机器人事件解析器,如果取消此项则默认为 true 开启状态,否则你手动填写 false 才会关闭 ]; return $config; diff --git a/resources/html/subdir/index.html b/resources/html/subdir/index.html new file mode 100644 index 00000000..5c370979 --- /dev/null +++ b/resources/html/subdir/index.html @@ -0,0 +1,10 @@ + + + + + Example page + + +
+ + diff --git a/src/Module/Example/Hello.php b/src/Module/Example/Hello.php index 40a41545..858ffe88 100644 --- a/src/Module/Example/Hello.php +++ b/src/Module/Example/Hello.php @@ -8,7 +8,9 @@ use ZM\ConnectionManager\ConnectionObject; use ZM\Console\Console; use ZM\Annotation\CQ\CQCommand; use ZM\Annotation\Http\RequestMapping; +use ZM\Event\EventDispatcher; use ZM\Store\Redis\ZMRedis; +use ZM\Utils\ZMUtil; /** * Class Hello @@ -40,11 +42,20 @@ class Hello } } + /** + * 使用命令 .reload 发给机器人远程重载,注意将 user_id 换成你自己的 QQ + * @CQCommand(".reload",user_id=627577391) + */ + public function reload() { + ctx()->reply("重启中..."); + ZMUtil::reload(); + } + /** * @CQCommand("我是谁") */ public function whoami() { - $user = ctx()->getRobot()->setCallback(true)->getLoginInfo(); + $user = ctx()->getRobot()->getLoginInfo(); return "你是" . $user["data"]["nickname"] . ",QQ号是" . $user["data"]["user_id"]; } @@ -121,6 +132,14 @@ class Hello Console::info("机器人 " . $conn->getOption("connect_id") . " 已断开连接!"); } + /** + * 阻止 Chrome 自动请求 /favicon.ico 导致的多条请求并发和干扰 + * @OnSwooleEvent("request",rule="ctx()->getRequest()->server['request_uri'] == '/favicon.ico'",level=200) + */ + public function onRequest() { + EventDispatcher::interrupt(); + } + /** * 框架会默认关闭未知的WebSocket链接,因为这个绑定的事件,你可以根据你自己的需求进行修改 * @OnSwooleEvent(type="open",rule="connectIsDefault()") diff --git a/src/Module/Middleware/TimerMiddleware.php b/src/Module/Middleware/TimerMiddleware.php index 34e21c7b..d90d2570 100644 --- a/src/Module/Middleware/TimerMiddleware.php +++ b/src/Module/Middleware/TimerMiddleware.php @@ -2,8 +2,9 @@ namespace Module\Middleware; -use ZM\Annotation\Http\After; -use ZM\Annotation\Http\Before; +use ZM\Annotation\Http\HandleAfter; +use ZM\Annotation\Http\HandleBefore; +use ZM\Annotation\Http\HandleException; use ZM\Annotation\Http\MiddlewareClass; use ZM\Console\Console; use ZM\Http\MiddlewareInterface; @@ -19,7 +20,7 @@ class TimerMiddleware implements MiddlewareInterface private $starttime; /** - * @Before() + * @HandleBefore() * @return bool */ public function onBefore() { @@ -28,9 +29,16 @@ class TimerMiddleware implements MiddlewareInterface } /** - * @After() + * @HandleAfter() */ public function onAfter() { Console::info("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms."); } + + /** + * @HandleException(\Exception::class) + */ + public function onException() { + Console::error("Using " . round((microtime(true) - $this->starttime) * 1000, 2) . " ms but an Exception occurred."); + } } diff --git a/src/ZM/API/ZMRobot.php b/src/ZM/API/ZMRobot.php index 126382bb..45230948 100644 --- a/src/ZM/API/ZMRobot.php +++ b/src/ZM/API/ZMRobot.php @@ -24,7 +24,7 @@ class ZMRobot /** @var ConnectionObject|null */ private $connection; - private $callback = null; + private $callback = true; private $prefix = 0; /** @@ -50,6 +50,9 @@ class ZMRobot return new ZMRobot($r[array_rand($r)]); } + public static function getFirst() { + } + /** * @return ZMRobot[] */ diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index 6bcb7eaf..0024768a 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -9,7 +9,7 @@ use ZM\Console\Console; use ReflectionClass; use ReflectionException; use ReflectionMethod; -use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping}; +use ZM\Annotation\Http\{HandleAfter, HandleBefore, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping}; use ZM\Annotation\Interfaces\Level; use ZM\Annotation\Module\Closed; use ZM\Utils\DataProvider; @@ -287,8 +287,8 @@ class AnnotationParser foreach ($reflection_class->getMethods() as $vss) { $method_annotations = $this->reader->getMethodAnnotations($vss); foreach ($method_annotations as $vsss) { - if ($vsss instanceof Before) $result["before"] = $vss->getName(); - if ($vsss instanceof After) $result["after"] = $vss->getName(); + if ($vsss instanceof HandleBefore) $result["before"] = $vss->getName(); + if ($vsss instanceof HandleAfter) $result["after"] = $vss->getName(); if ($vsss instanceof HandleException) { $result["exceptions"][$vsss->class_name] = $vss->getName(); } diff --git a/src/ZM/Annotation/CQ/CQCommand.php b/src/ZM/Annotation/CQ/CQCommand.php index 6a6d4021..a7ec162b 100644 --- a/src/ZM/Annotation/CQ/CQCommand.php +++ b/src/ZM/Annotation/CQ/CQCommand.php @@ -25,6 +25,8 @@ class CQCommand extends AnnotationBase implements Level public $start_with = ""; /** @var string */ public $end_with = ""; + /** @var string */ + public $keyword = ""; /** @var string[] */ public $alias = []; /** @var string */ diff --git a/src/ZM/Annotation/Http/After.php b/src/ZM/Annotation/Http/HandleAfter.php similarity index 80% rename from src/ZM/Annotation/Http/After.php rename to src/ZM/Annotation/Http/HandleAfter.php index ef24db54..9d2e43a4 100644 --- a/src/ZM/Annotation/Http/After.php +++ b/src/ZM/Annotation/Http/HandleAfter.php @@ -9,11 +9,11 @@ use Doctrine\Common\Annotations\Annotation\Target; use ZM\Annotation\AnnotationBase; /** - * Class After + * Class HandleAfter * @package ZM\Annotation\Http * @Annotation * @Target("METHOD") */ -class After extends AnnotationBase +class HandleAfter extends AnnotationBase { -} \ No newline at end of file +} diff --git a/src/ZM/Annotation/Http/Before.php b/src/ZM/Annotation/Http/HandleBefore.php similarity index 65% rename from src/ZM/Annotation/Http/Before.php rename to src/ZM/Annotation/Http/HandleBefore.php index f09cfc40..90a092a5 100644 --- a/src/ZM/Annotation/Http/Before.php +++ b/src/ZM/Annotation/Http/HandleBefore.php @@ -4,16 +4,15 @@ namespace ZM\Annotation\Http; -use Doctrine\Common\Annotations\Annotation\Required; use Doctrine\Common\Annotations\Annotation\Target; use ZM\Annotation\AnnotationBase; /** - * Class Before + * Class HandleBefore * @package ZM\Annotation\Http * @Annotation * @Target("METHOD") */ -class Before extends AnnotationBase +class HandleBefore extends AnnotationBase { -} \ No newline at end of file +} diff --git a/src/ZM/Annotation/Swoole/ZMSetup.php b/src/ZM/Annotation/Swoole/OnSetup.php similarity index 85% rename from src/ZM/Annotation/Swoole/ZMSetup.php rename to src/ZM/Annotation/Swoole/OnSetup.php index b3165937..654002b6 100644 --- a/src/ZM/Annotation/Swoole/ZMSetup.php +++ b/src/ZM/Annotation/Swoole/OnSetup.php @@ -13,6 +13,6 @@ use ZM\Annotation\AnnotationBase; * @Annotation * @Target("METHOD") */ -class ZMSetup extends AnnotationBase +class OnSetup extends AnnotationBase { } diff --git a/src/ZM/Annotation/Swoole/OnWorkerStart.php b/src/ZM/Annotation/Swoole/OnStart.php similarity index 86% rename from src/ZM/Annotation/Swoole/OnWorkerStart.php rename to src/ZM/Annotation/Swoole/OnStart.php index 1eb0f14d..e95e6275 100644 --- a/src/ZM/Annotation/Swoole/OnWorkerStart.php +++ b/src/ZM/Annotation/Swoole/OnStart.php @@ -12,7 +12,7 @@ use ZM\Annotation\AnnotationBase; * @Annotation * @Target("ALL") */ -class OnWorkerStart extends AnnotationBase +class OnStart extends AnnotationBase { /** * @var int diff --git a/src/ZM/Annotation/Swoole/SwooleEventAfter.php b/src/ZM/Annotation/Swoole/SwooleEventAfter.php deleted file mode 100644 index 6626a69e..00000000 --- a/src/ZM/Annotation/Swoole/SwooleEventAfter.php +++ /dev/null @@ -1,76 +0,0 @@ -type; - } - - /** - * @param string $type - */ - public function setType(string $type) { - $this->type = $type; - } - - /** - * @return string - */ - public function getRule(): string { - return $this->rule; - } - - /** - * @param string $rule - */ - public function setRule(string $rule) { - $this->rule = $rule; - } - - /** - * @return int - */ - public function getLevel(): int { - return $this->level; - } - - /** - * @param int $level - */ - public function setLevel(int $level) { - $this->level = $level; - } - - -} \ No newline at end of file diff --git a/src/ZM/Annotation/Swoole/HandleEvent.php b/src/ZM/Annotation/Swoole/SwooleHandler.php similarity index 83% rename from src/ZM/Annotation/Swoole/HandleEvent.php rename to src/ZM/Annotation/Swoole/SwooleHandler.php index 80a0fac1..d70fb7d0 100644 --- a/src/ZM/Annotation/Swoole/HandleEvent.php +++ b/src/ZM/Annotation/Swoole/SwooleHandler.php @@ -9,12 +9,12 @@ use Doctrine\Common\Annotations\Annotation\Target; use ZM\Annotation\AnnotationBase; /** - * Class HandleEvent + * Class SwooleHandler * @package ZM\Annotation\Swoole * @Annotation * @Target("METHOD") */ -class HandleEvent extends AnnotationBase +class SwooleHandler extends AnnotationBase { /** * @var string diff --git a/src/ZM/ConsoleApplication.php b/src/ZM/ConsoleApplication.php index 08bece9a..f19ab656 100644 --- a/src/ZM/ConsoleApplication.php +++ b/src/ZM/ConsoleApplication.php @@ -68,9 +68,6 @@ class ConsoleApplication extends Application } } - if (!is_dir(DataProvider::getWorkingDir() . '/src/')) { - die("Unable to find source directory.\nMaybe you need to run \"init\"?"); - } ZMConfig::setDirectory(DataProvider::getWorkingDir() . '/config'); ZMConfig::env($args["env"] ?? ""); if (ZMConfig::get("global") === false) { diff --git a/src/ZM/Context/Context.php b/src/ZM/Context/Context.php index 6ad6fa12..e1de0e1e 100644 --- a/src/ZM/Context/Context.php +++ b/src/ZM/Context/Context.php @@ -149,7 +149,7 @@ class Context implements ContextInterface } catch (Exception $e) { $r = false; } - if($r === false) { + if ($r === false) { throw new WaitTimeoutException($this, $timeout_prompt); } return $r["message"]; @@ -203,7 +203,6 @@ class Context implements ContextInterface switch ($mode) { case ZM_MATCH_ALL: $p = $arg; - array_shift($p); return trim(implode(" ", $p)) == "" ? $this->waitMessage($prompt_msg) : trim(implode(" ", $p)); case ZM_MATCH_NUMBER: foreach ($arg as $k => $v) { @@ -215,9 +214,9 @@ class Context implements ContextInterface } return $this->waitMessage($prompt_msg); case ZM_MATCH_FIRST: - if (isset($arg[1])) { - $a = $arg[1]; - array_splice($arg, 1, 1); + if (isset($arg[0])) { + $a = $arg[0]; + array_splice($arg, 0, 1); ctx()->setCache("match", $arg); return $a; } else { @@ -227,6 +226,22 @@ class Context implements ContextInterface throw new InvalidArgumentException(); } + /** + * @param string $prompt_msg + * @return int|mixed|string + * @throws InvalidArgumentException + * @throws WaitTimeoutException + */ + public function getNextArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_FIRST, $prompt_msg); } + + /** + * @param string $prompt_msg + * @return int|mixed|string + * @throws InvalidArgumentException + * @throws WaitTimeoutException + */ + public function getFullArg($prompt_msg = "") { return $this->getArgs(ZM_MATCH_ALL, $prompt_msg); } + public function cloneFromParent() { set_coroutine_params(self::$context[Co::getPcid()] ?? self::$context[$this->cid]); return context(); diff --git a/src/ZM/Context/ContextInterface.php b/src/ZM/Context/ContextInterface.php index 94689d37..00cbda60 100644 --- a/src/ZM/Context/ContextInterface.php +++ b/src/ZM/Context/ContextInterface.php @@ -103,6 +103,10 @@ interface ContextInterface */ public function getArgs($mode, $prompt_msg); + public function getNextArg($prompt_msg = ""); + + public function getFullArg($prompt_msg = ""); + public function setCache($key, $value); /** diff --git a/src/ZM/Event/EventDispatcher.php b/src/ZM/Event/EventDispatcher.php index 5f5339bf..8bbd3654 100644 --- a/src/ZM/Event/EventDispatcher.php +++ b/src/ZM/Event/EventDispatcher.php @@ -7,8 +7,12 @@ namespace ZM\Event; use Doctrine\Common\Annotations\AnnotationException; use Exception; use ZM\Annotation\AnnotationBase; -use ZM\Annotation\CQ\CQMetaEvent; +use ZM\Console\Console; use ZM\Exception\InterruptException; +use ZM\Exception\ZMException; +use ZM\Store\LightCacheInside; +use ZM\Store\Lock\SpinLock; +use ZM\Store\ZMAtomic; use ZM\Utils\ZMUtil; class EventDispatcher @@ -19,6 +23,10 @@ class EventDispatcher private $rule = null; /** @var null|callable */ private $return_func = null; + /** @var bool */ + private $log = false; + /** @var int */ + private $eid = 0; /** * @throws InterruptException @@ -27,8 +35,32 @@ class EventDispatcher throw new InterruptException('interrupt'); } + public static function enableEventTrace($event_class) { + SpinLock::lock("_event_trace"); + $list = LightCacheInside::get("wait_api", "event_trace"); + $list[$event_class] = true; + LightCacheInside::set("wait_api", "event_trace", $list); + SpinLock::unlock("_event_trace"); + } + + public static function disableEventTrace($event_class) { + SpinLock::lock("_event_trace"); + $list = LightCacheInside::get("wait_api", "event_trace"); + unset($list[$event_class]); + LightCacheInside::set("wait_api", "event_trace", $list); + SpinLock::unlock("_event_trace"); + } + public function __construct(string $class = '') { $this->class = $class; + try { + $this->eid = ZMAtomic::get("_event_id")->add(1); + $list = LightCacheInside::get("wait_api", "event_trace"); + } catch (ZMException $e) { + $list = []; + } + if (isset($list[$class])) $this->log = true; + if ($this->log) Console::verbose("[事件分发{$this->eid}] 开始分发事件: " . $class); } public function setRuleFunction(callable $rule = null) { @@ -43,10 +75,13 @@ class EventDispatcher public function dispatchEvents(...$params) { try { - foreach ((EventManager::$events[$this->class] ?? []) as $v) { $result = $this->dispatchEvent($v, $this->rule, ...$params); - if ($result !== false && is_callable($this->return_func)) ($this->return_func)($result); + if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 分发结束。"); + if ($result !== false && is_callable($this->return_func)) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] 单一对象 " . $v->class . "::" . $v->method . " 正在执行返回值处理函数 ..."); + ($this->return_func)($result); + } } return true; } catch (InterruptException $e) { @@ -67,9 +102,15 @@ class EventDispatcher public function dispatchEvent(?AnnotationBase $v, $rule_func = null, ...$params) { $q_c = $v->class; $q_f = $v->method; - if ($rule_func !== null && !$rule_func($v)) return false; + if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在判断 " . $q_c . "::" . $q_f . " 方法下的 rule ..."); + if ($rule_func !== null && !$rule_func($v)) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 判断为 false, 拒绝执行此方法。"); + return false; + } + if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法下的 rule 为真,继续执行方法本身 ..."); if (isset(EventManager::$middleware_map[$q_c][$q_f])) { $middlewares = EventManager::$middleware_map[$q_c][$q_f]; + if ($this->log) Console::verbose("[事件分发{$this->eid}] " . $q_c . "::" . $q_f . " 方法还绑定了 Middleware:" . implode(", ", $middlewares)); $before_result = true; $r = []; foreach ($middlewares as $k => $middleware) { @@ -81,22 +122,34 @@ class EventDispatcher $r[$k]->class = $q_c; $r[$k]->method = $q_f; if (isset($middleware_obj["before"])) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在前置事件,执行中 ..."); $rs = $middleware_obj["before"]; $before_result = $r[$k]->$rs(...$params); - if ($before_result === false) break; + if ($before_result === false) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 前置事件为 false,停止执行原事件,开始执行下一事件。"); + break; + } else { + if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 前置事件为 true,继续执行原事件。"); + } } } if ($before_result) { try { $q_o = ZMUtil::getModInstance($q_c); + if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ..."); $result = $q_o->$q_f(...$params); } catch (Exception $e) { - if ($e instanceof InterruptException) throw $e; + if ($e instanceof InterruptException) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] 检测到事件阻断调用,正在跳出事件分发器 ..."); + throw $e; + } + if ($this->log) Console::verbose("[事件分发{$this->eid}] 方法 " . $q_c . "::" . $q_f . " 执行过程中抛出了异常,正在倒序查找 Middleware 中的捕获方法 ..."); for ($i = count($middlewares) - 1; $i >= 0; --$i) { $middleware_obj = EventManager::$middlewares[$middlewares[$i]]; if (!isset($middleware_obj["exceptions"])) continue; foreach ($middleware_obj["exceptions"] as $name => $method) { if ($e instanceof $name) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] 方法 " . $q_c . "::" . $q_f . " 的异常 " . get_class($e) . " 被 Middleware:" . $middlewares[$i] . " 下的 " . get_class($r[$i]) . "::" . $method . " 捕获。"); $r[$i]->$method($e); self::interrupt(); } @@ -107,7 +160,9 @@ class EventDispatcher for ($i = count($middlewares) - 1; $i >= 0; --$i) { $middleware_obj = EventManager::$middlewares[$middlewares[$i]]; if (isset($middleware_obj["after"], $r[$i])) { + if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 存在后置事件,执行中 ..."); $r[$i]->{$middleware_obj["after"]}(...$params); + if ($this->log) Console::verbose("[事件分发{$this->eid}] Middleware 后置事件执行完毕!"); } } return $result; @@ -115,6 +170,7 @@ class EventDispatcher return false; } else { $q_o = ZMUtil::getModInstance($q_c); + if ($this->log) Console::verbose("[事件分发{$this->eid}] 正在执行方法 " . $q_c . "::" . $q_f . " ..."); return $q_o->$q_f(...$params); } } diff --git a/src/ZM/Event/ServerEventHandler.php b/src/ZM/Event/ServerEventHandler.php index 6783e1be..ddb3827d 100644 --- a/src/ZM/Event/ServerEventHandler.php +++ b/src/ZM/Event/ServerEventHandler.php @@ -16,7 +16,7 @@ use Swoole\Process; use Swoole\Timer; use ZM\Annotation\AnnotationParser; use ZM\Annotation\Http\RequestMapping; -use ZM\Annotation\Swoole\OnWorkerStart; +use ZM\Annotation\Swoole\OnStart; use ZM\Annotation\Swoole\OnSwooleEvent; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; @@ -24,7 +24,7 @@ use ZM\Console\Console; use Swoole\Http\Request; use Swoole\Server; use Swoole\WebSocket\Frame; -use ZM\Annotation\Swoole\HandleEvent; +use ZM\Annotation\Swoole\SwooleHandler; use ZM\Console\TermColor; use ZM\Context\Context; use ZM\Context\ContextInterface; @@ -33,6 +33,7 @@ use ZM\Exception\DbException; use ZM\Framework; use ZM\Http\Response; use ZM\Module\QQBot; +use ZM\Store\LightCacheInside; use ZM\Store\MySQL\SqlPoolStorage; use ZM\Store\Redis\ZMRedisPool; use ZM\Store\ZMBuf; @@ -44,7 +45,7 @@ use ZM\Utils\ZMUtil; class ServerEventHandler { /** - * @HandleEvent("start") + * @SwooleHandler("start") */ public function onStart() { global $terminal_id; @@ -87,14 +88,14 @@ class ServerEventHandler } /** - * @HandleEvent("shutdown") + * @SwooleHandler("shutdown") */ public function onShutdown() { Console::debug("正在关闭 Master 进程,pid=" . posix_getpid()); } /** - * @HandleEvent("WorkerStop") + * @SwooleHandler("WorkerStop") * @param $server * @param $worker_id */ @@ -103,7 +104,7 @@ class ServerEventHandler } /** - * @HandleEvent("WorkerStart") + * @SwooleHandler("WorkerStart") * @param Server $server * @param $worker_id */ @@ -205,12 +206,12 @@ class ServerEventHandler EventManager::registerTimerTick(); //启动计时器 //ZMBuf::unsetCache("wait_start"); set_coroutine_params(["server" => $server, "worker_id" => $worker_id]); - $dispatcher = new EventDispatcher(OnWorkerStart::class); + $dispatcher = new EventDispatcher(OnStart::class); $dispatcher->setRuleFunction(function ($v) { return server()->worker_id === $v->worker_id || $v->worker_id === -1; }); $dispatcher->dispatchEvents($server, $worker_id); - Console::debug("@OnWorkerStart 执行完毕"); + Console::debug("@OnStart 执行完毕"); } catch (Exception $e) { Console::error("Worker加载出错!停止服务!"); Console::error($e->getMessage() . "\n" . $e->getTraceAsString()); @@ -243,7 +244,7 @@ class ServerEventHandler } /** - * @HandleEvent("message") + * @SwooleHandler("message") * @param $server * @param Frame $frame */ @@ -266,9 +267,9 @@ class ServerEventHandler } }); try { - $starttime = microtime(true); + //$starttime = microtime(true); $dispatcher->dispatchEvents($conn); - Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!"); + //Console::success("Used ".round((microtime(true) - $starttime) * 1000, 3)." ms!"); } catch (Exception $e) { $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; Console::error("Uncaught exception " . get_class($e) . " when calling \"message\": " . $error_msg); @@ -282,7 +283,7 @@ class ServerEventHandler } /** - * @HandleEvent("request") + * @SwooleHandler("request") * @param $request * @param $response */ @@ -356,7 +357,7 @@ class ServerEventHandler } /** - * @HandleEvent("open") + * @SwooleHandler("open") * @param $server * @param Request $request */ @@ -369,6 +370,7 @@ class ServerEventHandler $conn = ManagerGM::get($request->fd); set_coroutine_params(["server" => $server, "request" => $request, "connection" => $conn, "fd" => $request->fd]); $conn->setOption("connect_id", strval($request->header["x-self-id"]) ?? ""); + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { @@ -380,6 +382,11 @@ class ServerEventHandler } }); try { + if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) { + if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) { + LightCacheInside::set("connect", "conn_fd", $request->fd); + } + } $dispatcher->dispatchEvents($conn); } catch (Exception $e) { $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; @@ -394,7 +401,7 @@ class ServerEventHandler } /** - * @HandleEvent("close") + * @SwooleHandler("close") * @param $server * @param $fd */ @@ -404,6 +411,8 @@ class ServerEventHandler if ($conn === null) return; Console::debug("Calling Swoole \"close\" event from fd=" . $fd); set_coroutine_params(["server" => $server, "connection" => $conn, "fd" => $fd]); + + $dispatcher = new EventDispatcher(OnSwooleEvent::class); $dispatcher->setRuleFunction(function ($v) { if ($v->getRule() == '') { @@ -415,6 +424,11 @@ class ServerEventHandler } }); try { + if ($conn->getName() === 'qq' && ZMConfig::get("global", "modules")["onebot"]["status"] === true) { + if (ZMConfig::get("global", "modules")["onebot"]["single_bot_mode"]) { + LightCacheInside::set("connect", "conn_fd", -1); + } + } $dispatcher->dispatchEvents($conn); } catch (Exception $e) { $error_msg = $e->getMessage() . " at " . $e->getFile() . "(" . $e->getLine() . ")"; @@ -429,7 +443,7 @@ class ServerEventHandler } /** - * @HandleEvent("pipeMessage") + * @SwooleHandler("pipeMessage") * @param $server * @param $src_worker_id * @param $data @@ -462,7 +476,7 @@ class ServerEventHandler } /** - * @HandleEvent("task") + * @SwooleHandler("task") */ public function onTask() { } @@ -504,7 +518,7 @@ class ServerEventHandler //加载插件 $plugins = ZMConfig::get("global", "modules") ?? []; - if (!isset($plugins["onebot"])) $plugins["onebot"] = true; + if (!isset($plugins["onebot"])) $plugins["onebot"] = ["status" => true, "single_bot_mode" => false]; if ($plugins["onebot"]) { $obj = new OnSwooleEvent(); @@ -514,6 +528,11 @@ class ServerEventHandler $obj->level = 99999; $obj->rule = 'connectIsQQ()'; EventManager::addEvent(OnSwooleEvent::class, $obj); + if ($plugins["onebot"]["single_bot_mode"]) { + LightCacheInside::set("connect", "conn_fd", -1); + } else { + LightCacheInside::set("connect", "conn_fd", -2); + } } //TODO: 编写加载外部插件的方式 diff --git a/src/ZM/Exception/DbException.php b/src/ZM/Exception/DbException.php index d663eacc..007d9ffe 100644 --- a/src/ZM/Exception/DbException.php +++ b/src/ZM/Exception/DbException.php @@ -4,9 +4,7 @@ namespace ZM\Exception; -use Exception; - class DbException extends ZMException { -} \ No newline at end of file +} diff --git a/src/ZM/Exception/InterruptException.php b/src/ZM/Exception/InterruptException.php index f5d00aa8..b5b5a5f8 100644 --- a/src/ZM/Exception/InterruptException.php +++ b/src/ZM/Exception/InterruptException.php @@ -4,8 +4,6 @@ namespace ZM\Exception; -use Exception; - class InterruptException extends ZMException { diff --git a/src/ZM/Exception/InvalidArgumentException.php b/src/ZM/Exception/InvalidArgumentException.php index 7ccc3b70..0052a29e 100644 --- a/src/ZM/Exception/InvalidArgumentException.php +++ b/src/ZM/Exception/InvalidArgumentException.php @@ -4,9 +4,7 @@ namespace ZM\Exception; -use Exception; - class InvalidArgumentException extends ZMException { -} \ No newline at end of file +} diff --git a/src/ZM/Exception/NotInitializedException.php b/src/ZM/Exception/NotInitializedException.php index f1bf753d..6f56e830 100644 --- a/src/ZM/Exception/NotInitializedException.php +++ b/src/ZM/Exception/NotInitializedException.php @@ -4,8 +4,6 @@ namespace ZM\Exception; -use Exception; - class NotInitializedException extends ZMException { diff --git a/src/ZM/Exception/RobotNotFoundException.php b/src/ZM/Exception/RobotNotFoundException.php index 9b734260..49880dda 100644 --- a/src/ZM/Exception/RobotNotFoundException.php +++ b/src/ZM/Exception/RobotNotFoundException.php @@ -4,7 +4,6 @@ namespace ZM\Exception; -use Exception; use Throwable; /** diff --git a/src/ZM/Exception/WaitTimeoutException.php b/src/ZM/Exception/WaitTimeoutException.php index c9d18255..76472561 100644 --- a/src/ZM/Exception/WaitTimeoutException.php +++ b/src/ZM/Exception/WaitTimeoutException.php @@ -4,7 +4,6 @@ namespace ZM\Exception; -use Exception; use Throwable; class WaitTimeoutException extends ZMException diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index b575733e..aef733f7 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -6,7 +6,7 @@ namespace ZM; use Doctrine\Common\Annotations\AnnotationReader; use Exception; -use ZM\Annotation\Swoole\ZMSetup; +use ZM\Annotation\Swoole\OnSetup; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; use ZM\Event\ServerEventHandler; @@ -20,7 +20,7 @@ use ReflectionException; use ReflectionMethod; use Swoole\Runtime; use Swoole\WebSocket\Server; -use ZM\Annotation\Swoole\HandleEvent; +use ZM\Annotation\Swoole\SwooleHandler; use ZM\Console\Console; use ZM\Utils\ZMUtil; @@ -67,7 +67,7 @@ class Framework self::$server = new Server(ZMConfig::get("global", "host"), ZMConfig::get("global", "port")); $this->server_set = ZMConfig::get("global", "swoole"); Console::init( - ZMConfig::get("global", "info_level"), + ZMConfig::get("global", "info_level") ?? 2, self::$server, $args["log-theme"] ?? "default", ($o = ZMConfig::get("console_color")) === false ? [] : $o @@ -150,11 +150,11 @@ class Framework $method_annotations = $reader->getMethodAnnotations($vs); if ($method_annotations != []) { $annotation = $method_annotations[0]; - if ($annotation instanceof HandleEvent) { + if ($annotation instanceof SwooleHandler) { $annotation->class = $v; $annotation->method = $vs->getName(); $event_list[strtolower($annotation->event)] = $annotation; - } elseif ($annotation instanceof ZMSetup) { + } elseif ($annotation instanceof OnSetup) { $annotation->class = $v; $annotation->method = $vs->getName(); $c = new $v(); diff --git a/src/ZM/Http/Response.php b/src/ZM/Http/Response.php index 2c937f89..c8cae4b8 100644 --- a/src/ZM/Http/Response.php +++ b/src/ZM/Http/Response.php @@ -4,6 +4,8 @@ namespace ZM\Http; +use ZM\Console\Console; + class Response { @@ -92,6 +94,7 @@ class Response */ public function status($http_code, $reason = null) { $this->status_code = $http_code; + Console::trace(); return $this->response->status($http_code, $reason); } @@ -184,7 +187,8 @@ class Response * @return mixed */ public function redirect($location, $http_code = null) { - return $this->redirect($location, $http_code); + $this->is_end = true; + return $this->response->redirect($location, $http_code); } /** diff --git a/src/ZM/Module/QQBot.php b/src/ZM/Module/QQBot.php index 902c1db7..c37207a4 100644 --- a/src/ZM/Module/QQBot.php +++ b/src/ZM/Module/QQBot.php @@ -21,7 +21,6 @@ use ZM\Utils\CoMessage; /** * Class QQBot * @package ZM\Module - * @ExternalModule("onebot") */ class QQBot { @@ -80,12 +79,13 @@ class QQBot //分发CQCommand事件 $dispatcher = new EventDispatcher(CQCommand::class); $dispatcher->setRuleFunction(function (CQCommand $v) use ($word) { - if ($v->match == "" && $v->pattern == "" && $v->regex == "") return false; + if(array_diff([$v->match, $v->pattern, $v->regex, $v->keyword, $v->end_with, $v->start_with], [""]) == []) return false; elseif (($v->user_id == 0 || ($v->user_id != 0 && $v->user_id == ctx()->getUserId())) && ($v->group_id == 0 || ($v->group_id != 0 && $v->group_id == (ctx()->getGroupId() ?? 0))) && ($v->message_type == '' || ($v->message_type != '' && $v->message_type == ctx()->getMessageType())) ) { if(($word[0] != "" && $v->match == $word[0]) || in_array($word[0], $v->alias)) { + array_shift($word); ctx()->setCache("match", $word); return true; } elseif ($v->start_with != "" && mb_strpos(ctx()->getMessage(), $v->start_with) === 0) { @@ -94,6 +94,9 @@ class QQBot } elseif ($v->end_with != "" && strlen(ctx()->getMessage()) == (strripos(ctx()->getMessage(), $v->end_with) + strlen($v->end_with))) { ctx()->setCache("match", [substr(ctx()->getMessage(), 0, strripos(ctx()->getMessage(), $v->end_with))]); return true; + } elseif ($v->keyword != "" && mb_strpos(ctx()->getMessage(), $v->keyword) !== false) { + ctx()->setCache("match", explode($v->keyword, ctx()->getMessage())); + return true; }elseif ($v->pattern != "") { $match = matchArgs($v->pattern, ctx()->getMessage()); if($match !== false) { diff --git a/src/ZM/Store/LightCache.php b/src/ZM/Store/LightCache.php index b6f0c720..c28c61e5 100644 --- a/src/ZM/Store/LightCache.php +++ b/src/ZM/Store/LightCache.php @@ -92,7 +92,7 @@ class LightCache } elseif (is_bool($value)) { $data_type = "bool"; $value = json_encode($value); - }else { + } else { throw new Exception("Only can set string, array and int"); } try { @@ -126,7 +126,7 @@ class LightCache throw new Exception("Only can set string, array and int"); } try { - if(self::$kv_table->get($key) === false) return false; + if (self::$kv_table->get($key) === false) return false; return self::$kv_table->set($key, [ "value" => $value, "data_type" => $data_type @@ -170,7 +170,7 @@ class LightCache } public static function savePersistence() { - if(self::$kv_table === null) return; + if (self::$kv_table === null) return; $r = []; foreach (self::$kv_table as $k => $v) { if ($v["expire"] === -2) { @@ -179,8 +179,10 @@ class LightCache } } if(self::$config["persistence_path"] == "") return; - $r = file_put_contents(self::$config["persistence_path"], json_encode($r, 128 | 256)); - if ($r === false) Console::error("Not saved, please check your \"persistence_path\"!"); + if (file_exists(self::$config["persistence_path"])) { + $r = file_put_contents(self::$config["persistence_path"], json_encode($r, 128 | 256)); + if ($r === false) Console::error("Not saved, please check your \"persistence_path\"!"); + } } private static function checkExpire($key) { diff --git a/src/ZM/Store/LightCacheInside.php b/src/ZM/Store/LightCacheInside.php index f3838cc7..c161eca0 100644 --- a/src/ZM/Store/LightCacheInside.php +++ b/src/ZM/Store/LightCacheInside.php @@ -6,6 +6,7 @@ namespace ZM\Store; use Exception; use Swoole\Table; +use ZM\Exception\ZMException; class LightCacheInside { @@ -15,9 +16,11 @@ class LightCacheInside public static $last_error = ''; public static function init() { - self::$kv_table["wait_api"] = new Table(2, 0); + self::$kv_table["wait_api"] = new Table(3, 0); self::$kv_table["wait_api"]->column("value", Table::TYPE_STRING, 65536); - $result = self::$kv_table["wait_api"]->create(); + self::$kv_table["connect"] = new Table(8, 0); + self::$kv_table["connect"]->column("value", Table::TYPE_STRING, 256); + $result = self::$kv_table["wait_api"]->create() && self::$kv_table["connect"]->create(); if ($result === false) { self::$last_error = '系统内存不足,申请失败'; return $result; @@ -25,8 +28,14 @@ class LightCacheInside return $result; } + /** + * @param string $table + * @param string $key + * @return mixed|null + * @throws ZMException + */ public static function get(string $table, string $key) { - if (!isset(self::$kv_table[$table])) throw new Exception("not initialized LightCache"); + if (!isset(self::$kv_table[$table])) throw new ZMException("not initialized LightCache"); $r = self::$kv_table[$table]->get($key); return $r === false ? null : json_decode($r["value"], true); } @@ -36,10 +45,10 @@ class LightCacheInside * @param string $key * @param string|array|int $value * @return mixed - * @throws Exception + * @throws ZMException */ public static function set(string $table, string $key, $value) { - if (self::$kv_table === null) throw new Exception("not initialized LightCache"); + if (self::$kv_table === null) throw new ZMException("not initialized LightCache"); try { return self::$kv_table[$table]->set($key, [ "value" => json_encode($value, 256) diff --git a/src/ZM/Store/ZMAtomic.php b/src/ZM/Store/ZMAtomic.php index 850a465e..b4e88ff8 100644 --- a/src/ZM/Store/ZMAtomic.php +++ b/src/ZM/Store/ZMAtomic.php @@ -29,6 +29,7 @@ class ZMAtomic } self::$atomics["stop_signal"] = new Atomic(0); self::$atomics["wait_msg_id"] = new Atomic(0); + self::$atomics["_event_id"] = new Atomic(0); for ($i = 0; $i < 10; ++$i) { self::$atomics["_tmp_" . $i] = new Atomic(0); } diff --git a/src/ZM/Utils/SingletonTrait.php b/src/ZM/Utils/SingletonTrait.php new file mode 100644 index 00000000..a6649b61 --- /dev/null +++ b/src/ZM/Utils/SingletonTrait.php @@ -0,0 +1,24 @@ +connections as $v) { - server()->close($v); - } LightCache::savePersistence(); //DataProvider::saveBuffer(); Timer::clearAll(); diff --git a/src/ZM/global_functions.php b/src/ZM/global_functions.php index 605c74dd..3a4a6971 100644 --- a/src/ZM/global_functions.php +++ b/src/ZM/global_functions.php @@ -1,16 +1,20 @@ $v) { - foreach($v as $ks => $vs) { + foreach (EventManager::$events as $event => $v) { + foreach ($v as $ks => $vs) { //echo get_class($vs).": ".$vs->class." => ".$vs->method.PHP_EOL; - if($vs->class == $s["class"] && $vs->method == $s["function"]) { - $list[get_class($vs)][]=$vs; + if ($vs->class == $s["class"] && $vs->method == $s["function"]) { + $list[get_class($vs)][] = $vs; } } } @@ -227,7 +231,7 @@ function ctx() { } } -function debug($msg) { Console::debug($msg); } +function zm_debug($msg) { Console::debug($msg); } function onebot_target_id_name($message_type) { return ($message_type == "group" ? "group_id" : "user_id"); @@ -268,6 +272,22 @@ function server() { return Framework::$server; } +/** + * @return ZMRobot + * @throws RobotNotFoundException + * @throws ZMException + */ +function bot() { + if (($conn = LightCacheInside::get("connect", "conn_fd")) == -2) { + return ZMRobot::getRandom(); + } elseif ($conn != -1) { + if (($obj = ManagerGM::get($conn)) !== null) return new ZMRobot($obj); + else throw new RobotNotFoundException("单机器人连接模式可能连接了多个机器人!"); + } else { + throw new RobotNotFoundException("没有任何机器人连接到框架!"); + } +} + diff --git a/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php b/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php index d09b2d4b..3ade094b 100644 --- a/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php +++ b/test/ZMTest/PassedTest/AnnotationParserRegisterTest.php @@ -9,7 +9,7 @@ use Module\Example\Hello; use PHPUnit\Framework\TestCase; use ReflectionException; use ZM\Annotation\AnnotationParser; -use ZM\Annotation\Swoole\OnWorkerStart; +use ZM\Annotation\Swoole\OnStart; use ZM\Console\Console; class AnnotationParserRegisterTest extends TestCase @@ -34,8 +34,8 @@ class AnnotationParserRegisterTest extends TestCase public function testAnnotation() { ob_start(); $gen = $this->parser->generateAnnotationEvents(); - $m = $gen[OnWorkerStart::class][0]->method; - $class = $gen[OnWorkerStart::class][0]->class; + $m = $gen[OnStart::class][0]->method; + $class = $gen[OnStart::class][0]->class; $c = new $class(); try { $c->$m();