Passed
Pull Request — master (#219)
by Dmitriy
03:37
created

MiddlewareDispatcher::addMiddleware()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
c 1
b 0
f 0
dl 0
loc 13
ccs 4
cts 6
cp 0.6667
crap 3.3332
rs 10
eloc 6
nc 4
nop 1
1
<?php
2
3
namespace Yiisoft\Yii\Web;
4
5
use Psr\Container\ContainerInterface;
6
use Psr\Http\Message\ResponseFactoryInterface;
7
use Psr\Http\Message\ResponseInterface;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Server\MiddlewareInterface;
10
use Psr\Http\Server\RequestHandlerInterface;
11
use Yiisoft\Injector\Injector;
12
use Yiisoft\Yii\Web\Middleware\Callback;
13
14
/**
15
 * MiddlewareDispatcher
16
 */
17
final class MiddlewareDispatcher implements MiddlewareInterface
18
{
19
    /**
20
     * @var MiddlewareInterface[]
21
     */
22
    private array $middlewares = [];
23
24
    private RequestHandlerInterface $nextHandler;
25
    private ContainerInterface $container;
26
27
    /**
28
     * Contains a chain of middleware wrapped in handlers.
29
     * Each handler points to the handler of middleware that will be processed next.
30
     * @var RequestHandlerInterface|null stack of middleware
31
     */
32
    private ?RequestHandlerInterface $stack = null;
33
34 3
    public function __construct(
35
        ContainerInterface $container,
36
        RequestHandlerInterface $nextHandler = null
37
    ) {
38
        $this->container = $container;
39 3
40 1
        $responseFactory = $container->get(ResponseFactoryInterface::class);
41
42
        $this->nextHandler = $nextHandler ?? new NotFoundHandler($responseFactory);
43 3
    }
44
45 3
    /**
46 3
     * @param callable|MiddlewareInterface $middleware
47
     * @return self
48
     */
49 3
    public function addMiddleware($middleware): self
50
    {
51 3
        if (is_callable($middleware)) {
52
            $middleware = $this->getCallbackMiddleware($middleware, $this->container);
0 ignored issues
show
Bug introduced by
It seems like $middleware can also be of type Psr\Http\Server\MiddlewareInterface; however, parameter $callback of Yiisoft\Yii\Web\Middlewa...getCallbackMiddleware() does only seem to accept callable, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

52
            $middleware = $this->getCallbackMiddleware(/** @scrutinizer ignore-type */ $middleware, $this->container);
Loading history...
53
        }
54
55
        if (!$middleware instanceof MiddlewareInterface) {
56
            throw new \InvalidArgumentException('Middleware should be either callable or MiddlewareInterface instance. ' . get_class($middleware) . ' given.');
0 ignored issues
show
Bug introduced by
$middleware of type callable is incompatible with the type object expected by parameter $object of get_class(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

56
            throw new \InvalidArgumentException('Middleware should be either callable or MiddlewareInterface instance. ' . get_class(/** @scrutinizer ignore-type */ $middleware) . ' given.');
Loading history...
57
        }
58
59 3
        array_unshift($this->middlewares, $middleware);
60
61 3
        return $this;
62
    }
63 3
64 3
    public function dispatch(ServerRequestInterface $request): ResponseInterface
65
    {
66 1
        return $this->process($request, $this->nextHandler);
67
    }
68
69
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
70 1
    {
71
        if ($this->stack === null) {
72 1
            for ($i = count($this->middlewares) - 1; $i >= 0; $i--) {
73
                $handler = $this->wrap($this->middlewares[$i], $handler);
74
            }
75
            $this->stack = $handler;
76 1
        }
77
78
        return $this->stack->handle($request);
0 ignored issues
show
Bug introduced by
The method handle() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

78
        return $this->stack->/** @scrutinizer ignore-call */ handle($request);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
79
    }
80
81
    /**
82
     * Wraps handler by middlewares
83
     */
84
    private function wrap(MiddlewareInterface $middleware, RequestHandlerInterface $handler): RequestHandlerInterface
85
    {
86
        return new class($middleware, $handler) implements RequestHandlerInterface {
87
            private MiddlewareInterface $middleware;
88
            private RequestHandlerInterface $handler;
89
90 1
            public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $handler)
91
            {
92 1
                $this->middleware = $middleware;
93
                $this->handler = $handler;
94
            }
95
96
            public function handle(ServerRequestInterface $request): ResponseInterface
97
            {
98
                return $this->middleware->process($request, $this->handler);
99
            }
100
        };
101
    }
102
103
    private function getCallbackMiddleware(callable $callback, ContainerInterface $container)
104
    {
105
        return new class($callback, $container) implements MiddlewareInterface
106
        {
107
            /**
108
             * @var callable a PHP callback matching signature of [[MiddlewareInterface::process()]].
109
             */
110
            private $callback;
111
            private $container;
112
113
            public function __construct(callable $callback, ContainerInterface $container)
114
            {
115
                $this->callback = $callback;
116
                $this->container = $container;
117
            }
118
119
            public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
120
            {
121
                return (new Injector($this->container))->invoke($this->callback, [$request, $handler]);
122
            }
123
        };
124
    }
125
}
126