Passed
Push — master ( 8df7ad...310ad9 )
by Divine Niiquaye
07:56
created

MiddlewareTrait::getMiddlewareHash()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 5
nc 3
nop 1
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 5
rs 9.6111
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of Flight Routing.
7
 *
8
 * PHP version 7.1 and above required
9
 *
10
 * @author    Divine Niiquaye Ibok <[email protected]>
11
 * @copyright 2019 Biurad Group (https://biurad.com/)
12
 * @license   https://opensource.org/licenses/BSD-3-Clause License
13
 *
14
 * For the full copyright and license information, please view the LICENSE
15
 * file that was distributed with this source code.
16
 */
17
18
namespace Flight\Routing\Traits;
19
20
use Closure;
21
use Flight\Routing\Exceptions\DuplicateRouteException;
22
use Flight\Routing\Exceptions\InvalidMiddlewareException;
23
use Laminas\Stratigility\Middleware\CallableMiddlewareDecorator;
24
use Laminas\Stratigility\Middleware\RequestHandlerMiddleware;
25
use Laminas\Stratigility\MiddlewarePipe;
26
use Psr\Container\NotFoundExceptionInterface;
27
use Psr\Http\Server\MiddlewareInterface;
28
use Psr\Http\Server\RequestHandlerInterface;
29
30
/**
31
 * Provides ability to manage set of middleware.
32
 */
33
trait MiddlewareTrait
34
{
35
    /**
36
     * Set of middleware to be applied for every request.
37
     *
38
     * @var array<int|string,mixed>
39
     */
40
    protected $middlewares = [];
41
42
    /**
43
     * Set of route middleware to be used in $middlewares
44
     * Stack, if string name is equal to a given middleware.
45
     *
46
     * @var array<int|string,mixed>
47
     */
48
    protected $nameMiddlewares = [];
49
50
    /**
51
     * Add new middleware(s) at the end of chain.
52
     *
53
     * Example (in bootstrap):
54
     * $this->addMiddleware(new ProxyMiddleware());
55
     *
56
     * @param array<string,mixed>|callable|MiddlewareInterface|RequestHandlerInterface|string ...$middlewares
57
     *
58
     * @throws DuplicateRouteException
59
     */
60 46
    public function addMiddleware(...$middlewares): void
61
    {
62 46
        foreach ($middlewares as $middleware) {
63 32
            if (\is_array($middleware) && !\is_callable($middleware)) {
64 5
                $this->addRecursiveMiddleware($middleware);
65
66 5
                continue;
67
            }
68
69 30
            $hash = $this->getMiddlewareHash($middleware);
70
71 30
            if (isset($this->middlewares[$hash])) {
72 1
                throw new DuplicateRouteException(\sprintf('A middleware with the hash "%s" already exists.', $hash));
73
            }
74
75 30
            $this->middlewares[$hash] = $middleware;
76
        }
77 46
    }
78
79
    /**
80
     * Create a middleware pipeline from an array of middleware.
81
     *
82
     * Each item is passed to prepare() before being passed to the
83
     * MiddlewarePipe instance the method returns.
84
     *
85
     * @throws InvalidMiddlewareException if middleware has not one of
86
     *                                    the specified types
87
     *
88
     * @return MiddlewarePipe
89
     */
90 26
    public function pipeline(): MiddlewarePipe
91
    {
92 26
        $pipeline    = new MiddlewarePipe();
93 26
        $middlewares = $this->getMiddlewares();
94
95 26
        foreach ($middlewares as $middleware) {
96 26
            $pipeline->pipe($this->prepare($middleware));
97
        }
98
99 23
        return $pipeline;
100
    }
101
102
    /**
103
     * Gets the middlewares from stack
104
     *
105
     * @return array<int,MiddlewareInterface|string>
106
     */
107 42
    public function getMiddlewares(): array
108
    {
109 42
        return \array_values($this->middlewares);
110
    }
111
112
    /**
113
     * @param array<int|string,mixed> $middlewares
114
     */
115 5
    protected function addRecursiveMiddleware(array $middlewares): void
116
    {
117 5
        foreach ($middlewares as $index => $middleware) {
118 5
            if (\is_string($index)) {
119 4
                $this->nameMiddlewares[$index] = $middleware;
120
121 4
                continue;
122
            }
123
124 1
            $this->addMiddleware($middleware);
125
        }
126 5
    }
127
128
    /**
129
     * Add a new middleware to the stack.
130
     *
131
     * Middleware are organized as a stack. That means middleware
132
     * that have been added before will be executed after the newly
133
     * added one (last in, first out).
134
     *
135
     * @param mixed $middleware
136
     *
137
     * @throws InvalidMiddlewareException if argument is not one of
138
     *                                    the specified types
139
     *
140
     * @return MiddlewareInterface
141
     */
142 26
    protected function prepare($middleware): MiddlewareInterface
143
    {
144 26
        $container = $this->resolver->getContainer();
145
146 26
        if (\is_string($middleware) && \array_key_exists($middleware, $this->nameMiddlewares)) {
147 1
            $middleware = $this->nameMiddlewares[$middleware];
148
        }
149
150 26
        if (\is_string($middleware) && null !== $container) {
151
            try {
152 3
                $middleware = $container->get($middleware);
153 1
            } catch (NotFoundExceptionInterface $e) {
154
                // ... handled at the end
155
            }
156
        }
157
158 26
        if (\is_string($middleware) && \class_exists($middleware)) {
159 5
            $middleware = new $middleware();
160
        }
161
162 26
        if ($middleware instanceof RequestHandlerInterface) {
163 1
            return new RequestHandlerMiddleware($middleware);
164
        }
165
166 26
        if (\is_callable($middleware)) {
167 2
            return new CallableMiddlewareDecorator($middleware);
168
        }
169
170 26
        if (!$middleware instanceof MiddlewareInterface) {
171 3
            throw InvalidMiddlewareException::forMiddleware($middleware);
172
        }
173
174 24
        return $middleware;
175
    }
176
177
    /**
178
     * @param mixed $middleware
179
     *
180
     * @return string
181
     */
182 30
    private function getMiddlewareHash($middleware): string
183
    {
184 30
        if ($middleware instanceof Closure || \is_object($middleware)) {
185 28
            return \spl_object_hash($middleware);
186
        }
187
188 12
        if (\is_callable($middleware) && \count($middleware) === 2) {
189 2
            return $middleware[1];
190
        }
191
192 12
        return \md5($middleware);
193
    }
194
}
195