diff --git a/src/Framework/global_functions.php b/src/Framework/global_functions.php index 3738764a..5d5b5aa5 100644 --- a/src/Framework/global_functions.php +++ b/src/Framework/global_functions.php @@ -178,9 +178,10 @@ function context() { if (isset(ZMBuf::$context[$cid])) { return new $c_class($cid); } else { + Console::debug("未找到当前协程的上下文($cid),正在找父进程的上下文"); while (($pcid = Co::getPcid($cid)) !== -1) { + $cid = $pcid; if (isset(ZMBuf::$context[$cid])) return new $c_class($cid); - else $cid = $pcid; } return null; } diff --git a/src/ZM/API/CQ.php b/src/ZM/API/CQ.php index bf959f0b..d8e172d4 100644 --- a/src/ZM/API/CQ.php +++ b/src/ZM/API/CQ.php @@ -216,9 +216,26 @@ class CQ } public static function removeCQ($msg) { - while (($cq = ZMUtil::getCQ($msg)) !== null) { + while (($cq = self::getCQ($msg)) !== null) { $msg = str_replace(mb_substr($msg, $cq["start"], $cq["end"] - $cq["start"] + 1), "", $msg); } return $msg; } + + public static function getCQ($msg) { + if (($start = mb_strpos($msg, '[')) === false) return null; + if (($end = mb_strpos($msg, ']')) === false) return null; + $msg = mb_substr($msg, $start + 1, $end - $start - 1); + if (mb_substr($msg, 0, 3) != "CQ:") return null; + $msg = mb_substr($msg, 3); + $msg2 = explode(",", $msg); + $type = array_shift($msg2); + $array = []; + foreach ($msg2 as $k => $v) { + $ss = explode("=", $v); + $sk = array_shift($ss); + $array[$sk] = implode("=", $ss); + } + return ["type" => $type, "params" => $array, "start" => $start, "end" => $end]; + } } diff --git a/src/ZM/API/CQAPI.php b/src/ZM/API/CQAPI.php index 864771dc..37bb9648 100644 --- a/src/ZM/API/CQAPI.php +++ b/src/ZM/API/CQAPI.php @@ -9,7 +9,7 @@ use Framework\Console; use Framework\ZMBuf; use ZM\Connection\ConnectionManager; use ZM\Connection\CQConnection; -use ZM\Connection\WSConnection; +use ZM\Event\EventHandler; use ZM\Utils\ZMRobot; /** @@ -198,7 +198,7 @@ class CQAPI } /** - * @param WSConnection $connection + * @param CQConnection $connection * @param $reply * @param |null $function * @return bool @@ -207,7 +207,7 @@ class CQAPI $api_id = ZMBuf::$atomics["wait_msg_id"]->get(); $reply["echo"] = $api_id; ZMBuf::$atomics["wait_msg_id"]->add(1); - + EventHandler::callCQAPISend($reply, $connection); if (is_callable($function)) { ZMBuf::appendKey("sent_api", $api_id, [ "data" => $reply, diff --git a/src/ZM/Annotation/AnnotationParser.php b/src/ZM/Annotation/AnnotationParser.php index c9bb3487..9db1e7e1 100644 --- a/src/ZM/Annotation/AnnotationParser.php +++ b/src/ZM/Annotation/AnnotationParser.php @@ -8,7 +8,15 @@ use Framework\{Console, ZMBuf}; use ReflectionClass; use ReflectionException; use ReflectionMethod; -use ZM\Annotation\CQ\{CQAfter, CQBefore, CQCommand, CQMessage, CQMetaEvent, CQNotice, CQRequest}; +use ZM\Annotation\CQ\{CQAfter, + CQAPIResponse, + CQAPISend, + CQBefore, + CQCommand, + CQMessage, + CQMetaEvent, + CQNotice, + CQRequest}; use ZM\Annotation\Http\{After, Before, Controller, HandleException, Middleware, MiddlewareClass, RequestMapping}; use Swoole\Timer; use ZM\Annotation\Interfaces\CustomAnnotation; @@ -38,7 +46,7 @@ class AnnotationParser ]; $reader = new AnnotationReader(); foreach ($all_class as $v) { - Console::debug("正在检索 ".$v); + Console::debug("正在检索 " . $v); $reflection_class = new ReflectionClass($v); $class_prefix = ''; $methods = $reflection_class->getMethods(ReflectionMethod::IS_PUBLIC); @@ -86,6 +94,10 @@ class AnnotationParser } } foreach ($methods as $vs) { + if ($middleware_addon !== null) { + Console::debug("Added middleware ".$middleware_addon->middleware . " to $v -> ".$vs->getName()); + ZMBuf::$events[MiddlewareInterface::class][$v][$vs->getName()][] = $middleware_addon->middleware; + } $method_annotations = $reader->getMethodAnnotations($vs); foreach ($method_annotations as $vss) { if ($vss instanceof Rule) $vss = self::registerRuleEvent($vss, $vs, $reflection_class); @@ -100,14 +112,14 @@ class AnnotationParser elseif ($vss instanceof CQCommand) ZMBuf::$events[CQCommand::class][] = $vss; elseif ($vss instanceof RequestMapping) { self::registerRequestMapping($vss, $vs, $reflection_class, $class_prefix); - if ($middleware_addon !== null) - ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method] = $middleware_addon->middleware; } elseif ($vss instanceof CustomAnnotation) ZMBuf::$events[get_class($vss)][] = $vss; elseif ($vss instanceof CQBefore) ZMBuf::$events[CQBefore::class][$vss->cq_event][] = $vss; elseif ($vss instanceof CQAfter) ZMBuf::$events[CQAfter::class][$vss->cq_event][] = $vss; elseif ($vss instanceof OnStart) ZMBuf::$events[OnStart::class][] = $vss; - elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method] = $vss->middleware; + elseif ($vss instanceof Middleware) ZMBuf::$events[MiddlewareInterface::class][$vss->class][$vss->method][] = $vss->middleware; elseif ($vss instanceof OnTick) self::addTimerTick($vss); + elseif ($vss instanceof CQAPISend) ZMBuf::$events[CQAPISend::class][] = $vss; + elseif ($vss instanceof CQAPIResponse) ZMBuf::$events[CQAPIResponse::class][$vss->retcode] = [$vss->class, $vss->method]; } } } diff --git a/src/ZM/Annotation/CQ/CQAPIResponse.php b/src/ZM/Annotation/CQ/CQAPIResponse.php new file mode 100644 index 00000000..67001d66 --- /dev/null +++ b/src/ZM/Annotation/CQ/CQAPIResponse.php @@ -0,0 +1,24 @@ +level; + } + + /** + * @param mixed $level + */ + public function setLevel($level) { + $this->level = $level; + } +} diff --git a/src/ZM/Annotation/CQ/CQAfter.php b/src/ZM/Annotation/CQ/CQAfter.php index 3bfd3c35..75bfbbfe 100644 --- a/src/ZM/Annotation/CQ/CQAfter.php +++ b/src/ZM/Annotation/CQ/CQAfter.php @@ -5,6 +5,7 @@ namespace ZM\Annotation\CQ; use Doctrine\Common\Annotations\Annotation\Target; use ZM\Annotation\AnnotationBase; +use ZM\Annotation\Interfaces\Level; /** * Class CQAfter @@ -12,11 +13,27 @@ use ZM\Annotation\AnnotationBase; * @Target("METHOD") * @package ZM\Annotation\CQ */ -class CQAfter extends AnnotationBase +class CQAfter extends AnnotationBase implements Level { /** * @var string * @Required() */ public $cq_event; -} \ No newline at end of file + + public $level = 20; + + /** + * @return mixed + */ + public function getLevel() { + return $this->level; + } + + /** + * @param mixed $level + */ + public function setLevel($level) { + $this->level = $level; + } +} diff --git a/src/ZM/Event/CQ/MessageEvent.php b/src/ZM/Event/CQ/MessageEvent.php index 99f24d65..9fce23e6 100644 --- a/src/ZM/Event/CQ/MessageEvent.php +++ b/src/ZM/Event/CQ/MessageEvent.php @@ -5,6 +5,7 @@ namespace ZM\Event\CQ; use Co; +use Doctrine\Common\Annotations\AnnotationException; use Framework\Console; use Framework\ZMBuf; use ZM\Annotation\CQ\CQAfter; @@ -12,6 +13,7 @@ use ZM\Annotation\CQ\CQBefore; use ZM\Annotation\CQ\CQCommand; use ZM\Annotation\CQ\CQMessage; use ZM\Connection\WSConnection; +use ZM\Event\EventHandler; use ZM\Exception\WaitTimeoutException; use ZM\Http\Response; use ZM\ModBase; @@ -31,16 +33,23 @@ class MessageEvent $this->circle = $circle; } + /** + * @return bool + * @throws AnnotationException + */ public function onBefore() { foreach (ZMBuf::$events[CQBefore::class]["message"] ?? [] as $v) { $c = $v->class; - $class = new $c([ - "data" => context()->getData(), - "connection" => $this->connection - ], ModHandleType::CQ_MESSAGE); - Console::debug("Calling CQBefore: " . $c . " -> " . $v->method); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } foreach (ZMBuf::get("wait_api", []) as $k => $v) { if (context()->getData()["user_id"] == $v["user_id"] && @@ -57,7 +66,9 @@ class MessageEvent return true; } - /** @noinspection PhpRedundantCatchClauseInspection */ + /** + * @throws AnnotationException + */ public function onActivate() { try { $word = split_explode(" ", str_replace("\r", "", context()->getMessage())); @@ -76,21 +87,25 @@ class MessageEvent if ($v->match == "" && $v->regexMatch == "") continue; else { $c = $v->class; - if (!isset($obj[$c])) - $obj[$c] = new $c([ - "data" => context()->getData(), - "connection" => context()->getConnection() - ], ModHandleType::CQ_MESSAGE); + $class_construct = [ + "data" => context()->getData(), + "connection" => context()->getConnection() + ]; + if(!isset($obj[$c])) { + $obj[$c] = new $c($class_construct); + } if ($word[0] != "" && $v->match == $word[0]) { Console::debug("Calling $c -> {$v->method}"); - $r = call_user_func([$obj[$c], $v->method], $word); - if (is_string($r)) $obj[$c]->reply($r); - $this->function_call = true; + $this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$word], function ($r) { + if (is_string($r)) context()->reply($r); + return true; + }); return; } elseif ($v->regexMatch != "" && ($args = matchArgs($v->regexMatch, context()->getMessage())) !== false) { - $r = call_user_func([$obj[$c], $v->method], $args); - if (is_string($r)) $obj[$c]->reply($r); - $this->function_call = true; + $this->function_call = EventHandler::callWithMiddleware($obj[$c], $v->method, $class_construct, [$args], function ($r){ + if (is_string($r)) context()->reply($r); + return true; + }); return; } } @@ -110,29 +125,35 @@ class MessageEvent "data" => context()->getData(), "connection" => $this->connection ], ModHandleType::CQ_MESSAGE); - $r = call_user_func([$obj[$c], $v->method], context()->getData()["message"]); - if (is_string($r)) $obj[$c]->reply($r); + EventHandler::callWithMiddleware($obj[$c], $v->method, [], [context()->getData()["message"]], function($r) { + if(is_string($r)) context()->reply($r); + }); if (context()->getCache("block_continue") === true) return; } } } catch (WaitTimeoutException $e) { - $e->module->finalReply($e->getMessage()); } } /** * 在调用完事件后执行的 + * @throws AnnotationException */ public function onAfter() { + context()->setCache("block_continue", null); foreach (ZMBuf::$events[CQAfter::class]["message"] ?? [] as $v) { $c = $v->class; - $class = new $c([ - "data" => context()->getData(), - "connection" => $this->connection - ], ModHandleType::CQ_MESSAGE); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } return true; } diff --git a/src/ZM/Event/CQ/MetaEvent.php b/src/ZM/Event/CQ/MetaEvent.php index 7111f921..bd418917 100644 --- a/src/ZM/Event/CQ/MetaEvent.php +++ b/src/ZM/Event/CQ/MetaEvent.php @@ -4,11 +4,12 @@ namespace ZM\Event\CQ; +use Doctrine\Common\Annotations\AnnotationException; use Framework\ZMBuf; use ZM\Annotation\CQ\CQBefore; use ZM\Annotation\CQ\CQMetaEvent; -use ZM\Connection\ConnectionManager; use ZM\Connection\CQConnection; +use ZM\Event\EventHandler; use ZM\Exception\WaitTimeoutException; use ZM\ModBase; use ZM\ModHandleType; @@ -26,21 +27,30 @@ class MetaEvent $this->circle = $circle; } + /** + * @return bool + * @throws AnnotationException + */ public function onBefore() { foreach (ZMBuf::$events[CQBefore::class]["meta_event"] ?? [] as $v) { $c = $v->class; - /** @var CQMetaEvent $v */ - $class = new $c([ - "data" => $this->data, - "connection" => $this->connection - ], ModHandleType::CQ_META_EVENT); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } return true; } - /** @noinspection PhpRedundantCatchClauseInspection */ + /** + * @throws AnnotationException + */ public function onActivate() { try { /** @var ModBase[] $obj */ @@ -56,8 +66,9 @@ class MetaEvent "data" => $this->data, "connection" => $this->connection ], ModHandleType::CQ_META_EVENT); - $r = call_user_func([$obj[$c], $v->method]); - if (is_string($r)) $obj[$c]->reply($r); + EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) { + if (is_string($r)) context()->reply($r); + }); if (context()->getCache("block_continue") === true) return; } } diff --git a/src/ZM/Event/CQ/NoticeEvent.php b/src/ZM/Event/CQ/NoticeEvent.php index 6b5130be..0973f826 100644 --- a/src/ZM/Event/CQ/NoticeEvent.php +++ b/src/ZM/Event/CQ/NoticeEvent.php @@ -4,11 +4,13 @@ namespace ZM\Event\CQ; +use Doctrine\Common\Annotations\AnnotationException; use Framework\ZMBuf; use ZM\Annotation\CQ\CQAfter; use ZM\Annotation\CQ\CQBefore; use ZM\Annotation\CQ\CQNotice; use ZM\Connection\CQConnection; +use ZM\Event\EventHandler; use ZM\Exception\WaitTimeoutException; use ZM\ModBase; use ZM\ModHandleType; @@ -26,20 +28,30 @@ class NoticeEvent $this->circle = $circle; } + /** + * @return bool + * @throws AnnotationException + */ public function onBefore() { foreach (ZMBuf::$events[CQBefore::class]["notice"] ?? [] as $v) { $c = $v->class; - /** @var CQNotice $v */ - $class = new $c([ - "data" => $this->data, - "connection" => $this->connection - ], ModHandleType::CQ_NOTICE); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } return true; } + /** + * @throws AnnotationException + */ public function onActivate() { try { /** @var ModBase[] $obj */ @@ -57,8 +69,9 @@ class NoticeEvent "data" => $this->data, "connection" => $this->connection ], ModHandleType::CQ_NOTICE); - $r = call_user_func([$obj[$c], $v->method]); - if (is_string($r)) $obj[$c]->reply($r); + EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) { + if (is_string($r)) context()->reply($r); + }); if (context()->getCache("block_continue") === true) return; } } @@ -67,15 +80,23 @@ class NoticeEvent } } + /** + * @return bool + * @throws AnnotationException + */ public function onAfter() { foreach (ZMBuf::$events[CQAfter::class]["notice"] ?? [] as $v) { $c = $v->class; - $class = new $c([ - "data" => $this->data, - "connection" => $this->connection - ], ModHandleType::CQ_NOTICE); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } return true; } diff --git a/src/ZM/Event/CQ/RequestEvent.php b/src/ZM/Event/CQ/RequestEvent.php index e10371aa..84f9cf7f 100644 --- a/src/ZM/Event/CQ/RequestEvent.php +++ b/src/ZM/Event/CQ/RequestEvent.php @@ -4,11 +4,13 @@ namespace ZM\Event\CQ; +use Doctrine\Common\Annotations\AnnotationException; use Framework\ZMBuf; use ZM\Annotation\CQ\CQAfter; use ZM\Annotation\CQ\CQBefore; use ZM\Annotation\CQ\CQRequest; use ZM\Connection\CQConnection; +use ZM\Event\EventHandler; use ZM\Exception\WaitTimeoutException; use ZM\ModBase; use ZM\ModHandleType; @@ -26,21 +28,30 @@ class RequestEvent $this->circle = $circle; } + /** + * @return bool + * @throws AnnotationException + */ public function onBefore() { foreach (ZMBuf::$events[CQBefore::class]["request"] ?? [] as $v) { $c = $v->class; - /** @var CQRequest $v */ - $class = new $c([ - "data" => $this->data, - "connection" => $this->connection - ], ModHandleType::CQ_REQUEST); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } return true; } - /** @noinspection PhpRedundantCatchClauseInspection */ + /** + * @throws AnnotationException + */ public function onActivate() { try { /** @var ModBase[] $obj */ @@ -58,8 +69,9 @@ class RequestEvent "data" => $this->data, "connection" => $this->connection ], ModHandleType::CQ_REQUEST); - $r = call_user_func([$obj[$c], $v->method]); - if (is_string($r)) $obj[$c]->reply($r); + EventHandler::callWithMiddleware($obj[$c],$v->method, [], [], function($r) { + if (is_string($r)) context()->reply($r); + }); if (context()->getCache("block_continue") === true) return; } } @@ -68,15 +80,23 @@ class RequestEvent } } + /** + * @return bool + * @throws AnnotationException + */ public function onAfter() { foreach (ZMBuf::$events[CQAfter::class]["request"] ?? [] as $v) { $c = $v->class; - $class = new $c([ - "data" => $this->data, - "connection" => $this->connection - ], ModHandleType::CQ_REQUEST); - $r = call_user_func_array([$class, $v->method], []); - if (!$r || $class->block_continue) return false; + EventHandler::callWithMiddleware( + $c, + $v->method, + ["data" => context()->getData(), "connection" => $this->connection], + [], + function ($r) { + if(!$r) context()->setCache("block_continue", true); + } + ); + if(context()->getCache("block_continue") === true) return false; } return true; } diff --git a/src/ZM/Event/EventHandler.php b/src/ZM/Event/EventHandler.php index 5dbed1b0..7e2d978c 100644 --- a/src/ZM/Event/EventHandler.php +++ b/src/ZM/Event/EventHandler.php @@ -5,6 +5,7 @@ namespace ZM\Event; use Co; +use Doctrine\Common\Annotations\AnnotationException; use Error; use Exception; use Framework\Console; @@ -12,13 +13,24 @@ use Framework\ZMBuf; use ZM\Event\Swoole\{MessageEvent, RequestEvent, WorkerStartEvent, WSCloseEvent, WSOpenEvent}; use Swoole\Server; use Swoole\WebSocket\Frame; +use ZM\Annotation\CQ\CQAPIResponse; +use ZM\Annotation\CQ\CQAPISend; +use ZM\Annotation\Http\MiddlewareClass; use ZM\Connection\ConnectionManager; +use ZM\Connection\CQConnection; +use ZM\Http\MiddlewareInterface; use ZM\Http\Response; use Framework\DataProvider; use ZM\Utils\ZMUtil; class EventHandler { + /** + * @param $event_name + * @param $param0 + * @param null $param1 + * @throws AnnotationException + */ public static function callSwooleEvent($event_name, $param0, $param1 = null) { //$starttime = microtime(true); unset(ZMBuf::$context[Co::getCid()]); @@ -80,6 +92,13 @@ class EventHandler //Console::info(Console::setColor("Event: " . $event_name . " 运行了 " . round(microtime(true) - $starttime, 5) . " 秒", "gold")); } + /** + * @param $event_data + * @param $conn_or_response + * @param int $level + * @return bool + * @throws AnnotationException + */ public static function callCQEvent($event_data, $conn_or_response, int $level = 0) { if ($level >= 5) { Console::warning("Recursive call reached " . $level . " times"); @@ -115,10 +134,10 @@ class EventHandler public static function callCQResponse($req) { //Console::info("收到来自API连接的回复:".json_encode($req, 128|256)); + $status = $req["status"]; + $retcode = $req["retcode"]; + $data = $req["data"]; if (isset($req["echo"]) && ZMBuf::array_key_exists("sent_api", $req["echo"])) { - $status = $req["status"]; - $retcode = $req["retcode"]; - $data = $req["data"]; $origin = ZMBuf::get("sent_api")[$req["echo"]]; $self_id = $origin["self_id"]; $response = [ @@ -127,6 +146,11 @@ class EventHandler "data" => $data, "self_id" => $self_id ]; + if (isset(ZMBuf::$events[CQAPIResponse::class][$req["retcode"]])) { + list($c, $method) = ZMBuf::$events[CQAPIResponse::class][$req["retcode"]]; + $class = new $c(["data" => $origin["data"]]); + call_user_func_array([$class, $method], [$origin["data"], $req]); + } if (($origin["func"] ?? null) !== null) { call_user_func($origin["func"], $response, $origin["data"]); } elseif (($origin["coroutine"] ?? false) !== false) { @@ -138,4 +162,91 @@ class EventHandler ZMBuf::unsetByValue("sent_api", $req["echo"]); } } + + public static function callCQAPISend($reply, ?CQConnection $connection) { + $action = $reply["action"] ?? null; + if ($action === null) { + Console::warning("API 激活事件异常!"); + return; + } + $content = ctx()->copy(); + go(function () use ($action, $reply, $connection, $content) { + set_coroutine_params($content); + context()->setCache("action", $action); + context()->setCache("reply", $reply); + foreach (ZMBuf::$events[CQAPISend::class] ?? [] as $k => $v) { + if ($v->action == "" || $v->action == $action) { + $c = $v->class; + self::callWithMiddleware($c, $v->method, context()->copy(), [$reply["action"], $reply["params"] ?? [], $connection->getQQ()]); + if (context()->getCache("block_continue") === true) break; + } + } + }); + } + + /** + * @param $c + * @param $method + * @param array $class_construct + * @param array $func_args + * @param null $after_call + * @return mixed|null + * @throws AnnotationException + * @throws Exception + */ + public static function callWithMiddleware($c, $method, array $class_construct, array $func_args, $after_call = null) { + $return_value = null; + $plain_class = is_object($c) ? get_class($c) : $c; + if (isset(ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method])) { + $middlewares = ZMBuf::$events[MiddlewareInterface::class][$plain_class][$method]; + $before_result = true; + $r = []; + foreach ($middlewares as $k => $middleware) { + if (!isset(ZMBuf::$events[MiddlewareClass::class][$middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware}\"!"); + $middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middleware]; + $before = $middleware_obj["class"]; + $r[$k] = new $before(); + $r[$k]->class = is_object($c) ? get_class($c) : $c; + $r[$k]->method = $method; + if (isset($middleware_obj["before"])) { + $before_result = call_user_func_array([$r[$k], $middleware_obj["before"]], $func_args); + if ($before_result === false) break; + } + } + if ($before_result) { + try { + if (is_object($c)) $class = $c; + else $class = new $c($class_construct); + $result = call_user_func_array([$class, $method], $func_args); + if (is_callable($after_call)) + $return_value = call_user_func_array($after_call, [$result]); + } catch (Exception $e) { + for ($i = count($middlewares) - 1; $i >= 0; --$i) { + $middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]]; + if (!isset($middleware_obj["exceptions"])) continue; + foreach ($middleware_obj["exceptions"] as $name => $method) { + if ($e instanceof $name) { + call_user_func_array([$r[$i], $method], [$e]); + context()->setCache("block_continue", true); + } + } + if (context()->getCache("block_continue") === true) return $return_value; + } + throw $e; + } + } + for ($i = count($middlewares) - 1; $i >= 0; --$i) { + $middleware_obj = ZMBuf::$events[MiddlewareClass::class][$middlewares[$i]]; + if (isset($middleware_obj["after"], $r[$i])) + call_user_func_array([$r[$i], $middleware_obj["after"]], $func_args); + } + } else { + if (is_object($c)) $class = $c; + else $class = new $c($class_construct); + $result = call_user_func_array([$class, $method], $func_args); + if (is_callable($after_call)) + $return_value = call_user_func_array($after_call, [$result]); + } + return $return_value; + } } diff --git a/src/ZM/Event/Swoole/MessageEvent.php b/src/ZM/Event/Swoole/MessageEvent.php index 0d678472..17461f85 100644 --- a/src/ZM/Event/Swoole/MessageEvent.php +++ b/src/ZM/Event/Swoole/MessageEvent.php @@ -53,9 +53,12 @@ class MessageEvent implements SwooleEvent foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) { if (strtolower($v->type) == "message" && $this->parseSwooleRule($v)) { $c = $v->class; - /** @var ModBase $class */ - $class = new $c(["server" => $this->server, "frame" => $this->frame, "connection" => $conn], ModHandleType::SWOOLE_MESSAGE); - call_user_func_array([$class, $v->method], [$conn]); + EventHandler::callWithMiddleware( + $c, + $v->method, + ["server" => $this->server, "frame" => $this->frame, "connection" => $conn], + [$conn] + ); if (context()->getCache("block_continue") === true) break; } } diff --git a/src/ZM/Event/Swoole/RequestEvent.php b/src/ZM/Event/Swoole/RequestEvent.php index 2facccd0..64e50b6f 100644 --- a/src/ZM/Event/Swoole/RequestEvent.php +++ b/src/ZM/Event/Swoole/RequestEvent.php @@ -5,18 +5,14 @@ namespace ZM\Event\Swoole; use Closure; -use Doctrine\Common\Annotations\AnnotationException; use Exception; use Framework\Console; use Framework\ZMBuf; use Swoole\Http\Request; -use ZM\Annotation\Http\MiddlewareClass; use ZM\Annotation\Swoole\SwooleEventAfter; use ZM\Annotation\Swoole\SwooleEventAt; -use ZM\Http\MiddlewareInterface; +use ZM\Event\EventHandler; use ZM\Http\Response; -use ZM\ModBase; -use ZM\ModHandleType; use Framework\DataProvider; use ZM\Utils\ZMUtil; @@ -120,57 +116,25 @@ class RequestEvent implements SwooleEvent return $this; } - context()->setCache("params", $params); + context()->setCache("params", $params); if (in_array(strtoupper($this->request->server["request_method"]), $node["request_method"] ?? [])) { //判断目标方法在不在里面 $c_name = $node["class"]; - if (isset(ZMBuf::$events[MiddlewareInterface::class][$c_name][$node["method"]])) { - $middleware = ZMBuf::$events[MiddlewareInterface::class][$c_name][$node["method"]]; - if (!isset(ZMBuf::$events[MiddlewareClass::class][$middleware])) throw new AnnotationException("Annotation parse error: Unknown MiddlewareClass named \"{$middleware}\"!"); - $middleware = ZMBuf::$events[MiddlewareClass::class][$middleware]; - $before = $middleware["class"]; - $r = new $before(); - $before_result = true; - if (isset($middleware["before"])) { - $before_result = call_user_func([$r, $middleware["before"]], $this->request, $this->response, $params); - if ($before_result !== false) $before_result = true; + EventHandler::callWithMiddleware( + $c_name, + $node["method"], + ["request" => $this->request, "response" => &$this->response, "params" => $params], + [$params], + function ($result) { + if (is_string($result) && !$this->response->isEnd()) $this->response->end($result); + if ($this->response->isEnd()) context()->setCache("block_continue", true); } - if ($before_result) { - try { - /** @var ModBase $class */ - $class = new $c_name(["request" => $this->request, "response" => $this->response, "params" => $params], ModHandleType::SWOOLE_REQUEST); - $result = call_user_func_array([$class, $node["method"]], [$params]); - if (is_string($result) && !$this->response->isEnd()) $this->response->end($result); - if (!$this->response->isEnd()) goto eventCall; - } catch (Exception $e) { - if (!isset($middleware["exceptions"])) throw $e; - foreach ($middleware["exceptions"] as $name => $method) { - if ($e instanceof $name) { - call_user_func([$r, $method], $e, $this->request, $this->response, $params); - return $this; - } - } - throw $e; - } - } - if (isset($middleware["after"])) { - call_user_func([$r, $middleware["after"]], $this->request, $this->response, $params); - return $this; - } - } else { - /** @var ModBase $class */ - $class = new $c_name(["request" => $this->request, "response" => $this->response, "params" => $params], ModHandleType::SWOOLE_REQUEST); - $r = call_user_func_array([$class, $node["method"]], [$params]); - if (is_string($r) && !$this->response->isEnd()) $this->response->end($r); - if (!$this->response->isEnd()) goto eventCall; - } + ); } - eventCall: foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) { if (strtolower($v->type) == "request" && $this->parseSwooleRule($v)) { $c = $v->class; - $class = new $c(["request" => $this->request, "response" => $this->response]); - $r = call_user_func_array([$class, $v->method], []); + EventHandler::callWithMiddleware($c, $v->method, ["request" => $this->request, "response" => $this->response], []); if (context()->getCache("block_continue") === true) break; } } diff --git a/src/ZM/Event/Swoole/WSCloseEvent.php b/src/ZM/Event/Swoole/WSCloseEvent.php index 058181a8..6c94ff06 100644 --- a/src/ZM/Event/Swoole/WSCloseEvent.php +++ b/src/ZM/Event/Swoole/WSCloseEvent.php @@ -4,11 +4,13 @@ namespace ZM\Event\Swoole; +use Doctrine\Common\Annotations\AnnotationException; use Framework\ZMBuf; use Swoole\Server; use ZM\Annotation\Swoole\SwooleEventAfter; use ZM\Annotation\Swoole\SwooleEventAt; use ZM\Connection\ConnectionManager; +use ZM\Event\EventHandler; use ZM\ModBase; use ZM\ModHandleType; use ZM\Utils\ZMUtil; @@ -26,6 +28,7 @@ class WSCloseEvent implements SwooleEvent /** * @inheritDoc + * @throws AnnotationException */ public function onActivate() { ZMUtil::checkWait(); @@ -34,9 +37,8 @@ class WSCloseEvent implements SwooleEvent foreach(ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) { if(strtolower($v->type) == "close" && $this->parseSwooleRule($v)) { $c = $v->class; - $class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE); - call_user_func_array([$class, $v->method], []); - if($class->block_continue) break; + EventHandler::callWithMiddleware($c, $v->method, ["server" => $this->server, "fd" => $this->fd], []); + if(context()->getCache("block_continue") === true) break; } } return $this; @@ -44,14 +46,13 @@ class WSCloseEvent implements SwooleEvent /** * @inheritDoc + * @throws AnnotationException */ public function onAfter() { foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) { if (strtolower($v->type) == "close" && $this->parseSwooleRule($v) === true) { $c = $v->class; - /** @var ModBase $class */ - $class = new $c(["server" => $this->server, "fd" => $this->fd], ModHandleType::SWOOLE_CLOSE); - call_user_func_array([$class, $v->method], []); + EventHandler::callWithMiddleware($c, $v->method, ["server" => $this->server, "fd" => $this->fd], []); if(context()->getCache("block_continue") === true) break; } } diff --git a/src/ZM/Event/Swoole/WSOpenEvent.php b/src/ZM/Event/Swoole/WSOpenEvent.php index 3b8e92b9..5038767b 100644 --- a/src/ZM/Event/Swoole/WSOpenEvent.php +++ b/src/ZM/Event/Swoole/WSOpenEvent.php @@ -5,6 +5,7 @@ namespace ZM\Event\Swoole; use Closure; +use Doctrine\Common\Annotations\AnnotationException; use Framework\ZMBuf; use Swoole\Http\Request; use Swoole\WebSocket\Server; @@ -14,6 +15,7 @@ use ZM\Connection\ConnectionManager; use ZM\Connection\CQConnection; use ZM\Connection\UnknownConnection; use ZM\Connection\WSConnection; +use ZM\Event\EventHandler; use ZM\ModBase; use ZM\ModHandleType; use ZM\Utils\ZMUtil; @@ -40,6 +42,7 @@ class WSOpenEvent implements SwooleEvent /** * @inheritDoc + * @throws AnnotationException */ public function onActivate() { ZMUtil::checkWait(); @@ -59,8 +62,12 @@ class WSOpenEvent implements SwooleEvent foreach (ZMBuf::$events[SwooleEventAt::class] ?? [] as $v) { if (strtolower($v->type) == "open" && $this->parseSwooleRule($v) === true) { $c = $v->class; - $class = new $c(["server" => $this->server, "request" => $this->request, "connection" => $this->conn], ModHandleType::SWOOLE_OPEN); - call_user_func_array([$class, $v->method], [$this->conn]); + EventHandler::callWithMiddleware( + $c, + $v->method, + ["server" => $this->server, "request" => $this->request, "connection" => $this->conn], + [$this->conn] + ); if (context()->getCache("block_continue") === true) break; } } diff --git a/src/ZM/Event/Swoole/WorkerStartEvent.php b/src/ZM/Event/Swoole/WorkerStartEvent.php index 0184a129..c929d835 100644 --- a/src/ZM/Event/Swoole/WorkerStartEvent.php +++ b/src/ZM/Event/Swoole/WorkerStartEvent.php @@ -22,9 +22,8 @@ use Framework\Console; use Framework\GlobalConfig; use Framework\ZMBuf; use Swoole\Server; +use ZM\Event\EventHandler; use ZM\Exception\DbException; -use ZM\ModBase; -use ZM\ModHandleType; use Framework\DataProvider; use ZM\Utils\SQLPool; use ZM\Utils\ZMUtil; @@ -90,25 +89,25 @@ class WorkerStartEvent implements SwooleEvent return $this; } + /** + * @return WorkerStartEvent + * @throws AnnotationException + */ public function onAfter(): WorkerStartEvent { foreach (ZMBuf::get("wait_start") as $v) { Coroutine::resume($v); } ZMBuf::unsetCache("wait_start"); + set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]); foreach (ZMBuf::$events[OnStart::class] ?? [] as $v) { $class_name = $v->class; - /** @var ModBase $class */ - $class = new $class_name(["server" => $this->server, "worker_id" => $this->worker_id], ModHandleType::SWOOLE_WORKER_START); - call_user_func_array([$class, $v->method], []); + EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []); } - set_coroutine_params(["server" => $this->server, "worker_id" => $this->worker_id]); foreach (ZMBuf::$events[SwooleEventAfter::class] ?? [] as $v) { /** @var AnnotationBase $v */ if (strtolower($v->type) == "workerstart") { $class_name = $v->class; - /** @var ModBase $class */ - $class = new $class_name(["server" => $this->server, "worker_id" => $this->worker_id], ModHandleType::SWOOLE_WORKER_START); - call_user_func_array([$class, $v->method], []); + EventHandler::callWithMiddleware($class_name, $v->method, ["server" => $this->server, "worker_id" => $this->worker_id], []); if (context()->getCache("block_continue") === true) break; } } diff --git a/src/ZM/Utils/ZMRobot.php b/src/ZM/Utils/ZMRobot.php index a6fd46b6..13184072 100644 --- a/src/ZM/Utils/ZMRobot.php +++ b/src/ZM/Utils/ZMRobot.php @@ -407,6 +407,6 @@ class ZMRobot private function getActionName(string $method) { $prefix = ($this->prefix == self::API_ASYNC ? '_async' : ($this->prefix == self::API_RATE_LIMITED ? '_rate_limited' : '')); $func_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $method)); - return $prefix . $func_name; + return $func_name . $prefix; } } diff --git a/src/ZM/Utils/ZMUtil.php b/src/ZM/Utils/ZMUtil.php index e641f346..0520aa9e 100644 --- a/src/ZM/Utils/ZMUtil.php +++ b/src/ZM/Utils/ZMUtil.php @@ -8,6 +8,7 @@ use Co; use framework\Console; use Framework\DataProvider; use Framework\ZMBuf; +use ZM\API\CQ; class ZMUtil { @@ -40,7 +41,7 @@ class ZMUtil public static function reload() { Console::info(Console::setColor("Reloading server...", "gold")); - foreach (ZMBuf::get("wait_api") as $k => $v) { + foreach (ZMBuf::get("wait_api", []) as $k => $v) { if ($v["result"] === null) Co::resume($v["coroutine"]); } foreach (ZMBuf::$server->connections as $v) { @@ -54,28 +55,14 @@ class ZMUtil * 解析CQ码 * @param $msg * @return array|null - * 0123456 - * [CQ:at] */ static function getCQ($msg) { - if (($start = mb_strpos($msg, '[')) === false) return null; - if (($end = mb_strpos($msg, ']')) === false) return null; - $msg = mb_substr($msg, $start + 1, $end - $start - 1); - if (mb_substr($msg, 0, 3) != "CQ:") return null; - $msg = mb_substr($msg, 3); - $msg2 = explode(",", $msg); - $type = array_shift($msg2); - $array = []; - foreach ($msg2 as $k => $v) { - $ss = explode("=", $v); - $sk = array_shift($ss); - $array[$sk] = implode("=", $ss); - } - return ["type" => $type, "params" => $array, "start" => $start, "end" => $end]; + return CQ::getCQ($msg); } public static function getModInstance($class) { if (!isset(ZMBuf::$instance[$class])) { + Console::debug("Class instance $class not exist, so I created it."); ZMBuf::$instance[$class] = new $class(); return ZMBuf::$instance[$class]; } else {