Runner.php$0 ➔ handle()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 7
cts 7
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
crap 3
1
<?php
2
declare(strict_types=1);
3
/**
4
 * Caridea
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7
 * use this file except in compliance with the License. You may obtain a copy of
8
 * the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15
 * License for the specific language governing permissions and limitations under
16
 * the License.
17
 *
18
 * @copyright 2017-2018 LibreWorks contributors
19
 * @license   Apache-2.0
20
 */
21
namespace Caridea\Dispatch;
22
23
use Psr\Http\Message\ServerRequestInterface;
24
use Psr\Http\Message\ResponseInterface;
25
use Psr\Http\Server\RequestHandlerInterface;
26
use Psr\Http\Server\MiddlewareInterface;
27
28
/**
29
 * Runs a request through the middleware.
30
 *
31
 * You can use this class itself as middleware, too. For example, you declare
32
 * this runner to be the second middleware of another runner. If this runner has
33
 * three middleware objects, it will participate in the containing runner like
34
 * this:
35
 *
36
 * ```
37
 * |→ Outer runner middleware 1
38
 * |  → Inner runner middleware 1
39
 * |    → Inner runner middleware 2
40
 * |      → Inner runner middleware 3
41
 * |        → Outer runner middleware 3
42
 * |        ← Outer runner middleware 3
43
 * |      ← Inner runner middleware 3
44
 * |    ← Inner runner middleware 2
45
 * |  ← Inner runner middleware 1
46
 * |← Outer runner middleware 1
47
 * ```
48
 *
49
 * @copyright 2017-2018 LibreWorks contributors
50
 * @license   Apache-2.0
51
 */
52
class Runner implements RequestHandlerInterface, MiddlewareInterface
53
{
54
    /**
55
     * @var \Psr\Http\Server\MiddlewareInterface[]
56
     */
57
    protected $middleware;
58
59
    /**
60
     * Creates a new Runner.
61
     *
62
     * @param iterable $middleware  Any traversable value containing MiddlewareInterface objects.
63
     * @param \Psr\Http\Message\ResponseInterface|null $response  Optional. The response for the innermost middleware to receive.
64
     * @throws \InvalidArgumentException if one of the items in `$middleware` is not a `MiddlewareInterface`
65
     * @throws \LengthException if no middleware are provided and no default `ResponseInterface` is provided
66
     */
67 7
    public function __construct(iterable $middleware, ?ResponseInterface $response = null)
68
    {
69 7
        $wares = [];
70 7
        foreach ($middleware as $mw) {
71 5
            if (!($mw instanceof MiddlewareInterface)) {
72 1
                throw new \InvalidArgumentException("Value must be an instance of MiddlewareInterface");
73
            }
74 4
            $wares[] = $mw;
75
        }
76 6
        if ($response !== null) {
77 4
            $wares[] = new Middleware\Prototype($response);
78
        }
79 6
        if (empty($wares)) {
80 1
            throw new \LengthException("You must specify at least one middleware or a ResponseInterface");
81
        }
82 5
        $this->middleware = array_reverse($wares);
83 5
    }
84
85
    /**
86
     * {@inheritDoc}
87
     *
88
     * @throws \UnderflowException if a middleware calls the provided handler and no middleware remain
89
     */
90 4
    public function handle(ServerRequestInterface $request): ResponseInterface
91
    {
92 4
        return $this->run()->handle($request);
93
    }
94
95
    /**
96
     * {@inheritDoc}
97
     *
98
     * In order for the `$handler` provided to be used, the innermost middleware
99
     * of this `Runner` needs to use the `RequestHandlerInterface` provided to
100
     * it. If you've supplied a `ResponseInterface` in this `Runner`'s
101
     * constructor, that isn't the case.
102
     */
103 1
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
104
    {
105 1
        return $this->run($handler)->handle($request);
106
    }
107
108
    /**
109
     * @param \Psr\Http\Server\RequestHandlerInterface|null $delegateHandler
110
     * @return \Psr\Http\Server\RequestHandlerInterface
111
     */
112
    protected function run($delegateHandler = null)
113
    {
114
        return new class($this->middleware, $delegateHandler) implements RequestHandlerInterface
115
        {
116
            /**
117
             * @var \Psr\Http\Server\MiddlewareInterface[]
118
             */
119
            private $toRun;
120
            /**
121
             * @var \Psr\Http\Server\RequestHandlerInterface|null
122
             */
123
            private $andFinally;
124
125 4
            public function __construct($reversedMiddleware, $delegateHandler)
126
            {
127 4
                $this->toRun = $reversedMiddleware;
128 4
                $this->andFinally = $delegateHandler;
129 4
            }
130
131 4
            public function handle(ServerRequestInterface $request): ResponseInterface
132
            {
133 4
                if (empty($this->toRun)) {
134 2
                    if ($this->andFinally !== null) {
135 1
                        return $this->andFinally->handle($request);
136
                    }
137 1
                    throw new \UnderflowException("No middleware remain");
138
                }
139 4
                $c = array_pop($this->toRun);
140 4
                return $c->process($request, $this);
141
            }
142
        };
143
    }
144
}
145