Skip to content

Commit b6ecaa3

Browse files
authored
Add composite parameters resolver (#79)
1 parent aead5ca commit b6ecaa3

11 files changed

+169
-16
lines changed

.editorconfig

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ indent_style = space
1010
indent_size = 4
1111
trim_trailing_whitespace = true
1212

13+
[*.php]
14+
ij_php_space_before_short_closure_left_parenthesis = false
15+
ij_php_space_after_type_cast = true
16+
1317
[*.md]
1418
trim_trailing_whitespace = false
1519

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# Yii Middleware Dispatcher Change Log
22

3-
## 5.0.1 under development
3+
## 5.1.0 under development
44

55
- Enh #75: Optimize `MiddlewareFactory` performance (@random-rage)
6+
- New #76: Add composite parameters resolver (@vjik)
67

78
## 5.0.0 January 09, 2023
89

README.md

+21-3
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ class CoolParametersResolver implements ParametersResolverInterface
110110
public function resolve(array $parameters, ServerRequestInterface $request): MiddlewareInterface
111111
{
112112
$resolvedParameters = [];
113-
foreach ($parameters as $parameter) {
114-
if ($request->getAttribute($parameter->getName()) !== null) {
115-
$resolvedParameters[$parameter->getName()] = $request->getAttribute($parameter->getName())
113+
foreach ($parameters as $name => $parameter) {
114+
if ($request->getAttribute($name) !== null) {
115+
$resolvedParameters[$name] = $request->getAttribute($name)
116116
}
117117
}
118118

@@ -133,6 +133,24 @@ $dispatcher = new MiddlewareDispatcher(
133133
);
134134
```
135135

136+
To combine several parameters' resolvers use `CompositeParametersResolver`:
137+
138+
```php
139+
use Yiisoft\Middleware\Dispatcher\CompositeParametersResolver;
140+
use Yiisoft\Middleware\Dispatcher\MiddlewareDispatcher;
141+
use Yiisoft\Middleware\Dispatcher\MiddlewareFactory;
142+
143+
$dispatcher = new MiddlewareDispatcher(
144+
new MiddlewareFactory(
145+
$diContainer, new CompositeParametersResolver(
146+
new CoolParametersResolver(),
147+
new YetAnotherParametersResolver(),
148+
)
149+
),
150+
$eventDispatcher
151+
);
152+
```
153+
136154
## Testing
137155

138156
### Unit testing

src/CompositeParametersResolver.php

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Middleware\Dispatcher;
6+
7+
use Psr\Http\Message\ServerRequestInterface;
8+
9+
use function in_array;
10+
11+
final class CompositeParametersResolver implements ParametersResolverInterface
12+
{
13+
/**
14+
* @var ParametersResolverInterface[]
15+
*/
16+
private array $resolvers;
17+
18+
public function __construct(ParametersResolverInterface ...$resolvers)
19+
{
20+
$this->resolvers = $resolvers;
21+
}
22+
23+
public function resolve(array $parameters, ServerRequestInterface $request): array
24+
{
25+
$results = [];
26+
foreach ($this->resolvers as $resolver) {
27+
$result = $resolver->resolve($parameters, $request);
28+
$results[] = $result;
29+
30+
$resultKeys = array_keys($result);
31+
$parameters = array_filter(
32+
$parameters,
33+
static fn($key) => !in_array($key, $resultKeys, true),
34+
ARRAY_FILTER_USE_KEY
35+
);
36+
}
37+
38+
return array_merge(...$results);
39+
}
40+
}

src/MiddlewareFactory.php

+21-8
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,11 @@ private function createCallableWrapper(callable $callback): MiddlewareInterface
144144
return new class ($callback, $this->container, $this->parametersResolver) implements MiddlewareInterface {
145145
/** @var callable */
146146
private $callback;
147-
/** @var ReflectionParameter[] */
148-
private array $callableParameters;
147+
/**
148+
* @var ReflectionParameter[]
149+
* @psalm-var array<string,ReflectionParameter>
150+
*/
151+
private array $callableParameters = [];
149152

150153
public function __construct(
151154
callable $callback,
@@ -154,7 +157,11 @@ public function __construct(
154157
) {
155158
$this->callback = $callback;
156159
$callback = Closure::fromCallable($callback);
157-
$this->callableParameters = (new ReflectionFunction($callback))->getParameters();
160+
161+
$callableParameters = (new ReflectionFunction($callback))->getParameters();
162+
foreach ($callableParameters as $parameter) {
163+
$this->callableParameters[$parameter->getName()] = $parameter;
164+
}
158165
}
159166

160167
public function process(
@@ -168,6 +175,7 @@ public function process(
168175
$this->parametersResolver->resolve($this->callableParameters, $request)
169176
);
170177
}
178+
171179
/** @var MiddlewareInterface|mixed|ResponseInterface $response */
172180
$response = (new Injector($this->container))->invoke($this->callback, $parameters);
173181
if ($response instanceof ResponseInterface) {
@@ -176,6 +184,7 @@ public function process(
176184
if ($response instanceof MiddlewareInterface) {
177185
return $response->process($request, $handler);
178186
}
187+
179188
throw new InvalidMiddlewareDefinitionException($this->callback);
180189
}
181190

@@ -193,8 +202,11 @@ public function __debugInfo(): array
193202
private function createActionWrapper(string $class, string $method): MiddlewareInterface
194203
{
195204
return new class ($this->container, $this->parametersResolver, $class, $method) implements MiddlewareInterface {
196-
/** @var ReflectionParameter[] */
197-
private array $actionParameters;
205+
/**
206+
* @var ReflectionParameter[]
207+
* @psalm-var array<string,ReflectionParameter>
208+
*/
209+
private array $actionParameters = [];
198210

199211
public function __construct(
200212
private ContainerInterface $container,
@@ -204,9 +216,10 @@ public function __construct(
204216
/** @var non-empty-string */
205217
private string $method
206218
) {
207-
$this->actionParameters = (new ReflectionClass($this->class))
208-
->getMethod($this->method)
209-
->getParameters();
219+
$actionParameters = (new ReflectionClass($this->class))->getMethod($this->method)->getParameters();
220+
foreach ($actionParameters as $parameter) {
221+
$this->actionParameters[$parameter->getName()] = $parameter;
222+
}
210223
}
211224

212225
public function process(

src/ParametersResolverInterface.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ interface ParametersResolverInterface
1919
*
2020
* @param ReflectionParameter[] $parameters
2121
*
22-
* @return array<array-key, mixed>
22+
* @psalm-param array<string,ReflectionParameter> $parameters
23+
*
24+
* @psalm-return array<string,mixed>
2325
*/
2426
public function resolve(array $parameters, ServerRequestInterface $request): array;
2527
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Middleware\Dispatcher\Tests;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use Psr\Http\Message\ServerRequestInterface;
9+
use Psr\Http\Server\RequestHandlerInterface;
10+
use Yiisoft\Middleware\Dispatcher\CompositeParametersResolver;
11+
use Yiisoft\Middleware\Dispatcher\MiddlewareFactory;
12+
use Yiisoft\Middleware\Dispatcher\Tests\Support\ParametersResolver\NameParametersResolver;
13+
use Yiisoft\Middleware\Dispatcher\Tests\Support\TestController;
14+
use Yiisoft\Test\Support\Container\SimpleContainer;
15+
16+
final class CompositeParametersResolverTest extends TestCase
17+
{
18+
public function testBase(): void
19+
{
20+
$resolver = new CompositeParametersResolver(
21+
new NameParametersResolver(['a' => 1, 'b' => 2]),
22+
new NameParametersResolver(['a' => 10, 'b' => 11, 'c' => 12, 'd' => 13]),
23+
);
24+
25+
$container = new SimpleContainer([TestController::class => new TestController()]);
26+
$middleware = (new MiddlewareFactory($container, $resolver))
27+
->create([TestController::class, 'compositeResolver']);
28+
29+
$response = $middleware->process(
30+
$this->createMock(ServerRequestInterface::class),
31+
$this->createMock(RequestHandlerInterface::class)
32+
);
33+
34+
$this->assertSame(
35+
'1-2-12-13',
36+
$response->getReasonPhrase(),
37+
);
38+
}
39+
}

tests/MiddlewareFactoryTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Yiisoft\Middleware\Dispatcher\MiddlewareFactory;
1717
use Yiisoft\Middleware\Dispatcher\ParametersResolverInterface;
1818
use Yiisoft\Middleware\Dispatcher\Tests\Support\InvokeableAction;
19-
use Yiisoft\Middleware\Dispatcher\Tests\Support\SimpleParametersResolver;
19+
use Yiisoft\Middleware\Dispatcher\Tests\Support\ParametersResolver\SimpleParametersResolver;
2020
use Yiisoft\Middleware\Dispatcher\Tests\Support\UseParamsController;
2121
use Yiisoft\Middleware\Dispatcher\Tests\Support\UseParamsMiddleware;
2222
use Yiisoft\Middleware\Dispatcher\Tests\Support\InvalidController;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\Middleware\Dispatcher\Tests\Support\ParametersResolver;
6+
7+
use Psr\Http\Message\ServerRequestInterface;
8+
use Yiisoft\Middleware\Dispatcher\ParametersResolverInterface;
9+
10+
use function array_key_exists;
11+
12+
final class NameParametersResolver implements ParametersResolverInterface
13+
{
14+
public function __construct(private array $data)
15+
{
16+
}
17+
18+
public function resolve(array $parameters, ServerRequestInterface $request): array
19+
{
20+
$result = [];
21+
foreach ($parameters as $name => $_parameter) {
22+
if (array_key_exists($name, $this->data)) {
23+
$result[$name] = $this->data[$name];
24+
}
25+
}
26+
27+
return $result;
28+
}
29+
}

tests/Support/SimpleParametersResolver.php tests/Support/ParametersResolver/SimpleParametersResolver.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Yiisoft\Middleware\Dispatcher\Tests\Support;
5+
namespace Yiisoft\Middleware\Dispatcher\Tests\Support\ParametersResolver;
66

77
use Psr\Http\Message\ServerRequestInterface;
88
use Yiisoft\Middleware\Dispatcher\ParametersResolverInterface;

tests/Support/TestController.php

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@ public function index(): ResponseInterface
1414
return new Response(200, ['test' => 'yii']);
1515
}
1616

17-
public function indexWithParams(string $test = '')
17+
public function indexWithParams(string $test = ''): ResponseInterface
1818
{
1919
return new Response(200, ['test' => $test]);
2020
}
21+
22+
public function compositeResolver(int $a = 0, int $b = 0, int $c = 0, int $d = 0): ResponseInterface
23+
{
24+
return new Response(
25+
reason: $a . '-' . $b . '-' . $c . '-' . $d,
26+
);
27+
}
2128
}

0 commit comments

Comments
 (0)