Test Failed
Push — master ( fd3819...5bc6ef )
by Divine Niiquaye
13:04
created

DataTrait::bind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
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.4 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 Flight\Routing\Exceptions\{InvalidControllerException, UriHandlerException};
21
use Flight\Routing\{Route, RouteCollection};
22
use Flight\Routing\Handlers\ResourceHandler;
23
24
trait DataTrait
25
{
26
    /** @var array<string,mixed> */
27
    protected array $data;
28
29
    private ?RouteCollection $collection = null;
30
31
    /**
32
     * Sets the route path prefix.
33
     *
34
     * @return $this
35
     */
36
    public function prefix(string $path)
37
    {
38
        if (!empty($path)) {
39
            $uri = $this->data['path'] ?? '/';
40
41
            if (\strlen($uri) > 1 && isset(Route::URL_PREFIX_SLASHES[$uri[1]])) {
42
                $uri = \substr($uri, 1);
43
            }
44
45
            if (isset(Route::URL_PREFIX_SLASHES[$path[-1]])) {
46
                $uri = \substr($uri, 1);
47
            }
48
49
            \preg_match(Route::PRIORITY_REGEX, $this->data['path'] = '/' . \ltrim($path . $uri, '/'), $matches);
50
            $this->data['prefix'] = $matches[0] ?? null;
51
        }
52
53
        return $this;
54
    }
55
56
    /**
57
     * Sets the route path pattern.
58
     *
59
     * @return $this
60
     */
61
    public function path(string $pattern)
62
    {
63
        if (\preg_match(Route::RCA_PATTERN, $pattern, $matches, \PREG_UNMATCHED_AS_NULL)) {
64
            if (null !== $matches[1]) {
65
                $this->data['schemes'][$matches[1]] = true;
66
            }
67
68
            if (null !== $matches[2]) {
69
                $this->data['hosts'][$matches[2]] = true;
70
            }
71
72
            if (null !== $matches[5]) {
73
                $handler = $matches[4] ?? $this->data['handler'] ?? null;
74
                $this->data['handler'] = !empty($handler) ? [$handler, $matches[5]] : $matches[5];
75
            }
76
77
            if (empty($matches[3])) {
78
                throw new UriHandlerException(\sprintf('The route pattern "%s" is invalid as route path must be present in pattern.', $pattern));
79
            }
80
81
            \preg_match(Route::PRIORITY_REGEX, $matches[3], $static) && $this->data['prefix'] = $static[0];
82
        }
83
84
        $this->data['path'] = '/' . \ltrim($matches[3] ?? $pattern, '/');
85
86
        return $this;
87
    }
88
89
    /**
90
     * Sets the requirement for the HTTP method.
91
     *
92
     * @param string $methods the HTTP method(s) name
93
     *
94
     * @return $this
95
     */
96
    public function method(string ...$methods)
97
    {
98
        foreach ($methods as $method) {
99
            $this->data['methods'][\strtoupper($method)] = true;
100
        }
101
102
        return $this;
103
    }
104
105
    /**
106
     * Sets the requirement of host on this Route.
107
     *
108
     * @param string $hosts The host for which this route should be enabled
109
     *
110
     * @return $this
111
     */
112
    public function domain(string ...$hosts)
113
    {
114
        foreach ($hosts as $host) {
115
            \preg_match(Route::URL_PATTERN, $host, $matches, \PREG_UNMATCHED_AS_NULL);
116
117
            if (isset($matches[1])) {
118
                $this->data['schemes'][$matches[1]] = true;
119
            }
120
121
            if (isset($matches[2])) {
122
                $this->data['hosts'][$matches[2]] = true;
123
            }
124
        }
125
126
        return $this;
127
    }
128
129
    /**
130
     * Sets the requirement of domain scheme on this Route.
131
     *
132
     * @param string ...$schemes
133
     *
134
     * @return $this
135
     */
136
    public function scheme(string ...$schemes)
137
    {
138
        foreach ($schemes as $scheme) {
139
            $this->data['schemes'][$scheme] = true;
140
        }
141
142
        return $this;
143
    }
144
145
    /**
146
     * Sets the route name.
147
     *
148
     * @return $this
149
     */
150
    public function bind(string $routeName)
151
    {
152
        $this->data['name'] = $routeName;
153
154
        return $this;
155
    }
156
157
    /**
158
     * Sets the parameter value for a route handler.
159
     *
160
     * @param mixed $value The parameter value
161
     *
162
     * @return $this
163
     */
164
    public function argument(string $parameter, $value)
165
    {
166
        if (\is_numeric($value)) {
167
            $value = (int) $value;
168
        } elseif (\is_string($value)) {
169
            $value = \rawurldecode($value);
170
        }
171
172
        $this->data['arguments'][$parameter] = $value;
173
174
        return $this;
175
    }
176
177
    /**
178
     * Sets the parameter values for a route handler.
179
     *
180
     * @param array<int|string> $parameters The route handler parameters
181
     *
182
     * @return $this
183
     */
184
    public function arguments(array $parameters)
185
    {
186
        foreach ($parameters as $variable => $value) {
187
            $this->argument($variable, $value);
188
        }
189
190
        return $this;
191
    }
192
193
    /**
194
     * Sets the route code that should be executed when matched.
195
     *
196
     * @param mixed $to PHP class, object or callable that returns the response when matched
197
     *
198
     * @return $this
199
     */
200
    public function run($to)
201
    {
202
        $this->data['handler'] = $to;
203
204
        return $this;
205
    }
206
207
    /**
208
     * Sets the missing namespace on route's handler.
209
     *
210
     * @throws InvalidControllerException if $namespace is invalid
211
     *
212
     * @return $this
213
     */
214
    public function namespace(string $namespace)
215
    {
216
        if (!empty($namespace)) {
217
            if ('\\' === $namespace[-1]) {
218
                throw new InvalidControllerException(\sprintf('Namespace "%s" provided for routes must not end with a "\\".', $namespace));
219
            }
220
221
            if (isset($this->data['handler'])) {
222
                $this->data['handler'] = $this->resolveNamespace($namespace, $this->data['handler']);
223
            }
224
        }
225
226
        return $this;
227
    }
228
229
    /**
230
     * Attach a named middleware group(s) to route.
231
     *
232
     * @return $this
233
     */
234
    public function piped(string ...$to)
235
    {
236
        foreach ($to as $namedMiddleware) {
237
            $this->data['middlewares'][$namedMiddleware] = true;
238
        }
239
240
        return $this;
241
    }
242
243
    /**
244
     * Sets the requirement for a route variable.
245
     *
246
     * @param string|string[] $regexp The regexp to apply
247
     *
248
     * @return $this
249
     */
250
    public function assert(string $variable, $regexp)
251
    {
252
        $this->data['patterns'][$variable] = $regexp;
253
254
        return $this;
255
    }
256
257
    /**
258
     * Sets the requirements for a route variable.
259
     *
260
     * @param array<string,string|string[]> $regexps The regexps to apply
261
     *
262
     * @return $this
263
     */
264
    public function asserts(array $regexps)
265
    {
266
        foreach ($regexps as $variable => $regexp) {
267
            $this->assert($variable, $regexp);
268
        }
269
270
        return $this;
271
    }
272
273
    /**
274
     * Sets the default value for a route variable.
275
     *
276
     * @param mixed $default The default value
277
     *
278
     * @return $this
279
     */
280
    public function default(string $variable, $default)
281
    {
282
        $this->data['defaults'][$variable] = $default;
283
284
        return $this;
285
    }
286
287
    /**
288
     * Sets the default values for a route variables.
289
     *
290
     * @param array<string,mixed> $values
291
     *
292
     * @return $this
293
     */
294
    public function defaults(array $values)
295
    {
296
        foreach ($values as $variable => $default) {
297
            $this->default($variable, $default);
298
        }
299
300
        return $this;
301
    }
302
303
    /**
304
     * Sets the route belonging to a particular collection.
305
     *
306
     * This method is kinda internal, only used in RouteCollection class,
307
     * and retrieved using this class end method.
308
     *
309
     * @internal used by RouteCollection class
310
     */
311
    public function belong(RouteCollection $to): void
312
    {
313
        $this->collection = $to;
314
    }
315
316
    /**
317
     * End a group stack or return self.
318
     */
319
    public function end(): ?RouteCollection
320
    {
321
        if (null !== $stack = $this->collection) {
322
            $this->collection = null; // Just remove it.
323
        }
324
325
        return $stack;
326
    }
327
328
    public function getName(): ?string
329
    {
330
        return $this->data['name'] ?? null;
331
    }
332
333
    public function getPath(): string
334
    {
335
        return $this->data['path'] ?? '/';
336
    }
337
338
    /**
339
     * @return array<int,string>
340
     */
341
    public function getMethods(): array
342
    {
343
        return \array_keys($this->data['methods'] ?? []);
344
    }
345
346
    /**
347
     * @return array<int,string>
348
     */
349
    public function getSchemes(): array
350
    {
351
        return \array_keys($this->data['schemes'] ?? []);
352
    }
353
354
    /**
355
     * @return array<int,string>
356
     */
357
    public function getHosts(): array
358
    {
359
        return \array_keys($this->data['hosts'] ?? []);
360
    }
361
362
    /**
363
     * @return array<int|string,mixed>
364
     */
365
    public function getArguments(): array
366
    {
367
        return $this->data['arguments'] ?? [];
368
    }
369
370
    /**
371
     * @return mixed
372
     */
373
    public function getHandler()
374
    {
375
        return $this->data['handler'] ?? null;
376
    }
377
378
    /**
379
     * @return array<int|string,mixed>
380
     */
381
    public function getDefaults(): array
382
    {
383
        return $this->data['defaults'] ?? [];
384
    }
385
386
    /**
387
     * @return array<string,string|string[]>
388
     */
389
    public function getPatterns(): array
390
    {
391
        return $this->data['patterns'] ?? [];
392
    }
393
394
    /**
395
     * Return the list of attached grouped middlewares.
396
     *
397
     * @return array<int,string>
398
     */
399
    public function getPiped(): array
400
    {
401
        return \array_keys($this->data['middlewares'] ?? []);
402
    }
403
404
    /**
405
     * Return's the static prefixed portion of the route path else null.
406
     *
407
     * @see Flight\Routing\RouteCollection::getRoutes()
408
     */
409
    public function getStaticPrefix(): ?string
410
    {
411
        return $this->data['prefix'] ?? null;
412
    }
413
414
    /**
415
     * @internal skip throwing an exception and return existing $controller
416
     *
417
     * @param callable|object|string|string[] $controller
418
     *
419
     * @return mixed
420
     */
421
    protected function resolveNamespace(string $namespace, $controller)
422
    {
423
        if ($controller instanceof ResourceHandler) {
424
            return $controller->namespace($namespace);
425
        }
426
427
        if (\is_string($controller) && '\\' === $controller[0]) {
428
            $controller = $namespace . $controller;
429
        } elseif ((\is_array($controller) && \array_keys($controller) === [0, 1]) && \is_string($controller[0])) {
430
            $controller[0] = $this->resolveNamespace($namespace, $controller[0]);
431
        }
432
433
        return $controller;
434
    }
435
}
436