Passed
Pull Request — master (#219)
by Alexander
02:07
created

MiddlewareDispatcher::addMiddleware()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 1
dl 0
loc 11
ccs 5
cts 6
cp 0.8333
crap 3.0416
rs 10
c 0
b 0
f 0
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\Yii\Web\Middleware\Callback;
12
13
/**
14
 * MiddlewareDispatcher
15
 */
16
final class MiddlewareDispatcher
17
{
18
    /**
19
     * @var MiddlewareInterface[]
20
     */
21
    private array $middlewares = [];
22
23
    private RequestHandlerInterface $nextHandler;
24
    private ContainerInterface $container;
25
26
    /**
27
     * Contains a chain of middleware wrapped in handlers.
28
     * Each handler points to the handler of middleware that will be processed next.
29
     * @var RequestHandlerInterface|null stack of middleware
30
     */
31
    private ?RequestHandlerInterface $stack = null;
32
33
    public function __construct(
34 3
        array $middlewares,
35
        ContainerInterface $container,
36
        RequestHandlerInterface $nextHandler = null
37
    ) {
38
        if ($middlewares === []) {
39 3
            throw new \InvalidArgumentException('Middlewares should be defined.');
40 1
        }
41
42
        $this->container = $container;
43 3
44
        for ($i = count($middlewares) - 1; $i >= 0; $i--) {
45 3
            $this->addMiddleware($middlewares[$i]);
46 3
        }
47
48
        $responseFactory = $container->get(ResponseFactoryInterface::class);
49 3
50
        $this->nextHandler = $nextHandler ?? new NotFoundHandler($responseFactory);
51 3
    }
52
53
    private function addCallable(callable $callback): void
54
    {
55
        array_unshift($this->middlewares, new Callback($callback, $this->container));
56
    }
57
58
    /**
59 3
     * @param callable|MiddlewareInterface $middleware
60
     * @return self
61 3
     */
62
    public function addMiddleware($middleware): self
63 3
    {
64 3
        if (is_callable($middleware)) {
65
            $this->addCallable($middleware);
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...spatcher::addCallable() 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

65
            $this->addCallable(/** @scrutinizer ignore-type */ $middleware);
Loading history...
66 1
        } elseif ($middleware instanceof MiddlewareInterface) {
67
            array_unshift($this->middlewares, $middleware);
68
        } else {
69
            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

69
            throw new \InvalidArgumentException('Middleware should be either callable or MiddlewareInterface instance. ' . get_class(/** @scrutinizer ignore-type */ $middleware) . ' given.');
Loading history...
70 1
        }
71
72 1
        return $this;
73
    }
74
75
    public function dispatch(ServerRequestInterface $request): ResponseInterface
76 1
    {
77
        return $this->process($request, $this->nextHandler);
78
    }
79
80
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
81
    {
82
        if ($this->stack === null) {
83
            for ($i = count($this->middlewares) - 1; $i >= 0; $i--) {
84
                $handler = $this->wrap($this->middlewares[$i], $handler);
85
            }
86
            $this->stack = $handler;
87
        }
88
89
        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

89
        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...
90 1
    }
91
92 1
    /**
93
     * Wraps handler by middlewares
94
     */
95
    private function wrap(MiddlewareInterface $middleware, RequestHandlerInterface $handler): RequestHandlerInterface
96
    {
97
        return new class($middleware, $handler) implements RequestHandlerInterface {
98
            private MiddlewareInterface $middleware;
99
            private RequestHandlerInterface $handler;
100
101
            public function __construct(MiddlewareInterface $middleware, RequestHandlerInterface $handler)
102
            {
103
                $this->middleware = $middleware;
104
                $this->handler = $handler;
105
            }
106
107
            public function handle(ServerRequestInterface $request): ResponseInterface
108
            {
109
                return $this->middleware->process($request, $this->handler);
110
            }
111
        };
112
    }
113
}
114