Passed
Push — main ( c6deb1...79ccdf )
by Dimitri
12:23
created

Middleware   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 283
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 81
dl 0
loc 283
rs 8.96
c 0
b 0
f 0
wmc 43

14 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
A append() 0 6 1
B insertBefore() 0 21 7
A pathApplies() 0 25 5
B handle() 0 31 8
A insertAt() 0 6 1
A insert() 0 3 1
A getMiddleware() 0 11 2
A prepend() 0 6 1
B insertAfter() 0 21 7
A makeMiddleware() 0 9 4
A alias() 0 3 1
A aliases() 0 5 1
A add() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like Middleware often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Middleware, and based on these observations, apply Extract Interface, too.

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\Container\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 alias(string $alias, callable|object|string $middleware): self
55
    {
56
		return $this->aliases([$alias => $middleware]);
57
    }
58
59
    /**
60
     * Ajoute des alias de middlewares
61
     */
62
    public function aliases(array $aliases): self
63
    {
64
        $this->aliases = array_merge($this->aliases, $aliases);
65
66
        return $this;
67
    }
68
69
    /**
70
     * Ajoute un middleware a la chaine d'execution
71
     *
72
     * @param array|callable|object|string $middlewares
73
     */
74
    public function add($middlewares, array $options = []): self
75
    {
76
        if (! is_array($middlewares)) {
77
            $middlewares = [$middlewares];
78
        }
79
80
        foreach ($middlewares as $middleware) {
81
            $this->append($middleware, $options);
82
        }
83
84
        return $this;
85
    }
86
87
    /**
88
     * Ajoute un middleware en bout de chaine
89
     *
90
     * @param callable|object|string $middleware
91
     */
92
    public function append($middleware, array $options = []): self
93
    {
94
        $middleware          = $this->makeMiddleware($middleware);
95
        $this->middlewares[] = compact('middleware', 'options');
96
97
        return $this;
98
    }
99
100
    /**
101
     * Ajoute un middleware en debut de chaine
102
     *
103
     * @param callable|object|string $middleware
104
     */
105
    public function prepend($middleware, array $options = []): self
106
    {
107
        $middleware = $this->makeMiddleware($middleware);
108
        array_unshift($this->middlewares, compact('middleware', 'options'));
109
110
        return $this;
111
    }
112
113
    /**
114
     * insert un middleware a une position donnee
115
     *
116
     * @param callable|object|string $middleware
117
     *
118
     * @alias insertAt
119
     */
120
    public function insert(int $index, $middleware, array $options = []): self
121
    {
122
        return $this->insertAt($index, $middleware, $options);
123
    }
124
125
    /**
126
     * Insérez un middleware appelable à un index spécifique.
127
     *
128
     * Si l'index existe déjà, le nouvel appelable sera inséré,
129
     * et l'élément existant sera décalé d'un indice supérieur.
130
     *
131
     * @param int                    $index      La position où le middleware doit être insérer.
132
     * @param callable|object|string $middleware Le middleware à inserer.
133
     */
134
    public function insertAt(int $index, $middleware, array $options = []): self
135
    {
136
        $middleware = $this->makeMiddleware($middleware);
137
        array_splice($this->middlewares, $index, 0, compact('middleware', 'options'));
138
139
        return $this;
140
    }
141
142
    /**
143
     * Insérez un objet middleware avant la première classe correspondante.
144
     *
145
     * Trouve l'index du premier middleware qui correspond à la classe fournie,
146
     * et insère l'appelable fourni avant.
147
     *
148
     * @param string                 $class      Le nom de classe pour insérer le middleware avant.
149
     * @param callable|object|string $middleware Le middleware à inserer.
150
     *
151
     * @throws LogicException Si le middleware à insérer avant n'est pas trouvé.
152
     */
153
    public function insertBefore(string $class, $middleware, array $options = []): self
154
    {
155
        $found = false;
156
        $i     = 0;
157
158
        if (array_key_exists($class, $this->aliases)) {
159
            $class = $this->aliases[$class];
160
        }
161
162
        foreach ($this->middlewares as $i => $object) {
163
            if ((is_string($object) && $object === $class) || is_a($object, $class)) {
164
                $found = true;
165
                break;
166
            }
167
        }
168
169
        if ($found) {
170
            return $this->insertAt($i, $middleware, $options);
171
        }
172
173
        throw new LogicException(sprintf("No middleware matching '%s' could be found.", $class));
174
    }
175
176
    /**
177
     * Insérez un objet middleware après la première classe correspondante.
178
     *
179
     * Trouve l'index du premier middleware qui correspond à la classe fournie,
180
     * et insère le callback fourni après celui-ci. Si la classe n'est pas trouvée,
181
     * cette méthode se comportera comme add().
182
     *
183
     * @param string                 $class      Le nom de classe pour insérer le middleware après.
184
     * @param callable|object|string $middleware Le middleware à inserer.
185
     */
186
    public function insertAfter(string $class, $middleware, array $options = []): self
187
    {
188
        $found = false;
189
        $i     = 0;
190
191
        if (array_key_exists($class, $this->aliases)) {
192
            $class = $this->aliases[$class];
193
        }
194
195
        foreach ($this->middlewares as $i => $object) {
196
            if ((is_string($object) && $object === $class) || is_a($object, $class)) {
197
                $found = true;
198
                break;
199
            }
200
        }
201
202
        if ($found) {
203
            return $this->insertAt($i + 1, $middleware, $options);
204
        }
205
206
        return $this->add($middleware, $options);
207
    }
208
209
    /**
210
     * Execution du middleware
211
     */
212
    public function handle(ServerRequestInterface $request): ResponseInterface
213
    {
214
		if (empty($processing = $this->getMiddleware())) {
215
			return $this->response;
216
		}
217
218
        ['middleware' => $middleware, 'options' => $options] = $processing;
219
220
        if (empty($middleware)) {
221
            return $this->response;
222
        }
223
224
        if (isset($options['except']) && $this->pathApplies($this->path, $options['except'])) {
225
            return $this->handle($request);
226
        }
227
228
        unset($options['except']);
229
230
        if (is_callable($middleware)) {
231
            return $middleware($request, $this->response, [$this, 'handle']);
232
        }
233
234
        if ($middleware instanceof MiddlewareInterface) {
235
            if ($middleware instanceof BaseMiddleware) {
236
                $middleware = $middleware->init($options + ['path' => $this->path]);
237
            }
238
239
            return $middleware->process($request, $this);
240
        }
241
242
        return $this->response;
243
    }
244
245
    /**
246
     * Fabrique un middleware
247
     *
248
     * @param callable|object|string $middleware
249
     *
250
     * @return callable|object
251
     */
252
    private function makeMiddleware($middleware)
253
    {
254
        if (is_string($middleware) && array_key_exists($middleware, $this->aliases)) {
255
			$middleware = $this->aliases[$middleware];
256
        }
257
		
258
		return is_string($middleware)
259
			? Services::container()->get($middleware)
260
			: $middleware;
261
    }
262
263
    /**
264
     * Recuperation du middleware actuel
265
     */
266
    private function getMiddleware(): array
267
    {
268
        $middleware = [];
269
270
        if (isset($this->middlewares[$this->index])) {
271
            $middleware = $this->middlewares[$this->index];
272
        }
273
274
        $this->index++;
275
276
        return $middleware;
277
    }
278
279
    /**
280
     * Check paths for match for URI
281
     */
282
    private function pathApplies(string $uri, array|string $paths): bool
283
    {
284
        // empty path matches all
285
        if (empty($paths)) {
286
            return true;
287
        }
288
289
        // make sure the paths are iterable
290
        if (is_string($paths)) {
0 ignored issues
show
introduced by
The condition is_string($paths) is always false.
Loading history...
291
            $paths = [$paths];
292
        }
293
294
        // treat each paths as pseudo-regex
295
        foreach ($paths as $path) {
296
            // need to escape path separators
297
            $path = str_replace('/', '\/', trim($path, '/ '));
298
            // need to make pseudo wildcard real
299
            $path = strtolower(str_replace('*', '.*', $path));
300
            // Does this rule apply here?
301
            if (preg_match('#^' . $path . '$#', $uri, $match) === 1) {
302
                return true;
303
            }
304
        }
305
306
        return false;
307
    }
308
}
309