Skip to content

Commit

Permalink
Make Route, Group and MatchingResult dispatcher independent
Browse files Browse the repository at this point in the history
  • Loading branch information
vjik committed Nov 2, 2023
1 parent 04be2f6 commit d03478c
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 431 deletions.
9 changes: 4 additions & 5 deletions .phpstorm.meta.php/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
'host',
'hosts',
'corsMiddleware',
'items',
'middlewareDefinitions',
'hasDispatcher',
'hasCorsMiddleware'
'routes',
'hasCorsMiddleware',
'enabledMiddlewares',
);
}
}
11 changes: 5 additions & 6 deletions .phpstorm.meta.php/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
registerArgumentsSet(
'routeDataKeys',
'name',
'pattern',
'host',
'hosts',
'pattern',
'methods',
'override',
'defaults',
'dispatcherWithMiddlewares',
'hasDispatcher',
'hasMiddlewares'
'override',
'hasMiddlewares',
'enabledMiddlewares',
);
}
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- New #195: Add debug collector for `yiisoft/yii-debug` (@xepozz)
- Chg #207: Replace two `RouteCollectorInterface` methods `addRoute()` and `addGroup()` to single `addRoute()` (@vjik)
- Enh #202: Add support for `psr/http-message` version `^2.0` (@vjik)
- Chg #222: Make `Route`, `Group` and `MatchingResult` dispatcher independent (@rustamwin, @vjik)

## 3.0.0 February 17, 2023

Expand Down
34 changes: 34 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Upgrading Instructions for Yii Router

This file contains the upgrade notes for the Yii Router.
These notes highlight changes that could break your application when you upgrade it from one major version to another.

## 4.0.0

In this release classes `Route`, `Group` and `MatchingResult` are made dispatcher independent. Now you don't can inject
own middleware dispatcher to group or to route.

The following backward incompatible changes have been made.

### `Route`

- Removed parameter `$dispatcher` from `Route` creating methods: `get()`, `post()`, `put()`, `delete()`, `patch()`,
`head()`, `options()`, `methods()`.
- Removed methods `Route::injectDispatcher()` and `Route::withDispatcher()`.
- `Route::getData()` changes:
- removed elements `dispatcherWithMiddlewares` and `hasDispatcher`;
- added element `enabledMiddlewares`.

### `Group`

- Removed parameter `$dispatcher` from `Group::create()` method.
- Removed method `Group::withDispatcher()`.
- `Group::getData()` changes:
- removed element `hasDispatcher`;
- key `items` renamed to `routes`;
- key `middlewareDefinitions` renamed to `enabledMiddlewares`.

### `MatchingResult`

- Removed `MatchingResult` implementation from `MiddlewareInterface`, so it is no longer middleware.
- Removed method `MatchingResult::process()`.
85 changes: 31 additions & 54 deletions src/Group.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use InvalidArgumentException;
use RuntimeException;
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;

use function in_array;

Expand All @@ -15,12 +14,12 @@ final class Group
/**
* @var Group[]|Route[]
*/
private array $items = [];
private array $routes = [];

/**
* @var array[]|callable[]|string[]
*/
private array $middlewareDefinitions = [];
private array $middlewares = [];

/**
* @var string[]
Expand All @@ -29,62 +28,40 @@ final class Group
private ?string $namePrefix = null;
private bool $routesAdded = false;
private bool $middlewareAdded = false;
private array $disabledMiddlewareDefinitions = [];
private array $disabledMiddlewares = [];

/**
* @var array|callable|string|null Middleware definition for CORS requests.
*/
private $corsMiddleware = null;

private function __construct(private ?string $prefix = null, private ?MiddlewareDispatcher $dispatcher = null)
{
private function __construct(
private ?string $prefix = null
) {
}

/**
* Create a new group instance.
*
* @param string|null $prefix URL prefix to prepend to all routes of the group.
* @param MiddlewareDispatcher|null $dispatcher Middleware dispatcher to use for the group.
*/
public static function create(
?string $prefix = null,
MiddlewareDispatcher $dispatcher = null
): self {
return new self($prefix, $dispatcher);
public static function create(?string $prefix = null): self {
return new self($prefix);
}

public function routes(self|Route ...$routes): self
{
if ($this->middlewareAdded) {
throw new RuntimeException('routes() can not be used after prependMiddleware().');
}
$new = clone $this;
foreach ($routes as $route) {
if ($new->dispatcher !== null && !$route->getData('hasDispatcher')) {
$route = $route->withDispatcher($new->dispatcher);
}
$new->items[] = $route;
}

$new = clone $this;
$new->routes = $routes;
$new->routesAdded = true;

return $new;
}

public function withDispatcher(MiddlewareDispatcher $dispatcher): self
{
$group = clone $this;
$group->dispatcher = $dispatcher;
foreach ($group->items as $index => $item) {
if (!$item->getData('hasDispatcher')) {
$item = $item->withDispatcher($dispatcher);
$group->items[$index] = $item;
}
}

return $group;
}

/**
* Adds a middleware definition that handles CORS requests.
* If set, routes for {@see Method::OPTIONS} request will be added automatically.
Expand All @@ -103,29 +80,31 @@ public function withCors(array|callable|string|null $middlewareDefinition): self
* Appends a handler middleware definition that should be invoked for a matched route.
* First added handler will be executed first.
*/
public function middleware(array|callable|string ...$middlewareDefinition): self
public function middleware(array|callable|string ...$definition): self
{
if ($this->routesAdded) {
throw new RuntimeException('middleware() can not be used after routes().');
}

$new = clone $this;
array_push(
$new->middlewareDefinitions,
...array_values($middlewareDefinition)
$new->middlewares,
...array_values($definition)
);

return $new;
}

/**
* Prepends a handler middleware definition that should be invoked for a matched route.
* First added handler will be executed last.
*/
public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
public function prependMiddleware(array|callable|string ...$definition): self
{
$new = clone $this;
array_unshift(
$new->middlewareDefinitions,
...array_values($middlewareDefinition)
$new->middlewares,
...array_values($definition)
);
$new->middlewareAdded = true;
return $new;
Expand Down Expand Up @@ -163,12 +142,12 @@ public function hosts(string ...$hosts): self
* It is useful to avoid invoking one of the parent group middleware for
* a certain route.
*/
public function disableMiddleware(mixed ...$middlewareDefinition): self
public function disableMiddleware(mixed ...$definition): self
{
$new = clone $this;
array_push(
$new->disabledMiddlewareDefinitions,
...array_values($middlewareDefinition),
$new->disabledMiddlewares,
...array_values($definition),
);
return $new;
}
Expand All @@ -180,10 +159,10 @@ public function disableMiddleware(mixed ...$middlewareDefinition): self
*
* @psalm-return (
* T is ('prefix'|'namePrefix'|'host') ? string|null :
* (T is 'items' ? Group[]|Route[] :
* (T is 'routes' ? Group[]|Route[] :
* (T is 'hosts' ? array<array-key, string> :
* (T is ('hasCorsMiddleware'|'hasDispatcher') ? bool :
* (T is 'middlewareDefinitions' ? list<array|callable|string> :
* (T is ('hasCorsMiddleware') ? bool :
* (T is 'enabledMiddlewares' ? list<array|callable|string> :
* (T is 'corsMiddleware' ? array|callable|string|null : mixed)
* )
* )
Expand All @@ -199,23 +178,21 @@ public function getData(string $key): mixed
'host' => $this->hosts[0] ?? null,
'hosts' => $this->hosts,
'corsMiddleware' => $this->corsMiddleware,
'items' => $this->items,
'routes' => $this->routes,
'hasCorsMiddleware' => $this->corsMiddleware !== null,
'hasDispatcher' => $this->dispatcher !== null,
'middlewareDefinitions' => $this->getMiddlewareDefinitions(),
'enabledMiddlewares' => $this->getEnabledMiddlewares(),
default => throw new InvalidArgumentException('Unknown data key: ' . $key),
};
}

private function getMiddlewareDefinitions(): array
private function getEnabledMiddlewares(): array
{
/** @var mixed $definition */
foreach ($this->middlewareDefinitions as $index => $definition) {
if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
unset($this->middlewareDefinitions[$index]);
foreach ($this->middlewares as $index => $definition) {
if (in_array($definition, $this->disabledMiddlewares, true)) {
unset($this->middlewares[$index]);
}
}

return array_values($this->middlewareDefinitions);
return array_values($this->middlewares);
}
}
42 changes: 7 additions & 35 deletions src/MatchingResult.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,14 @@

namespace Yiisoft\Router;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;
use Yiisoft\Http\Method;
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;

final class MatchingResult implements MiddlewareInterface
final class MatchingResult
{
/**
* @var array<string,string>
* @var string[]
* @psalm-var array<string,string>
*/
private array $arguments = [];

Expand All @@ -24,21 +20,13 @@ final class MatchingResult implements MiddlewareInterface
*/
private array $methods = [];

private ?MiddlewareDispatcher $dispatcher = null;

private function __construct(private ?Route $route)
{
}

public function withDispatcher(MiddlewareDispatcher $dispatcher): self
{
$new = clone $this;
$new->dispatcher = $dispatcher;
return $new;
}

/**
* @param array<string,string> $arguments
* @param string[] $arguments
* @psalm-param array<string,string> $arguments
*/
public static function fromSuccess(Route $route, array $arguments): self
{
Expand Down Expand Up @@ -71,7 +59,8 @@ public function isMethodFailure(): bool
}

/**
* @return array<string,string>
* @return string[]
* @psalm-return array<string,string>
*/
public function arguments(): array
{
Expand All @@ -94,21 +83,4 @@ public function route(): Route

return $this->route;
}

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!$this->isSuccess()) {
return $handler->handle($request);
}

// Inject dispatcher only if we have not previously injected.
// This improves performance in event-loop applications.
if ($this->dispatcher !== null && !$this->route->getData('hasDispatcher')) {
$this->route->injectDispatcher($this->dispatcher);
}

return $this->route
->getData('dispatcherWithMiddlewares')
->dispatch($request, $handler);
}
}
6 changes: 3 additions & 3 deletions src/Middleware/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface

$this->currentRoute->setRouteWithArguments($result->route(), $result->arguments());

return $result
->withDispatcher($this->dispatcher)
->process($request, $handler);
return $this->dispatcher
->withMiddlewares($result->route()->getData('enabledMiddlewares'))
->dispatch($request, $handler);
}
}
Loading

0 comments on commit d03478c

Please sign in to comment.