Skip to content

Commit 2a4f200

Browse files
vjikrustamwin
andauthored
Make Route, Group and MatchingResult dispatcher independent (#222)
Co-authored-by: Rustam Mamadaminov <rmamdaminov@gmail.com>
1 parent 04be2f6 commit 2a4f200

18 files changed

+444
-441
lines changed

.github/workflows/rector.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ name: rector
1414
jobs:
1515
rector:
1616
uses: yiisoft/actions/.github/workflows/rector.yml@master
17+
secrets:
18+
token: ${{ secrets.YIISOFT_GITHUB_TOKEN }}
1719
with:
1820
os: >-
1921
['ubuntu-latest']
2022
php: >-
21-
['8.0']
23+
['8.2']

.phpstorm.meta.php/Group.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
'host',
1212
'hosts',
1313
'corsMiddleware',
14-
'items',
15-
'middlewareDefinitions',
16-
'hasDispatcher',
17-
'hasCorsMiddleware'
14+
'routes',
15+
'hasCorsMiddleware',
16+
'enabledMiddlewares',
1817
);
19-
}
18+
}

.phpstorm.meta.php/Route.php

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
registerArgumentsSet(
88
'routeDataKeys',
99
'name',
10+
'pattern',
1011
'host',
1112
'hosts',
12-
'pattern',
1313
'methods',
14-
'override',
1514
'defaults',
16-
'dispatcherWithMiddlewares',
17-
'hasDispatcher',
18-
'hasMiddlewares'
15+
'override',
16+
'hasMiddlewares',
17+
'enabledMiddlewares',
1918
);
20-
}
19+
}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
- New #195: Add debug collector for `yiisoft/yii-debug` (@xepozz)
66
- Chg #207: Replace two `RouteCollectorInterface` methods `addRoute()` and `addGroup()` to single `addRoute()` (@vjik)
77
- Enh #202: Add support for `psr/http-message` version `^2.0` (@vjik)
8+
- Chg #222: Make `Route`, `Group` and `MatchingResult` dispatcher-independent (@rustamwin, @vjik)
89

910
## 3.0.0 February 17, 2023
1011

UPGRADE.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Upgrading Instructions for Yii Router
2+
3+
This file contains the upgrade notes for the Yii Router.
4+
These notes highlight changes that could break your application when you upgrade it from one major version to another.
5+
6+
## 4.0.0
7+
8+
In this release classes `Route`, `Group` and `MatchingResult` are made dispatcher-independent. Now you don't can inject
9+
own middleware dispatcher to group or to route.
10+
11+
The following backward incompatible changes have been made.
12+
13+
### `Route`
14+
15+
- Removed parameter `$dispatcher` from `Route` creating methods: `get()`, `post()`, `put()`, `delete()`, `patch()`,
16+
`head()`, `options()`, `methods()`.
17+
- Removed methods `Route::injectDispatcher()` and `Route::withDispatcher()`.
18+
- `Route::getData()` changes:
19+
- removed elements `dispatcherWithMiddlewares` and `hasDispatcher`;
20+
- added element `enabledMiddlewares`.
21+
22+
### `Group`
23+
24+
- Removed parameter `$dispatcher` from `Group::create()` method.
25+
- Removed method `Group::withDispatcher()`.
26+
- `Group::getData()` changes:
27+
- removed element `hasDispatcher`;
28+
- key `items` renamed to `routes`;
29+
- key `middlewareDefinitions` renamed to `enabledMiddlewares`.
30+
31+
### `MatchingResult`
32+
33+
- Removed `MatchingResult` implementation from `MiddlewareInterface`, so it is no longer middleware.
34+
- Removed method `MatchingResult::process()`.

src/Debug/DebugRoutesCommand.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
use Yiisoft\VarDumper\VarDumper;
1515
use Yiisoft\Yii\Debug\Debugger;
1616

17+
/**
18+
* @codeCoverageIgnore
19+
*/
1720
final class DebugRoutesCommand extends Command
1821
{
1922
public const COMMAND_NAME = 'debug:routes';

src/Debug/RouterCollector.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
use Yiisoft\Yii\Debug\Collector\CollectorTrait;
1313
use Yiisoft\Yii\Debug\Collector\SummaryCollectorInterface;
1414

15+
/**
16+
* @codeCoverageIgnore
17+
*/
1518
final class RouterCollector implements SummaryCollectorInterface
1619
{
1720
use CollectorTrait;

src/Debug/UrlMatcherInterfaceProxy.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
use Yiisoft\Router\MatchingResult;
99
use Yiisoft\Router\UrlMatcherInterface;
1010

11+
/**
12+
* @codeCoverageIgnore
13+
*/
1114
final class UrlMatcherInterfaceProxy implements UrlMatcherInterface
1215
{
1316
public function __construct(private UrlMatcherInterface $urlMatcher, private RouterCollector $routerCollector)

src/Group.php

Lines changed: 52 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use InvalidArgumentException;
88
use RuntimeException;
9-
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
9+
use Yiisoft\Router\Internal\MiddlewareFilter;
1010

1111
use function in_array;
1212

@@ -15,12 +15,13 @@ final class Group
1515
/**
1616
* @var Group[]|Route[]
1717
*/
18-
private array $items = [];
18+
private array $routes = [];
1919

2020
/**
2121
* @var array[]|callable[]|string[]
22+
* @psalm-var list<array|callable|string>
2223
*/
23-
private array $middlewareDefinitions = [];
24+
private array $middlewares = [];
2425

2526
/**
2627
* @var string[]
@@ -29,62 +30,46 @@ final class Group
2930
private ?string $namePrefix = null;
3031
private bool $routesAdded = false;
3132
private bool $middlewareAdded = false;
32-
private array $disabledMiddlewareDefinitions = [];
33+
private array $disabledMiddlewares = [];
34+
35+
/**
36+
* @psalm-var list<array|callable|string>|null
37+
*/
38+
private ?array $enabledMiddlewaresCache = null;
3339

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

39-
private function __construct(private ?string $prefix = null, private ?MiddlewareDispatcher $dispatcher = null)
40-
{
45+
private function __construct(
46+
private ?string $prefix = null
47+
) {
4148
}
4249

4350
/**
4451
* Create a new group instance.
4552
*
4653
* @param string|null $prefix URL prefix to prepend to all routes of the group.
47-
* @param MiddlewareDispatcher|null $dispatcher Middleware dispatcher to use for the group.
4854
*/
49-
public static function create(
50-
?string $prefix = null,
51-
MiddlewareDispatcher $dispatcher = null
52-
): self {
53-
return new self($prefix, $dispatcher);
55+
public static function create(?string $prefix = null): self
56+
{
57+
return new self($prefix);
5458
}
5559

5660
public function routes(self|Route ...$routes): self
5761
{
5862
if ($this->middlewareAdded) {
5963
throw new RuntimeException('routes() can not be used after prependMiddleware().');
6064
}
61-
$new = clone $this;
62-
foreach ($routes as $route) {
63-
if ($new->dispatcher !== null && !$route->getData('hasDispatcher')) {
64-
$route = $route->withDispatcher($new->dispatcher);
65-
}
66-
$new->items[] = $route;
67-
}
6865

66+
$new = clone $this;
67+
$new->routes = $routes;
6968
$new->routesAdded = true;
7069

7170
return $new;
7271
}
7372

74-
public function withDispatcher(MiddlewareDispatcher $dispatcher): self
75-
{
76-
$group = clone $this;
77-
$group->dispatcher = $dispatcher;
78-
foreach ($group->items as $index => $item) {
79-
if (!$item->getData('hasDispatcher')) {
80-
$item = $item->withDispatcher($dispatcher);
81-
$group->items[$index] = $item;
82-
}
83-
}
84-
85-
return $group;
86-
}
87-
8873
/**
8974
* Adds a middleware definition that handles CORS requests.
9075
* If set, routes for {@see Method::OPTIONS} request will be added automatically.
@@ -103,31 +88,38 @@ public function withCors(array|callable|string|null $middlewareDefinition): self
10388
* Appends a handler middleware definition that should be invoked for a matched route.
10489
* First added handler will be executed first.
10590
*/
106-
public function middleware(array|callable|string ...$middlewareDefinition): self
91+
public function middleware(array|callable|string ...$definition): self
10792
{
10893
if ($this->routesAdded) {
10994
throw new RuntimeException('middleware() can not be used after routes().');
11095
}
96+
11197
$new = clone $this;
11298
array_push(
113-
$new->middlewareDefinitions,
114-
...array_values($middlewareDefinition)
99+
$new->middlewares,
100+
...array_values($definition)
115101
);
102+
103+
$new->enabledMiddlewaresCache = null;
104+
116105
return $new;
117106
}
118107

119108
/**
120109
* Prepends a handler middleware definition that should be invoked for a matched route.
121110
* First added handler will be executed last.
122111
*/
123-
public function prependMiddleware(array|callable|string ...$middlewareDefinition): self
112+
public function prependMiddleware(array|callable|string ...$definition): self
124113
{
125114
$new = clone $this;
126115
array_unshift(
127-
$new->middlewareDefinitions,
128-
...array_values($middlewareDefinition)
116+
$new->middlewares,
117+
...array_values($definition)
129118
);
119+
130120
$new->middlewareAdded = true;
121+
$new->enabledMiddlewaresCache = null;
122+
131123
return $new;
132124
}
133125

@@ -163,13 +155,16 @@ public function hosts(string ...$hosts): self
163155
* It is useful to avoid invoking one of the parent group middleware for
164156
* a certain route.
165157
*/
166-
public function disableMiddleware(mixed ...$middlewareDefinition): self
158+
public function disableMiddleware(mixed ...$definition): self
167159
{
168160
$new = clone $this;
169161
array_push(
170-
$new->disabledMiddlewareDefinitions,
171-
...array_values($middlewareDefinition),
162+
$new->disabledMiddlewares,
163+
...array_values($definition),
172164
);
165+
166+
$new->enabledMiddlewaresCache = null;
167+
173168
return $new;
174169
}
175170

@@ -180,10 +175,10 @@ public function disableMiddleware(mixed ...$middlewareDefinition): self
180175
*
181176
* @psalm-return (
182177
* T is ('prefix'|'namePrefix'|'host') ? string|null :
183-
* (T is 'items' ? Group[]|Route[] :
178+
* (T is 'routes' ? Group[]|Route[] :
184179
* (T is 'hosts' ? array<array-key, string> :
185-
* (T is ('hasCorsMiddleware'|'hasDispatcher') ? bool :
186-
* (T is 'middlewareDefinitions' ? list<array|callable|string> :
180+
* (T is ('hasCorsMiddleware') ? bool :
181+
* (T is 'enabledMiddlewares' ? list<array|callable|string> :
187182
* (T is 'corsMiddleware' ? array|callable|string|null : mixed)
188183
* )
189184
* )
@@ -199,23 +194,25 @@ public function getData(string $key): mixed
199194
'host' => $this->hosts[0] ?? null,
200195
'hosts' => $this->hosts,
201196
'corsMiddleware' => $this->corsMiddleware,
202-
'items' => $this->items,
197+
'routes' => $this->routes,
203198
'hasCorsMiddleware' => $this->corsMiddleware !== null,
204-
'hasDispatcher' => $this->dispatcher !== null,
205-
'middlewareDefinitions' => $this->getMiddlewareDefinitions(),
199+
'enabledMiddlewares' => $this->getEnabledMiddlewares(),
206200
default => throw new InvalidArgumentException('Unknown data key: ' . $key),
207201
};
208202
}
209203

210-
private function getMiddlewareDefinitions(): array
204+
/**
205+
* @return array[]|callable[]|string[]
206+
* @psalm-return list<array|callable|string>
207+
*/
208+
private function getEnabledMiddlewares(): array
211209
{
212-
/** @var mixed $definition */
213-
foreach ($this->middlewareDefinitions as $index => $definition) {
214-
if (in_array($definition, $this->disabledMiddlewareDefinitions, true)) {
215-
unset($this->middlewareDefinitions[$index]);
216-
}
210+
if ($this->enabledMiddlewaresCache !== null) {
211+
return $this->enabledMiddlewaresCache;
217212
}
218213

219-
return array_values($this->middlewareDefinitions);
214+
$this->enabledMiddlewaresCache = MiddlewareFilter::filter($this->middlewares, $this->disabledMiddlewares);
215+
216+
return $this->enabledMiddlewaresCache;
220217
}
221218
}

src/Internal/MiddlewareFilter.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Router\Internal;
6+
7+
/**
8+
* @internal
9+
*/
10+
final class MiddlewareFilter
11+
{
12+
/**
13+
* @param array[]|callable[]|string[] $middlewares
14+
* @return array[]|callable[]|string[]
15+
*
16+
* @psalm-param list<array|callable|string> $middlewares
17+
* @psalm-return list<array|callable|string>
18+
*/
19+
public static function filter(array $middlewares, array $disabledMiddlewares): array
20+
{
21+
$result = [];
22+
23+
foreach ($middlewares as $middleware) {
24+
if (in_array($middleware, $disabledMiddlewares, true)) {
25+
continue;
26+
}
27+
28+
$result[] = $middleware;
29+
}
30+
31+
return $result;
32+
}
33+
}

0 commit comments

Comments
 (0)