Skip to content

Commit 5c417f3

Browse files
authored
Add array definition support (#53)
1 parent 1188670 commit 5c417f3

9 files changed

+236
-74
lines changed

CHANGELOG.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
# Yii Middleware Dispatcher Change Log
22

3-
## 2.0.2 under development
3+
## 2.1.0 under development
44

5-
- Enh #45: Implement friendly exception for `InvalidMiddlewareDefinitionException` (vjik)
5+
- New #53: Add array definition support for middleware (@vjik)
6+
- Enh #45: Implement friendly exception for `InvalidMiddlewareDefinitionException` (@vjik)
67

78
## 2.0.1 February 14, 2022
89

9-
- Chg #47: Add debug info to action wrapper (rustamwin)
10+
- Chg #47: Add debug info to action wrapper (@rustamwin)
1011

1112
## 2.0.0 November 10, 2021
1213

13-
- Chg #43: Reverse middleware order in `withMiddlewares()` (rustamwin)
14+
- Chg #43: Reverse middleware order in `withMiddlewares()` (@rustamwin)
1415

1516
## 1.0.0 November 08, 2021
1617

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,15 @@ In the above we have used a callback. Overall the following options are availabl
7171
```
7272
The middleware returned will be executed.
7373
- A callback `function(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface`.
74+
- An array definition (see [syntax](https://github.com/yiisoft/definitions#arraydefinition)) of middleware:
75+
```php
76+
[
77+
'class' => MyMiddleware::class,
78+
'__construct()' => [
79+
'someVar' => 42,
80+
],
81+
]
82+
```
7483

7584
For handler action and callable typed parameters are automatically injected using dependency injection container.
7685
Current request and handler could be obtained by type-hinting for `ServerRequestInterface` and `RequestHandlerInterface`.

composer.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"psr/http-message": "^1.0",
2424
"psr/http-server-handler": "^1.0",
2525
"psr/http-server-middleware": "^1.0",
26+
"yiisoft/definitions": "^2.0",
2627
"yiisoft/friendly-exception": "^1.1",
2728
"yiisoft/injector": "^1.0"
2829
},

src/InvalidMiddlewareDefinitionException.php

+111-39
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,18 @@
66

77
use InvalidArgumentException;
88
use Psr\Http\Server\MiddlewareInterface;
9+
use Yiisoft\Definitions\Exception\InvalidConfigException;
10+
use Yiisoft\Definitions\Helpers\DefinitionValidator;
911
use Yiisoft\FriendlyException\FriendlyExceptionInterface;
1012

13+
use function array_slice;
14+
use function count;
1115
use function get_class;
16+
use function gettype;
1217
use function is_array;
18+
use function is_bool;
19+
use function is_float;
20+
use function is_int;
1321
use function is_object;
1422
use function is_string;
1523

@@ -19,7 +27,7 @@ final class InvalidMiddlewareDefinitionException extends InvalidArgumentExceptio
1927
* @var mixed
2028
*/
2129
private $definition;
22-
private ?string $definitionString;
30+
private string $definitionString;
2331

2432
/**
2533
* @param mixed $middlewareDefinition
@@ -29,13 +37,9 @@ public function __construct($middlewareDefinition)
2937
$this->definition = $middlewareDefinition;
3038
$this->definitionString = $this->convertDefinitionToString($middlewareDefinition);
3139

32-
$message = 'Parameter should be either PSR middleware class name or a callable.';
33-
34-
if ($this->definitionString !== null) {
35-
$message .= ' Got ' . $this->definitionString . '.';
36-
}
37-
38-
parent::__construct($message);
40+
parent::__construct(
41+
'Parameter should be either PSR middleware class name or a callable. Got ' . $this->definitionString . '.'
42+
);
3943
}
4044

4145
public function getName(): string
@@ -45,15 +49,13 @@ public function getName(): string
4549

4650
public function getSolution(): ?string
4751
{
48-
$solution = [];
49-
50-
if ($this->definitionString !== null) {
51-
$solution[] = <<<SOLUTION
52+
$solution = [
53+
<<<SOLUTION
5254
## Got definition value
5355
5456
`{$this->definitionString}`
55-
SOLUTION;
56-
}
57+
SOLUTION
58+
];
5759

5860
$suggestion = $this->generateSuggestion();
5961
if ($suggestion !== null) {
@@ -64,11 +66,48 @@ public function getSolution(): ?string
6466
$solution[] = <<<SOLUTION
6567
## Middleware definition examples
6668
67-
- PSR middleware class name: `Yiisoft\Session\SessionMiddleware::class`.
68-
- Action in controller: `[App\Backend\UserController::class, 'index']`.
69+
PSR middleware class name:
70+
71+
```php
72+
Yiisoft\Session\SessionMiddleware::class
73+
```
74+
75+
PSR middleware array definition:
76+
77+
```php
78+
[
79+
'class' => MyMiddleware::class,
80+
'__construct()' => [
81+
'someVar' => 42,
82+
],
83+
]
84+
```
85+
86+
Closure that returns `ResponseInterface`:
87+
88+
```php
89+
static function (): ResponseInterface {
90+
return new Response(418);
91+
},
92+
```
93+
94+
Closure that returns `MiddlewareInterface`:
95+
96+
```php
97+
static function (): MiddlewareInterface {
98+
return new TestMiddleware();
99+
}
100+
```
101+
102+
Action in controller:
103+
104+
```php
105+
[App\Backend\UserController::class, 'index']
106+
```
69107
70108
## Related links
71109
110+
- [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition)
72111
- [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php)
73112
SOLUTION;
74113

@@ -107,6 +146,27 @@ public function {$this->definition[1]}(): ResponseInterface
107146
);
108147
}
109148

149+
if (is_array($this->definition)) {
150+
try {
151+
DefinitionValidator::validateArrayDefinition($this->definition);
152+
} catch (InvalidConfigException $e) {
153+
return <<<SOLUTION
154+
You may have an error in array definition. Array definition validation result:
155+
156+
```
157+
{$e->getMessage()}
158+
```
159+
SOLUTION;
160+
}
161+
162+
/** @psalm-suppress MixedArgument In valid array definition element "class" always is string */
163+
return sprintf(
164+
'Array definition is valid, class `%s` exists, but does not implement `%s`.',
165+
$this->definition['class'],
166+
MiddlewareInterface::class
167+
);
168+
}
169+
110170
return null;
111171
}
112172

@@ -142,7 +202,7 @@ private function isControllerWithNonExistAction(): bool
142202
/**
143203
* @param mixed $middlewareDefinition
144204
*/
145-
private function convertDefinitionToString($middlewareDefinition): ?string
205+
private function convertDefinitionToString($middlewareDefinition): string
146206
{
147207
if (is_object($middlewareDefinition)) {
148208
return 'an instance of "' . get_class($middlewareDefinition) . '"';
@@ -153,30 +213,42 @@ private function convertDefinitionToString($middlewareDefinition): ?string
153213
}
154214

155215
if (is_array($middlewareDefinition)) {
156-
$items = $middlewareDefinition;
157-
foreach ($middlewareDefinition as $item) {
158-
if (!is_string($item)) {
159-
return null;
160-
}
216+
$items = [];
217+
/** @var mixed $value */
218+
foreach (array_slice($middlewareDefinition, 0, 2) as $key => $value) {
219+
$items[] = (is_string($key) ? '"' . $key . '" => ' : '') . $this->convertToString($value);
161220
}
162-
array_walk(
163-
$items,
164-
/**
165-
* @param mixed $item
166-
* @psalm-param array-key $key
167-
*/
168-
static function (&$item, $key) {
169-
$item = (string)$item;
170-
$item = '"' . $item . '"';
171-
if (is_string($key)) {
172-
$item = '"' . $key . '" => ' . $item;
173-
}
174-
}
175-
);
176-
/** @var string[] $items */
177-
return '[' . implode(', ', $items) . ']';
221+
return '[' . implode(', ', $items) . (count($middlewareDefinition) > 2 ? ', ...' : '') . ']';
178222
}
179223

180-
return null;
224+
return $this->convertToString($middlewareDefinition);
225+
}
226+
227+
/**
228+
* @param mixed $value
229+
*/
230+
private function convertToString($value): string
231+
{
232+
if (is_string($value)) {
233+
return '"' . $value . '"';
234+
}
235+
236+
if (is_int($value) || is_float($value)) {
237+
return (string) $value;
238+
}
239+
240+
if (is_bool($value)) {
241+
return $value ? 'true' : 'false';
242+
}
243+
244+
if ($value === null) {
245+
return 'null';
246+
}
247+
248+
if (is_object($value)) {
249+
return get_class($value);
250+
}
251+
252+
return gettype($value);
181253
}
182254
}

src/MiddlewareDispatcher.php

+1
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function dispatch(
6262
* - A controller handler action in format `[TestController::class, 'index']`. `TestController` instance will
6363
* be created and `index()` method will be executed.
6464
* - A function returning a middleware. The middleware returned will be executed.
65+
* - An array definition of middleware ({@link https://github.com/yiisoft/definitions#arraydefinition}).
6566
*
6667
* For handler action and callable
6768
* typed parameters are automatically injected using dependency injection container.

0 commit comments

Comments
 (0)