diff --git a/src/ZM/Annotation/AnnotationHandler.php b/src/ZM/Annotation/AnnotationHandler.php index 7b74aec3..2d07c0ba 100644 --- a/src/ZM/Annotation/AnnotationHandler.php +++ b/src/ZM/Annotation/AnnotationHandler.php @@ -6,7 +6,6 @@ namespace ZM\Annotation; use Throwable; use ZM\Exception\InterruptException; -use ZM\Middleware\MiddlewareHandler; /** * 注解调用器,原 EventDispatcher @@ -124,41 +123,30 @@ class AnnotationHandler /** * 调用单个注解 * - * @param mixed ...$params 传入的参数们 * @throws InterruptException * @throws Throwable */ - public function handle(AnnotationBase $v, ?callable $rule_callback = null, ...$params): bool + public function handle(AnnotationBase $v, ?callable $rule_callback = null): bool { // 由于3.0有额外的插件模式支持,所以注解就不再提供独立的闭包函数调用支持了 // 提取要调用的目标类和方法名称 $target_class = new ($v->class)(); $target_method = $v->method; // 先执行规则,失败就返回false - if ($rule_callback !== null && !$rule_callback($v, $params)) { + if ($rule_callback !== null && !$rule_callback($v)) { $this->status = self::STATUS_RULE_FAILED; return false; } $callback = [$target_class, $target_method]; try { - // 这块代码几乎等同于 middleware()->process() 中的内容,但由于注解调用器内含有一些特殊的特性(比如返回值回调),所以需要拆开来 - $before_result = middleware()->processBefore($callback, $params); - if ($before_result) { - // before都通过了,就执行本身,通过依赖注入执行 - // $this->return_val = container()->call($callback, $params); - $this->return_val = $callback(...$params); - } else { - // 没通过就标记是BEFORE_FAILED,然后接着执行after - $this->status = self::STATUS_BEFORE_FAILED; - } - middleware()->processAfter($callback, $params); - } /* @noinspection PhpRedundantCatchClauseInspection */ catch (InterruptException $e) { + $this->return_val = middleware()->process($callback); + } catch (InterruptException $e) { // 这里直接抛出这个异常的目的就是给上层handleAll()捕获 throw $e; } catch (Throwable $e) { // 其余的异常就交给中间件的异常捕获器过一遍,没捕获的则继续抛出 $this->status = self::STATUS_EXCEPTION; - MiddlewareHandler::getInstance()->processException($callback, $params, $e); + throw $e; } return true; } diff --git a/src/ZM/Command/Server/ServerStartCommand.php b/src/ZM/Command/Server/ServerStartCommand.php index fff41984..5f320a4d 100644 --- a/src/ZM/Command/Server/ServerStartCommand.php +++ b/src/ZM/Command/Server/ServerStartCommand.php @@ -4,10 +4,11 @@ declare(strict_types=1); namespace ZM\Command\Server; +use Exception; +use OneBot\Driver\Process\ProcessManager; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use ZM\Exception\ConfigException; use ZM\Exception\InitException; use ZM\Exception\ZMKnownException; use ZM\Framework; @@ -45,7 +46,8 @@ class ServerStartCommand extends ServerCommand /** * @throws ZMKnownException - * @throws ConfigException|InitException + * @throws InitException + * @throws Exception * @noinspection PhpComposerExtensionStubsInspection */ protected function execute(InputInterface $input, OutputInterface $output): int @@ -57,7 +59,8 @@ class ServerStartCommand extends ServerCommand return 1; } }*/ - if (\OneBot\Driver\Process\ProcessManager::isSupportedMultiProcess()) { + // 如果是支持多进程模式的,那么就检查框架进程的状态 + if (ProcessManager::isSupportedMultiProcess()) { $state = ProcessStateManager::getProcessState(ZM_PROCESS_MASTER); if (!$input->getOption('no-state-check')) { if (is_array($state) && posix_getsid($state['pid'] ?? -1) !== false) { @@ -67,6 +70,7 @@ class ServerStartCommand extends ServerCommand } } } + // 框架启动的入口 (new Framework($input->getOptions()))->init()->start(); return 0; } diff --git a/src/ZM/Middleware/MiddlewareHandler.php b/src/ZM/Middleware/MiddlewareHandler.php index b63867c6..ca7fac2a 100644 --- a/src/ZM/Middleware/MiddlewareHandler.php +++ b/src/ZM/Middleware/MiddlewareHandler.php @@ -73,54 +73,85 @@ class MiddlewareHandler $this->reg_map[$stack_id][] = [$name, $params]; } + public function getPipeClosure(callable $callback, $stack_id) + { + unset($this->stack[$stack_id]); + /** @noinspection PhpUnnecessaryLocalVariableInspection */ + $pipe_func = function (array $mid_list, ...$args) use ($callback, $stack_id, &$pipe_func) { + $return = true; + try { + while (($item = array_shift($mid_list)) !== null) { + $this->stack[$stack_id][] = $item; + // 如果是 pipeline 形式的中间件,则使用闭包回去 + if (class_exists($item[0]) && is_a($item[0], PipelineInterface::class, true)) { + return resolve($item[0])->handle(function (...$args) use ($mid_list, &$pipe_func) { + return $pipe_func($mid_list, ...$args); + }, ...$args); + } elseif (isset($this->middlewares[$item[0]]['before'])) { + $return = container()->call($this->middlewares[$item[0]]['before'], $args); + // before 没执行完,直接跳出,不执行本体 + if ($return === false) { + array_pop($this->callable_stack); + $mid_list = []; + break; + } + } + } + if ($return !== false) { + $result = container()->call($callback, $args); + } + while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) { + // 如果是 pipeline 形式的中间件,则使用闭包回去 + if (class_exists($item[0]) && is_a($item[0], PipelineInterface::class, true)) { + continue; + } + if (isset($this->middlewares[$item[0]]['after'])) { + $after_result = container()->call($this->middlewares[$item[0]]['after'], $args); + } + } + } catch (Throwable $e) { + while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) { + // 如果是 pipeline 形式的中间件,则使用闭包回去 + if (class_exists($item[0]) && is_a($item[0], PipelineInterface::class, true)) { + throw $e; + } + + foreach (($this->middlewares[$item[0]]['exception'] ?? []) as $k => $v) { + if (is_a($e, $k)) { + $exception_result = $v($e); + unset($this->stack[$stack_id]); + break 2; + } + } + } + if (!isset($exception_result)) { + throw $e; + } + } + return $result ?? $after_result ?? $exception_result ?? null; + }; + return $pipe_func; + } + /** * @throws InvalidArgumentException * @throws Throwable */ - public function process(callable $callback, array $args) + public function process(callable $callback) { - try { - $before_result = MiddlewareHandler::getInstance()->processBefore($callback, $args); - if ($before_result) { - $result = container()->call($callback, $args); - } - MiddlewareHandler::getInstance()->processAfter($callback, $args); - } catch (Throwable $e) { - MiddlewareHandler::getInstance()->processException($callback, $args, $e); - } - return $result ?? null; - } - - /** - * 调用中间件的前 - * - * @param callable $callback 必须是数组形式的动态调用 - * @param array $args 参数列表 - * @throws InvalidArgumentException - */ - public function processBefore(callable $callback, array $args): bool - { - // 压栈ID $stack_id = $this->getStackId($callback); - // 清除之前的 unset($this->stack[$stack_id]); + $this->callable_stack[] = $callback; + // 遍历执行before并压栈,并在遇到返回false后停止 try { - foreach (($this->reg_map[$stack_id] ?? []) as $item) { - $this->stack[$stack_id][] = $item; - if (isset($this->middlewares[$item[0]]['before'])) { - $return = container()->call($this->middlewares[$item[0]]['before'], $args); - if ($return === false) { - array_pop($this->callable_stack); - return false; - } - } - } + $mid_list = ($this->reg_map[$stack_id] ?? []); + $final_result = ($this->getPipeClosure($callback, $stack_id))($mid_list); } finally { array_pop($this->callable_stack); } - return true; + return $final_result ?? null; } /** @@ -133,60 +164,11 @@ class MiddlewareHandler return end($this->callable_stack); } - /** - * TODO: 调用中间件的后 - * - * @param callable $callback 必须是数组形式的动态调用 - * @param array $args 参数列表 - * @throws InvalidArgumentException - */ - public function processAfter(callable $callback, array $args) - { - // 压栈ID - $stack_id = $this->getStackId($callback); - // 从栈内倒序取出已经执行过的中间件,并执行after - $this->callable_stack[] = $callback; - try { - while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) { - if (isset($this->middlewares[$item[0]]['after'])) { - container()->call($this->middlewares[$item[0]]['after'], $args); - } - } - } finally { - array_pop($this->callable_stack); - } - } - - /** - * TODO: 调用中间件的异常捕获处理 - * - * @param callable $callback 必须是数组形式的动态调用 - * @param array $args 参数列表 - * @throws InvalidArgumentException - * @throws Throwable - */ - public function processException(callable $callback, array $args, Throwable $throwable) - { - // 压栈ID - $stack_id = $this->getStackId($callback); - // 从栈内倒序取出已经执行过的中间件,并执行after - while (isset($this->stack[$stack_id]) && ($item = array_pop($this->stack[$stack_id])) !== null) { - foreach ($this->middlewares[$item[0]]['exception'] as $k => $v) { - if (is_a($throwable, $k)) { - $v($throwable, ...$args); - unset($this->stack[$stack_id]); - return; - } - } - } - throw $throwable; - } - /** * @param callable $callback 可执行的方法 * @throws InvalidArgumentException */ - private function getStackId(callable $callback): string + public function getStackId(callable $callback): string { if ($callback instanceof Closure) { // 闭包情况下,直接根据闭包的ID号来找stack diff --git a/src/ZM/Middleware/Pipeline.php b/src/ZM/Middleware/Pipeline.php new file mode 100644 index 00000000..728315c0 --- /dev/null +++ b/src/ZM/Middleware/Pipeline.php @@ -0,0 +1,60 @@ +value = $value; + return $this; + } + + /** + * 指定要过的中间件列表 + * + * @param array $middlewares 要过的中间件(或者叫兼容管道的中间件也行)列表 + * @return $this + */ + public function through(array $middlewares): Pipeline + { + $this->middlewares = $middlewares; + return $this; + } + + /** + * 接下来要调用的内容 + * + * @param callable $callback 然后调用一个什么东西 + * @throws InvalidArgumentException + * @return null|mixed 返回调用结果或null + */ + public function then(callable $callback) + { + $stack_id = middleware()->getStackId($callback); + + // 遍历执行before并压栈,并在遇到返回false后停止 + $final_result = (middleware()->getPipeClosure($callback, $stack_id))($this->middlewares, $this->value); + + return $final_result ?? null; + } +} diff --git a/src/ZM/Middleware/PipelineInterface.php b/src/ZM/Middleware/PipelineInterface.php new file mode 100644 index 00000000..fdc82734 --- /dev/null +++ b/src/ZM/Middleware/PipelineInterface.php @@ -0,0 +1,10 @@ +registerBefore(static::class, [$this, 'onBefore']); - middleware()->registerAfter(static::class, [$this, 'onAfter']); - } - - public function onBefore(): bool - { - $this->starttime = microtime(true); - return true; - } - - public function onAfter() - { - logger()->info('Using ' . round((microtime(true) - $this->starttime) * 1000, 4) . ' ms'); + $starttime = microtime(true); + $result = $callback(...$params); + logger()->info('Pipeline using ' . round((microtime(true) - $starttime) * 1000, 4) . ' ms'); + return $result; } }