Passed
Push — main ( 2e91a7...db810f )
by Dimitri
03:15
created

Middleware::insertBefore()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 11
c 0
b 0
f 0
nc 12
nop 3
dl 0
loc 21
rs 8.8333
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Http;
13
14
use BlitzPHP\Loader\Services;
15
use BlitzPHP\Middlewares\BaseMiddleware;
16
use BlitzPHP\Middlewares\BodyParser;
17
use BlitzPHP\Middlewares\Cors;
18
use LogicException;
19
use Psr\Http\Message\ResponseInterface;
20
use Psr\Http\Message\ServerRequestInterface;
21
use Psr\Http\Server\MiddlewareInterface;
22
use Psr\Http\Server\RequestHandlerInterface;
23
24
class Middleware implements RequestHandlerInterface
25
{
26
    /**
27
     * Middlewares a executer pour la requete courante
28
     */
29
    protected array $middlewares = [];
30
    
31
    /**
32
     * Index du middleware actuellement executer
33
     */
34
    protected int $index = 0;
35
36
    /**
37
     * Aliases des middlewares
38
     */
39
    protected array $aliases = [
40
        'body-parser' => BodyParser::class,
41
        'cors'        => Cors::class,
42
    ];
43
44
    /**
45
     * Contructor
46
     */
47
    public function __construct(protected Response $response, protected string $path)
48
    {
49
    }
50
51
    /**
52
     * Ajoute un alias de middleware
53
     */
54
    public function aliases(array $aliases): self
55
    {
56
        $this->aliases = array_merge($this->aliases, $aliases);
57
58
        return $this;
59
    }
60
61
    /**
62
     * Ajoute un middleware a la chaine d'execution
63
     *
64
     * @param array|callable|object|string $middlewares
65
     */
66
    public function add($middlewares, array $options = []): self
67
    {
68
        if (! is_array($middlewares)) {
69
            $middlewares = [$middlewares];
70
        }
71
72
        
73
        foreach ($middlewares as $middleware) {
74
            $this->append($middleware, $options);
75
        }
76
        
77
        return $this;
78
    }
79
80
    /**
81
     * Ajoute un middleware en bout de chaine
82
     *
83
     * @param callable|object|string $middleware
84
     */
85
    public function append($middleware, array $options = []): self
86
    {
87
        $middleware          = $this->makeMiddleware($middleware);
88
        $this->middlewares[] = compact('middleware', 'options');
89
90
        return $this;
91
    }
92
93
    /**
94
     * Ajoute un middleware en debut de chaine
95
     *
96
     * @param callable|object|string $middleware
97
     */
98
    public function prepend($middleware, array $options = []): self
99
    {
100
        $middleware = $this->makeMiddleware($middleware);
101
        array_unshift($this->middlewares, compact('middleware', 'options'));
102
103
        return $this;
104
    }
105
106
    /**
107
     * insert un middleware a une position donnee
108
     *
109
     * @param callable|object|string $middleware
110
     *
111
     * @alias insertAt
112
     */
113
    public function insert(int $index, $middleware, array $options = []): self
114
    {
115
        return $this->insertAt($index, $middleware, $options);
116
    }
117
118
    /**
119
     * Insérez un middleware appelable à un index spécifique.
120
     *
121
     * Si l'index existe déjà, le nouvel appelable sera inséré,
122
     * et l'élément existant sera décalé d'un indice supérieur.
123
     *
124
     * @param int                    $index      La position où le middleware doit être insérer.
125
     * @param callable|object|string $middleware Le middleware à inserer.
126
     */
127
    public function insertAt(int $index, $middleware, array $options = []): self
128
    {
129
        $middleware = $this->makeMiddleware($middleware);
130
        array_splice($this->middlewares, $index, 0, compact('middleware', 'options'));
131
132
        return $this;
133
    }
134
135
    /**
136
     * Insérez un objet middleware avant la première classe correspondante.
137
     *
138
     * Trouve l'index du premier middleware qui correspond à la classe fournie,
139
     * et insère l'appelable fourni avant.
140
     *
141
     * @param string                 $class      Le nom de classe pour insérer le middleware avant.
142
     * @param callable|object|string $middleware Le middleware à inserer.
143
     *
144
     * @throws LogicException Si le middleware à insérer avant n'est pas trouvé.
145
     */
146
    public function insertBefore(string $class, $middleware, array $options = []): self
147
    {
148
        $found = false;
149
        $i     = 0;
150
151
        if (array_key_exists($class, $this->aliases)) {
152
            $class = $this->aliases[$class];
153
        }
154
155
        foreach ($this->middlewares as $i => $object) {
156
            if ((is_string($object) && $object === $class) || is_a($object, $class)) {
157
                $found = true;
158
                break;
159
            }
160
        }
161
162
        if ($found) {
163
            return $this->insertAt($i, $middleware, $options);
164
        }
165
166
        throw new LogicException(sprintf("No middleware matching '%s' could be found.", $class));
167
    }
168
169
    /**
170
     * Insérez un objet middleware après la première classe correspondante.
171
     *
172
     * Trouve l'index du premier middleware qui correspond à la classe fournie,
173
     * et insère le callback fourni après celui-ci. Si la classe n'est pas trouvée,
174
     * cette méthode se comportera comme add().
175
     *
176
     * @param string                 $class      Le nom de classe pour insérer le middleware après.
177
     * @param callable|object|string $middleware Le middleware à inserer.
178
     */
179
    public function insertAfter(string $class, $middleware, array $options = []): self
180
    {
181
        $found = false;
182
        $i     = 0;
183
184
        if (array_key_exists($class, $this->aliases)) {
185
            $class = $this->aliases[$class];
186
        }
187
188
        foreach ($this->middlewares as $i => $object) {
189
            if ((is_string($object) && $object === $class) || is_a($object, $class)) {
190
                $found = true;
191
                break;
192
            }
193
        }
194
195
        if ($found) {
196
            return $this->insertAt($i + 1, $middleware, $options);
197
        }
198
199
        return $this->add($middleware, $options);
200
    }
201
202
    /**
203
     * Execution du middleware
204
     */
205
    public function handle(ServerRequestInterface $request): ResponseInterface
206
    {
207
        ['middleware' => $middleware, 'options' => $options] = $this->getMiddleware();
208
209
        if (empty($middleware)) {
210
            return $this->response;
211
        }
212
213
        if (isset($options['except']) && $this->pathApplies($this->path, $options['except'])) {
214
            return $this->handle($request);
215
        }
216
217
        unset($options['except']);
218
219
        if (is_callable($middleware)) {
220
            return $middleware($request, $this->response, [$this, 'handle']);
221
        }
222
223
        if ($middleware instanceof MiddlewareInterface) {
224
            if ($middleware instanceof BaseMiddleware) {
225
                $middleware = $middleware->init($options + ['path' => $this->path]);
226
            }
227
228
            return $middleware->process($request, $this);
229
        }
230
231
        return $this->response;
232
    }
233
234
    /**
235
     * Fabrique un middleware
236
     *
237
     * @param callable|object|string $middleware
238
     *
239
     * @return callable|object
240
     */
241
    private function makeMiddleware($middleware)
242
    {
243
        if (is_string($middleware)) {
244
            if (array_key_exists($middleware, $this->aliases)) {
245
                $middleware = $this->aliases[$middleware];
246
            }
247
            
248
            $middleware = Services::container()->get($middleware);
249
        }
250
        
251
        return $middleware;
252
    }
253
254
    /**
255
     * Recuperation du middleware actuel
256
     */
257
    private function getMiddleware(): array
258
    {
259
        $middleware = [];
260
261
        if (isset($this->middlewares[$this->index])) {
262
            $middleware = $this->middlewares[$this->index];
263
        }
264
265
        $this->index++;
266
267
        return $middleware;
268
    }
269
270
    /**
271
     * Check paths for match for URI
272
     */
273
    private function pathApplies(string $uri, array|string $paths): bool
274
    {
275
        // empty path matches all
276
        if (empty($paths)) {
277
            return true;
278
        }
279
280
        // make sure the paths are iterable
281
        if (is_string($paths)) {
0 ignored issues
show
introduced by
The condition is_string($paths) is always false.
Loading history...
282
            $paths = [$paths];
283
        }
284
285
        // treat each paths as pseudo-regex
286
        foreach ($paths as $path) {
287
            // need to escape path separators
288
            $path = str_replace('/', '\/', trim($path, '/ '));
289
            // need to make pseudo wildcard real
290
            $path = strtolower(str_replace('*', '.*', $path));
291
            // Does this rule apply here?
292
            if (preg_match('#^' . $path . '$#', $uri, $match) === 1) {
293
                return true;
294
            }
295
        }
296
297
        return false;
298
    }
299
}
300