Skip to content

Commit

Permalink
Add array definition support (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
vjik authored Jun 21, 2022
1 parent 1188670 commit 5c417f3
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 74 deletions.
9 changes: 5 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
# Yii Middleware Dispatcher Change Log

## 2.0.2 under development
## 2.1.0 under development

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

## 2.0.1 February 14, 2022

- Chg #47: Add debug info to action wrapper (rustamwin)
- Chg #47: Add debug info to action wrapper (@rustamwin)

## 2.0.0 November 10, 2021

- Chg #43: Reverse middleware order in `withMiddlewares()` (rustamwin)
- Chg #43: Reverse middleware order in `withMiddlewares()` (@rustamwin)

## 1.0.0 November 08, 2021

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ In the above we have used a callback. Overall the following options are availabl
```
The middleware returned will be executed.
- A callback `function(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface`.
- An array definition (see [syntax](https://github.com/yiisoft/definitions#arraydefinition)) of middleware:
```php
[
'class' => MyMiddleware::class,
'__construct()' => [
'someVar' => 42,
],
]
```

For handler action and callable typed parameters are automatically injected using dependency injection container.
Current request and handler could be obtained by type-hinting for `ServerRequestInterface` and `RequestHandlerInterface`.
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"psr/http-message": "^1.0",
"psr/http-server-handler": "^1.0",
"psr/http-server-middleware": "^1.0",
"yiisoft/definitions": "^2.0",
"yiisoft/friendly-exception": "^1.1",
"yiisoft/injector": "^1.0"
},
Expand Down
150 changes: 111 additions & 39 deletions src/InvalidMiddlewareDefinitionException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@

use InvalidArgumentException;
use Psr\Http\Server\MiddlewareInterface;
use Yiisoft\Definitions\Exception\InvalidConfigException;
use Yiisoft\Definitions\Helpers\DefinitionValidator;
use Yiisoft\FriendlyException\FriendlyExceptionInterface;

use function array_slice;
use function count;
use function get_class;
use function gettype;
use function is_array;
use function is_bool;
use function is_float;
use function is_int;
use function is_object;
use function is_string;

Expand All @@ -19,7 +27,7 @@ final class InvalidMiddlewareDefinitionException extends InvalidArgumentExceptio
* @var mixed
*/
private $definition;
private ?string $definitionString;
private string $definitionString;

/**
* @param mixed $middlewareDefinition
Expand All @@ -29,13 +37,9 @@ public function __construct($middlewareDefinition)
$this->definition = $middlewareDefinition;
$this->definitionString = $this->convertDefinitionToString($middlewareDefinition);

$message = 'Parameter should be either PSR middleware class name or a callable.';

if ($this->definitionString !== null) {
$message .= ' Got ' . $this->definitionString . '.';
}

parent::__construct($message);
parent::__construct(
'Parameter should be either PSR middleware class name or a callable. Got ' . $this->definitionString . '.'
);
}

public function getName(): string
Expand All @@ -45,15 +49,13 @@ public function getName(): string

public function getSolution(): ?string
{
$solution = [];

if ($this->definitionString !== null) {
$solution[] = <<<SOLUTION
$solution = [
<<<SOLUTION
## Got definition value
`{$this->definitionString}`
SOLUTION;
}
SOLUTION
];

$suggestion = $this->generateSuggestion();
if ($suggestion !== null) {
Expand All @@ -64,11 +66,48 @@ public function getSolution(): ?string
$solution[] = <<<SOLUTION
## Middleware definition examples
- PSR middleware class name: `Yiisoft\Session\SessionMiddleware::class`.
- Action in controller: `[App\Backend\UserController::class, 'index']`.
PSR middleware class name:
```php
Yiisoft\Session\SessionMiddleware::class
```
PSR middleware array definition:
```php
[
'class' => MyMiddleware::class,
'__construct()' => [
'someVar' => 42,
],
]
```
Closure that returns `ResponseInterface`:
```php
static function (): ResponseInterface {
return new Response(418);
},
```
Closure that returns `MiddlewareInterface`:
```php
static function (): MiddlewareInterface {
return new TestMiddleware();
}
```
Action in controller:
```php
[App\Backend\UserController::class, 'index']
```
## Related links
- [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition)
- [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php)
SOLUTION;

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

if (is_array($this->definition)) {
try {
DefinitionValidator::validateArrayDefinition($this->definition);
} catch (InvalidConfigException $e) {
return <<<SOLUTION
You may have an error in array definition. Array definition validation result:
```
{$e->getMessage()}
```
SOLUTION;
}

/** @psalm-suppress MixedArgument In valid array definition element "class" always is string */
return sprintf(
'Array definition is valid, class `%s` exists, but does not implement `%s`.',
$this->definition['class'],
MiddlewareInterface::class
);
}

return null;
}

Expand Down Expand Up @@ -142,7 +202,7 @@ private function isControllerWithNonExistAction(): bool
/**
* @param mixed $middlewareDefinition
*/
private function convertDefinitionToString($middlewareDefinition): ?string
private function convertDefinitionToString($middlewareDefinition): string
{
if (is_object($middlewareDefinition)) {
return 'an instance of "' . get_class($middlewareDefinition) . '"';
Expand All @@ -153,30 +213,42 @@ private function convertDefinitionToString($middlewareDefinition): ?string
}

if (is_array($middlewareDefinition)) {
$items = $middlewareDefinition;
foreach ($middlewareDefinition as $item) {
if (!is_string($item)) {
return null;
}
$items = [];
/** @var mixed $value */
foreach (array_slice($middlewareDefinition, 0, 2) as $key => $value) {
$items[] = (is_string($key) ? '"' . $key . '" => ' : '') . $this->convertToString($value);
}
array_walk(
$items,
/**
* @param mixed $item
* @psalm-param array-key $key
*/
static function (&$item, $key) {
$item = (string)$item;
$item = '"' . $item . '"';
if (is_string($key)) {
$item = '"' . $key . '" => ' . $item;
}
}
);
/** @var string[] $items */
return '[' . implode(', ', $items) . ']';
return '[' . implode(', ', $items) . (count($middlewareDefinition) > 2 ? ', ...' : '') . ']';
}

return null;
return $this->convertToString($middlewareDefinition);
}

/**
* @param mixed $value
*/
private function convertToString($value): string
{
if (is_string($value)) {
return '"' . $value . '"';
}

if (is_int($value) || is_float($value)) {
return (string) $value;
}

if (is_bool($value)) {
return $value ? 'true' : 'false';
}

if ($value === null) {
return 'null';
}

if (is_object($value)) {
return get_class($value);
}

return gettype($value);
}
}
1 change: 1 addition & 0 deletions src/MiddlewareDispatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public function dispatch(
* - A controller handler action in format `[TestController::class, 'index']`. `TestController` instance will
* be created and `index()` method will be executed.
* - A function returning a middleware. The middleware returned will be executed.
* - An array definition of middleware ({@link https://github.com/yiisoft/definitions#arraydefinition}).
*
* For handler action and callable
* typed parameters are automatically injected using dependency injection container.
Expand Down
Loading

0 comments on commit 5c417f3

Please sign in to comment.