Passed
Push — master ( cfe0c1...d1cf98 )
by Divine Niiquaye
02:27
created

Route::castDomain()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 7
ccs 5
cts 5
cp 1
crap 2
rs 10
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;
19
20
use Closure;
21
use Flight\Routing\Interfaces\RouteInterface;
22
use Serializable;
23
24
/**
25
 * Value object representing a single route.
26
 *
27
 * Routes are a combination of path, middleware, and HTTP methods; two routes
28
 * representing the same path and overlapping HTTP methods are not allowed,
29
 * while two routes representing the same path and non-overlapping HTTP methods
30
 * can be used (and should typically resolve to different middleware).
31
 *
32
 * Internally, only those three properties are required. However, underlying
33
 * router implementations may allow or require additional information, such as
34
 * information defining how to generate a URL from the given route, qualifiers
35
 * for how segments of a route match, or even default values to use. These may
36
 * be provided after instantiation via the "defaults" property and related
37
 * addDefaults() method.
38
 *
39
 * @author Divine Niiquaye Ibok <[email protected]>
40
 */
41
class Route implements Serializable, RouteInterface
42
{
43 1
    use Traits\CastingTrait;
44
45
    /**
46
     * A Pattern to Locates appropriate route by name, support dynamic route allocation using following pattern:
47
     * Pattern route:   `pattern/*<controller@action>`
48
     * Default route: `*<controller@action>`
49
     * Only action:   `pattern/*<action>`.
50
     *
51
     * @var string
52
     */
53
    public const RCA_PATTERN = '/^(?:(?P<route>[^(.*)]+)\*<)?(?:(?P<controller>[^@]+)@+)?(?P<action>[a-z_\-]+)\>$/i';
54
55
    /** @var string[] */
56
    private $methods = [];
57
58
    /** @var string */
59
    private $path;
60
61
    /** @var null|string */
62
    private $domain;
63
64
    /** @var string */
65
    private $name;
66
67
    /** @var callable|object|string|string[] */
68
    private $controller;
69
70
    /** @var string[] */
71
    private $schemes = [];
72
73
    /** @var array<int|string,mixed> */
74
    private $arguments = [];
75
76
    /** @var array<string,mixed> */
77
    private $defaults = [];
78
79
    /** @var array<string,string|string[]> */
80
    private $patterns = [];
81
82
    /** @var array<int,mixed> */
83
    private $middlewares = [];
84
85
    /**
86
     * Create a new Route constructor.
87
     *
88
     * @param string                               $name    The route name
89
     * @param string[]                             $methods The route HTTP methods
90
     * @param string                               $pattern The route pattern
91
     * @param null|callable|object|string|string[] $handler The route callable
92
     */
93 128
    public function __construct(string $name, array $methods, string $pattern, $handler)
94
    {
95 128
        $this->name       = $name;
96 128
        $this->controller = null === $handler ? '' : $handler;
97 128
        $this->methods    = \array_map('strtoupper', $methods);
98 128
        $this->path       = $this->castRoute($pattern);
99 127
    }
100
101
    /**
102
     * @internal
103
     *
104
     * @return array<string,mixed>
105
     */
106 1
    public function __serialize(): array
107
    {
108
        return [
109 1
            'name'          => $this->name,
110 1
            'path'          => $this->path,
111 1
            'host'          => $this->domain,
112 1
            'schemes'       => $this->schemes,
113 1
            'defaults'      => $this->defaults,
114 1
            'patterns'      => $this->patterns,
115 1
            'methods'       => $this->methods,
116 1
            'middlewares'   => $this->middlewares,
117 1
            'arguments'     => $this->arguments,
118 1
            'handler'       => $this->controller instanceof Closure ? [$this, 'getController'] : $this->controller,
119
        ];
120
    }
121
122
    /**
123
     * @internal
124
     *
125
     * @param array<string,mixed> $data
126
     */
127 1
    public function __unserialize(array $data): void
128
    {
129 1
        $this->name          = $data['name'];
130 1
        $this->path          = $data['path'];
131 1
        $this->domain        = $data['host'];
132 1
        $this->defaults      = $data['defaults'];
133 1
        $this->schemes       = $data['schemes'];
134 1
        $this->patterns      = $data['patterns'];
135 1
        $this->methods       = $data['methods'];
136 1
        $this->controller    = $data['handler'];
137 1
        $this->middlewares   = $data['middlewares'];
138 1
        $this->arguments     = $data['arguments'];
139 1
    }
140
141
    /**
142
     * {@inheritDoc}
143
     */
144 64
    public function getName(): string
145
    {
146 64
        return $this->name;
147
    }
148
149
    /**
150
     * {@inheritDoc}
151
     */
152 98
    public function getPath(): string
153
    {
154 98
        return $this->path;
155
    }
156
157
    /**
158
     * {@inheritDoc}
159
     */
160 60
    public function getMethods(): array
161
    {
162 60
        return $this->methods;
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 73
    public function getDomain(): string
169
    {
170 73
        return \str_replace(['http://', 'https://'], '', (string) $this->domain);
171
    }
172
173
    /**
174
     * {@inheritdoc}
175
     */
176 48
    public function getSchemes(): array
177
    {
178 48
        return $this->schemes;
179
    }
180
181
    /**
182
     * {@inheritDoc}
183
     */
184 49
    public function getController()
185
    {
186 49
        return $this->controller;
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192 44
    public function getArguments(): array
193
    {
194 44
        $routeArguments = [];
195
196 44
        foreach ($this->arguments as $key => $value) {
197 18
            if (\is_int($key)) {
198 3
                continue;
199
            }
200
201 17
            $value                = \is_numeric($value) ? (int) $value : $value;
202 17
            $routeArguments[$key] = \is_string($value) ? \rawurldecode($value) : $value;
203
        }
204
205 44
        return $routeArguments;
206
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 68
    public function getDefaults(): array
212
    {
213 68
        return $this->defaults;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219 50
    public function getPatterns(): array
220
    {
221 50
        return $this->patterns;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 48
    public function getMiddlewares(): array
228
    {
229 48
        return $this->middlewares;
230
    }
231
232
    /**
233
     * {@inheritDoc}
234
     */
235 3
    public function setName(string $name): RouteInterface
236
    {
237 3
        $this->name = $name;
238
239 3
        return $this;
240
    }
241
242
    /**
243
     * {@inheritdoc}
244
     */
245 14
    public function setDomain(string $domain): RouteInterface
246
    {
247 14
        if (false !== \preg_match('@^(?:(https?):)?(\/\/[^/]+)@i', $domain, $matches)) {
248 14
            if (empty($matches)) {
249 2
                $matches = [$domain, null, $domain];
250
            }
251
252 14
            [, $scheme, $domain] = $matches;
253
254 14
            if (!empty($scheme)) {
255 6
                $this->setScheme($scheme);
256
            }
257
        }
258 14
        $this->domain = \trim($domain, '//');
259
260 14
        return $this;
261
    }
262
263
    /**
264
     * {@inheritDoc}
265
     */
266 8
    public function setScheme(string ...$schemes): RouteInterface
267
    {
268 8
        foreach ($schemes as $scheme) {
269 8
            $this->schemes[] = $scheme;
270
        }
271
272 8
        return $this;
273
    }
274
275
    /**
276
     * {@inheritdoc}
277
     */
278 38
    public function setArguments(array $arguments): RouteInterface
279
    {
280 38
        foreach ($arguments as $key => $value) {
281 22
            $this->arguments[$key] = $value;
282
        }
283
284 38
        return $this;
285
    }
286
287
    /**
288
     * {@inheritdoc}
289
     */
290 50
    public function setDefaults(array $defaults): RouteInterface
291
    {
292 50
        foreach ($defaults as $key => $value) {
293 50
            $this->defaults[$key] = $value;
294
        }
295
296 50
        return $this;
297
    }
298
299
    /**
300
     * {@inheritdoc}
301
     */
302 3
    public function setPatterns(array $patterns): RouteInterface
303
    {
304 3
        foreach ($patterns as $key => $expression) {
305 3
            $this->addPattern($key, $expression);
306
        }
307
308 3
        return $this;
309
    }
310
311
    /**
312
     * {@inheritDoc}
313
     */
314 4
    public function addMethod(string ...$methods): RouteInterface
315
    {
316 4
        foreach ($methods as $method) {
317 4
            $this->methods[] = \strtoupper($method);
318
        }
319
320 4
        return $this;
321
    }
322
323
    /**
324
     * {@inheritdoc}
325
     */
326 8
    public function addPattern(string $name, $expression): RouteInterface
327
    {
328 8
        $this->patterns[$name] = $expression;
329
330 8
        return $this;
331
    }
332
333
    /**
334
     * {@inheritDoc}
335
     */
336 5
    public function addPrefix(string $prefix): RouteInterface
337
    {
338 5
        $this->path = $this->castPrefix($this->path, $prefix);
339
340 5
        return $this;
341
    }
342
343
    /**
344
     * {@inheritDoc}
345
     */
346 53
    public function addMiddleware(...$middlewares): RouteInterface
347
    {
348 53
        foreach ($middlewares as $middleware) {
349 53
            $this->middlewares[] = $middleware;
350
        }
351
352 53
        return $this;
353
    }
354
}
355