Passed
Pull Request — master (#34)
by Aleksei
02:17 queued 12s
created

SquashedMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 6
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Middleware\Dispatcher;
6
7
use Generator;
8
use Iterator;
9
use Psr\EventDispatcher\EventDispatcherInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\MiddlewareInterface;
13
use Psr\Http\Server\RequestHandlerInterface;
14
use Yiisoft\Middleware\Dispatcher\Event\AfterMiddleware;
15
use Yiisoft\Middleware\Dispatcher\Event\BeforeMiddleware;
16
use Yiisoft\Middleware\Dispatcher\Exception\InvalidMiddlewareDefinitionException;
17
18
final class SquashedMiddleware implements MiddlewareInterface
19
{
20
    private ?RequestHandlerInterface $handler;
21
22 7
    private function __construct(
23
        iterable $middlewares,
24
        ?MiddlewareFactoryInterface $factory = null,
25
        ?EventDispatcherInterface $dispatcher = null
26
    ) {
27 7
        $this->handler = $this->createHandler($this->iterateMiddlewares($middlewares, $factory), $dispatcher);
28 7
    }
29
30
    /**
31
     * @param iterable $middlewares Middlewares to squash. It can be definitions for the MiddlewareFactoryInterface in
32
     * case when the $factory parameter specified.
33
     * @param MiddlewareFactoryInterface|null $factory Specify this parameter for middleware definitions resolving.
34
     * @param EventDispatcherInterface|null $dispatcher Specify this parameter if you need to listen related events:
35
     * - {@see \Yiisoft\Middleware\Dispatcher\Event\AfterMiddleware}
36
     * - {@see \Yiisoft\Middleware\Dispatcher\Event\BeforeMiddleware}
37
     *
38
     * @return static
39
     */
40 7
    public static function create(
41
        iterable $middlewares,
42
        ?MiddlewareFactoryInterface $factory = null,
43
        ?EventDispatcherInterface $dispatcher = null
44
    ): self {
45 7
        return new self($middlewares, $factory, $dispatcher);
46
    }
47
48 7
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
49
    {
50 7
        return $this->handler->withRequestHandler($handler)->handle($request);
0 ignored issues
show
Bug introduced by
The method withRequestHandler() 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

50
        return $this->handler->/** @scrutinizer ignore-call */ withRequestHandler($handler)->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...
Bug introduced by
The method withRequestHandler() does not exist on Psr\Http\Server\RequestHandlerInterface. It seems like you code against a sub-type of Psr\Http\Server\RequestHandlerInterface such as anonymous//src/SquashedMiddleware.php$0. ( Ignorable by Annotation )

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

50
        return $this->handler->/** @scrutinizer ignore-call */ withRequestHandler($handler)->handle($request);
Loading history...
51
    }
52
53
    /**
54
     * @psalm-param Iterator<mixed, MiddlewareInterface> $iterator
55
     */
56 7
    private function createHandler(Iterator $iterator, ?EventDispatcherInterface $dispatcher): RequestHandlerInterface
57
    {
58 7
        return new class(null, $iterator, $dispatcher) implements RequestHandlerInterface {
59
            private ?self $root;
60
            private ?self $nextHandler = null;
61
62
            /** @var Iterator<mixed, MiddlewareInterface>|null  */
63
            private ?Iterator $iterator;
64
            private ?MiddlewareInterface $middleware = null;
65
            private ?EventDispatcherInterface $dispatcher;
66
            private RequestHandlerInterface $fallbackHandler;
67
68
            public function __clone()
69
            {
70 7
                if ($this->root !== null) {
71
                    return;
72
                }
73 7
                $current = $this;
74 7
                while ($current->nextHandler !== null) {
75
                    $current->nextHandler = clone $current->nextHandler;
76
                    $current->nextHandler->root = $this;
77
                    $current = $current->nextHandler;
78
                }
79 7
            }
80
81
            final public function withRequestHandler(RequestHandlerInterface $handler): self
82
            {
83 7
                $new = clone $this;
84 7
                $new->fallbackHandler = $handler;
85 7
                return $new;
86
            }
87
88
            /**
89
             * @psalm-param Iterator<mixed, MiddlewareInterface> $iterator
90
             */
91
            public function __construct(?self $root, Iterator $iterator, ?EventDispatcherInterface $dispatcher)
92
            {
93 7
                $this->iterator = $iterator;
94 7
                $this->dispatcher = $dispatcher;
95 7
                $this->root = $root;
96 7
            }
97
98
            /**
99
             * @psalm-suppress PossiblyNullReference
100
             * @psalm-suppress PossiblyNullArgument
101
             */
102
            final public function handle(ServerRequestInterface $request): ResponseInterface
103
            {
104
                // If Middleware is not cached
105 7
                if ($this->middleware === null) {
106 7
                    $this->middleware = $this->iterator->current();
0 ignored issues
show
Bug introduced by
The method current() 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

106
                    /** @scrutinizer ignore-call */ 
107
                    $this->middleware = $this->iterator->current();

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...
107 7
                    $this->iterator->next();
108 7
                    if ($this->iterator->valid()) {
109 3
                        $this->nextHandler = new self($this->root ?? $this, $this->iterator, $this->dispatcher);
0 ignored issues
show
Bug introduced by
It seems like $this->iterator can also be of type null; however, parameter $iterator of anonymous//src/SquashedM...re.php$0::__construct() does only seem to accept Iterator, 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

109
                        $this->nextHandler = new self($this->root ?? $this, /** @scrutinizer ignore-type */ $this->iterator, $this->dispatcher);
Loading history...
110
                    }
111 7
                    $this->iterator = null;
112
                }
113 7
                return $this->processMiddleware($request);
114
            }
115
116
            private function processMiddleware(ServerRequestInterface $request): ResponseInterface
117
            {
118 7
                $nextHandler = $this->nextHandler ?? ($this->root ?? $this)->fallbackHandler;
119 7
                if ($this->middleware === null) {
120 1
                    return $nextHandler->handle($request);
121
                }
122
                // If the event dispatcher not exists
123 7
                if ($this->dispatcher === null) {
124 5
                    return $this->middleware->process($request, $nextHandler);
125
                }
126 2
                $this->dispatcher->dispatch(new BeforeMiddleware($this->middleware, $request));
127
                try {
128 2
                    return $response = $this->middleware->process($request, $nextHandler);
129
                } finally {
130 2
                    $this->dispatcher->dispatch(new AfterMiddleware($this->middleware, $response ?? null));
131
                }
132
            }
133
134
            public function __destruct()
135
            {
136 6
                $this->root = null;
137 6
                $this->nextHandler = null;
138 6
            }
139
        };
140
    }
141
142
    /**
143
     * @psalm-param iterable<mixed, mixed> $middlewares
144
     * @psalm-return Generator<int, MiddlewareInterface, mixed, RequestHandlerInterface>
145
     */
146 7
    private function iterateMiddlewares(iterable $middlewares, ?MiddlewareFactoryInterface $factory): Generator
147
    {
148
        /** @var mixed $middleware */
149 7
        foreach ($middlewares as $middleware) {
150 7
            if ($middleware instanceof MiddlewareInterface) {
151
                yield $middleware;
152
            }
153 7
            if ($factory === null) {
154
                // todo: create better and friendly exception
155
                throw new InvalidMiddlewareDefinitionException(
156
                    $middleware,
157
                    'Invalid middleware. If you pass middleware definition then pass middleware factory also.'
158
                );
159
            }
160 7
            yield $factory->create($middleware);
161
        }
162 6
    }
163
164 6
    public function __destruct()
165
    {
166 6
        $this->handler->__destruct();
0 ignored issues
show
Bug introduced by
The method __destruct() does not exist on Psr\Http\Server\RequestHandlerInterface. It seems like you code against a sub-type of Psr\Http\Server\RequestHandlerInterface such as anonymous//src/SquashedMiddleware.php$0. ( Ignorable by Annotation )

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

166
        $this->handler->/** @scrutinizer ignore-call */ 
167
                        __destruct();
Loading history...
167 6
        $this->handler = null;
168 6
    }
169
}
170