Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 18 additions & 69 deletions src/DispatchContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@
use ReflectionParameter;
use Respect\Rest\Routes\AbstractRoute;
use Respect\Rest\Routines\ParamSynced;
use Respect\Rest\Routines\ProxyableBy;
use Respect\Rest\Routines\ProxyableThrough;
use Respect\Rest\Routines\Routinable;
use Throwable;

use function array_values;
use function assert;
use function explode;
use function implode;
use function is_callable;
use function rawurldecode;
use function rtrim;
use function set_error_handler;
Expand Down Expand Up @@ -50,6 +47,8 @@ final class DispatchContext

public ResponseFactoryInterface|null $responseFactory = null;

private RoutinePipeline|null $routinePipeline = null;

private string $effectiveMethod = '';

private string $effectivePath = '';
Expand Down Expand Up @@ -137,9 +136,13 @@ public function response(): ResponseInterface|null
}

$errorHandler = $this->prepareForErrorForwards();
$preRoutineResult = $this->processPreRoutines();
$preRoutineResult = $this->routinePipeline()->processBy($this);

if ($preRoutineResult !== null) {
if ($preRoutineResult instanceof AbstractRoute) {
return $this->forward($preRoutineResult);
}

if ($preRoutineResult instanceof ResponseInterface) {
return $this->finalizeResponse($preRoutineResult);
}
Expand All @@ -157,7 +160,7 @@ public function response(): ResponseInterface|null
return $this->forward($rawResult);
}

$processedResult = $this->processPosRoutines($rawResult);
$processedResult = $this->routinePipeline()->processThrough($this, $rawResult);
$errorResponse = $this->forwardErrors($errorHandler);

if ($errorResponse !== null) {
Expand Down Expand Up @@ -205,6 +208,11 @@ public function forward(AbstractRoute $route): ResponseInterface|null
return $this->response();
}

public function setRoutinePipeline(RoutinePipeline $routinePipeline): void
{
$this->routinePipeline = $routinePipeline;
}

/** @return callable|null The previous error handler, or null */
protected function prepareForErrorForwards(): callable|null
{
Expand All @@ -229,70 +237,6 @@ static function (
return null;
}

protected function processPreRoutines(): mixed
{
assert($this->route !== null);
foreach ($this->route->routines as $routine) {
if (!$routine instanceof ProxyableBy) {
continue;
}

$result = $this->routineCall(
'by',
$this->method(),
$routine,
$this->params,
);

if ($result instanceof AbstractRoute) {
return $this->forward($result);
}

if ($result instanceof ResponseInterface) {
return $result;
}

if ($result === false) {
return false;
}
}

return null;
}

/**
* Processes post-routines on the raw handler result.
* Routines still receive raw values (arrays, strings, etc.) — not ResponseInterface.
*/
protected function processPosRoutines(mixed $response): mixed
{
$proxyResults = [];
assert($this->route !== null);

foreach ($this->route->routines as $routine) {
if (!($routine instanceof ProxyableThrough)) {
continue;
}

$proxyResults[] = $this->routineCall(
'through',
$this->method(),
$routine,
$this->params,
);
}

foreach ($proxyResults as $proxyCallback) {
if (!is_callable($proxyCallback)) {
continue;
}

$response = $proxyCallback($response);
}

return $response;
}

protected function forwardErrors(callable|null $errorHandler): ResponseInterface|null
{
if ($errorHandler !== null) {
Expand Down Expand Up @@ -395,6 +339,11 @@ protected function finalizeResponse(ResponseInterface $response): ResponseInterf
return $response->withBody($this->responseFactory->createResponse()->getBody());
}

private function routinePipeline(): RoutinePipeline
{
return $this->routinePipeline ??= new RoutinePipeline();
}

private function mergeHeaderValues(string $existing, string $appended): string
{
$mergedValues = [];
Expand Down
6 changes: 5 additions & 1 deletion src/DispatchEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@

final class DispatchEngine
{
private RoutinePipeline $routinePipeline;

public function __construct(private Router $router)
{
$this->routinePipeline = new RoutinePipeline();
}

public function dispatch(ServerRequestInterface $serverRequest): DispatchContext
Expand All @@ -39,6 +42,7 @@ public function dispatchContext(DispatchContext $context): DispatchContext
$this->router->isAutoDispatched = false;
$this->router->context = $context;
$context->responseFactory ??= $this->router->responseFactory;
$context->setRoutinePipeline($this->routinePipeline);

if (!$this->isRoutelessDispatch($context) && $context->route === null) {
$this->routeDispatch($context);
Expand Down Expand Up @@ -302,7 +306,7 @@ private function routineMatch(
$tempParams = $matchedByPath[$route];
$context->clearResponseMeta();
$context->route = $route;
if ($route->matchRoutines($context, $tempParams)) {
if ($this->routinePipeline->matches($context, $tempParams)) {
return $this->configureContext(
$context,
$route,
Expand Down
21 changes: 0 additions & 21 deletions src/Routes/AbstractRoute.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use ReflectionNamedType;
use Respect\Rest\DispatchContext;
use Respect\Rest\Routines\IgnorableFileExtension;
use Respect\Rest\Routines\ProxyableWhen;
use Respect\Rest\Routines\Routinable;
use Respect\Rest\Routines\Unique;
use Respect\Rest\Stream;
Expand Down Expand Up @@ -195,26 +194,6 @@ public function createUri(mixed ...$params): string
return rtrim((string) $this->virtualHost, ' /') . sprintf($this->regexForReplace, ...$params);
}

/** @param array<int, mixed> $params */
public function matchRoutines(DispatchContext $context, array $params = []): bool
{
foreach ($this->routines as $routine) {
if (
$routine instanceof ProxyableWhen
&& !$context->routineCall(
'when',
$context->method(),
$routine,
$params,
)
) {
return false;
}
}

return true;
}

/** @param array<int, mixed> $params */
public function match(DispatchContext $context, array &$params = []): bool
{
Expand Down
84 changes: 84 additions & 0 deletions src/RoutinePipeline.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace Respect\Rest;

use Psr\Http\Message\ResponseInterface;
use Respect\Rest\Routes\AbstractRoute;
use Respect\Rest\Routines\ProxyableBy;
use Respect\Rest\Routines\ProxyableThrough;
use Respect\Rest\Routines\ProxyableWhen;

use function assert;
use function is_callable;

final class RoutinePipeline
{
/** @param array<int, mixed> $params */
public function matches(DispatchContext $context, array $params = []): bool
{
assert($context->route !== null);

foreach ($context->route->routines as $routine) {
if (
$routine instanceof ProxyableWhen
&& !$context->routineCall('when', $context->method(), $routine, $params)
) {
return false;
}
}

return true;
}

public function processBy(DispatchContext $context): mixed
{
assert($context->route !== null);

foreach ($context->route->routines as $routine) {
if (!$routine instanceof ProxyableBy) {
continue;
}

$result = $context->routineCall(
'by',
$context->method(),
$routine,
$context->params,
);

if ($result instanceof AbstractRoute || $result instanceof ResponseInterface || $result === false) {
return $result;
}
}

return null;
}

public function processThrough(DispatchContext $context, mixed $response): mixed
{
assert($context->route !== null);

foreach ($context->route->routines as $routine) {
if (!($routine instanceof ProxyableThrough)) {
continue;
}

$proxyCallback = $context->routineCall(
'through',
$context->method(),
$routine,
$context->params,
);

if (!is_callable($proxyCallback)) {
continue;
}

$response = $proxyCallback($response);
}

return $response;
}
}
Loading