diff --git a/composer.json b/composer.json index 8022fd5..6fbdf06 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ ], "license": "MIT", "require": { - "php": ">=8.4", + "php": ">=8.5", "composer-runtime-api": "^2.0", "psr/http-server-middleware": "^1.0", "psr/http-message": "^1.0" diff --git a/src/Handler.php b/src/Handler.php index fe12a60..3cb12f7 100644 --- a/src/Handler.php +++ b/src/Handler.php @@ -5,6 +5,7 @@ namespace Kiri\Router; use Closure; use Kiri; +use Kiri\Router\Annotate\Defer; use Kiri\Router\Format\IFormat; use Kiri\Router\Format\MixedFormat; use Kiri\Router\Format\NoBody; @@ -32,6 +33,11 @@ class Handler implements RequestHandlerInterface */ protected array $middlewares = []; + /** + * @var Defer[] + */ + protected array $deferred = []; + protected ?string $sourceFile = null; protected string $sourceKind = 'attribute'; @@ -152,6 +158,25 @@ class Handler implements RequestHandlerInterface } + /** + * @param Defer[] $deferred + * @return void + */ + public function setDeferred(array $deferred): void + { + $this->deferred = $deferred; + } + + + /** + * @return Defer[] + */ + public function getDeferred(): array + { + return $this->deferred; + } + + /** * @param ServerRequestInterface $request * @return ResponseInterface @@ -163,8 +188,35 @@ class Handler implements RequestHandlerInterface $data = call_user_func([$controller, $this->handler[1]], ...$this->parameters); + $this->executeDeferred(); + /** 根据返回类型 */ return $this->format->call($data); } + + /** + * @return void + */ + private function executeDeferred(): void + { + foreach ($this->deferred as $defer) { + try { + $callback = $defer->callback; + $params = $defer->params; + + if (is_array($callback)) { + [$class, $method] = $callback; + $instance = Kiri::getDi()->get($class); + call_user_func([$instance, $method], ...$params); + } else { + $instance = Kiri::getDi()->get($callback); + call_user_func([$instance, '__invoke'], ...$params); + } + } catch (\Throwable $throwable) { + \Kiri::getLogger()->error('Defer callback failed: ' . $throwable->getMessage()); + } + } + } + } diff --git a/src/RouteEntry.php b/src/RouteEntry.php index 0d1eff3..7fff1b6 100644 --- a/src/RouteEntry.php +++ b/src/RouteEntry.php @@ -6,6 +6,9 @@ namespace Kiri\Router; class RouteEntry { + /** + * @param array $deferred Array of ['callback' => string|array, 'params' => array] + */ public function __construct( public readonly string $requestMethod, public readonly string $path, @@ -14,6 +17,7 @@ class RouteEntry public readonly array $middlewares = [], public readonly ?string $sourceFile = null, public readonly string $sourceKind = 'attribute', + public readonly array $deferred = [], ) { } } diff --git a/src/RouterCollector.php b/src/RouterCollector.php index 56d5ab2..f42e496 100644 --- a/src/RouterCollector.php +++ b/src/RouterCollector.php @@ -6,6 +6,8 @@ namespace Kiri\Router; use Closure; +use Kiri\Router\Annotate\Defer; +use Kiri\Router\Annotate\DeferRegistry; use Kiri\Router\Base\NotFoundController; use Kiri\Router\Constrict\RequestMethod; use Psr\Http\Server\MiddlewareInterface; @@ -236,6 +238,7 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate $this->methods[$method . '_' . $path] = $handler; $handler->setMiddlewares($this->registerMiddleware($handler->getClass(), $handler->getMethod())); + $handler->setDeferred(DeferRegistry::get($handler->getClass(), $handler->getMethod())); } @@ -256,6 +259,7 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate $middlewares = $handler instanceof Handler ? $handler->getMiddlewares() : $handler->middlewares; $sourceFile = $handler instanceof Handler ? $handler->getSourceFile() : $handler->sourceFile; $sourceKind = $handler instanceof Handler ? $handler->getSourceKind() : $handler->sourceKind; + $deferred = $handler instanceof Handler ? $this->serializeDeferred($handler->getDeferred()) : ($handler->deferred ?? []); $entries[] = [ 'request_method' => $requestMethod, @@ -265,6 +269,7 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate 'middlewares' => $middlewares, 'source_file' => $sourceFile, 'source_kind' => $sourceKind, + 'deferred' => $deferred, ]; } @@ -314,6 +319,7 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate middlewares: is_array($entry['middlewares'] ?? null) ? $entry['middlewares'] : [], sourceFile: is_string($sourceFile) ? $this->normalizePath($sourceFile) : null, sourceKind: is_string($entry['source_kind'] ?? null) ? $entry['source_kind'] : 'attribute', + deferred: is_array($entry['deferred'] ?? null) ? $entry['deferred'] : [], ); } @@ -373,10 +379,11 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate /** - * @param array $response - * @param array $middlewares - * @return array + * @param string $class + * @param string $method + * @return Defer[] */ + private function appendMiddleware(array $response, array $middlewares): array { foreach ($middlewares as $middleware) { @@ -448,6 +455,39 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate } + /** + * @param Defer[] $deferred + * @return array + */ + private function serializeDeferred(array $deferred): array + { + $result = []; + foreach ($deferred as $defer) { + $result[] = [ + 'callback' => $defer->callback, + 'params' => $defer->params, + ]; + } + return $result; + } + + + /** + * @param array $data + * @return Defer[] + */ + private function deserializeDeferred(array $data): array + { + $result = []; + foreach ($data as $item) { + if (isset($item['callback'])) { + $result[] = new Defer($item['callback'], $item['params'] ?? []); + } + } + return $result; + } + + private function normalizePath(string $path): string { $resolved = realpath($path) ?: $path; @@ -464,6 +504,7 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate $handler->setMiddlewares($method->middlewares); $handler->setSourceFile($method->sourceFile); $handler->setSourceKind($method->sourceKind); + $handler->setDeferred($this->deserializeDeferred($method->deferred)); $method = $handler; }