Completed
Pull Request — 3.x (#139)
by Hari
03:30
created

Route::setIsMatchCallable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
/**
3
 *
4
 * This file is part of Aura for PHP.
5
 *
6
 * @license http://opensource.org/licenses/bsd-license.php BSD
7
 *
8
 */
9
namespace Aura\Router;
10
11
use Psr\Http\Message\ServerRequestInterface;
12
13
/**
14
 *
15
 * An individual route with a name, path, attributes, defaults, etc.
16
 *
17
 * @package Aura.Router
18
 *
19
 * @property-read string $name The route name.
20
 *
21
 * @property-read string $path The route path.
22
 *
23
 * @property-read string $namePrefix
24
 *
25
 * @property-read string $pathPrefix
26
 *
27
 * @property-read string $host
28
 *
29
 * @property-read array $defaults Default values for attributes.
30
 *
31
 * @property-read array $attributes Attribute values added by the rules.
32
 *
33
 * @property-read array $tokens Placeholder token names and regexes.
34
 *
35
 * @property-read string $wildcard The name of the wildcard token.
36
 *
37
 * @property-read array $accept
38
 *
39
 * @property-read mixed $auth The auth value.
40
 *
41
 * @property-read array $extras
42
 *
43
 * @property-read bool $secure
44
 *
45
 * @property-read array $allows
46
 *
47
 * @property-read bool $routable
48
 *
49
 * @property-read string $failedRule
50
 *
51
 * @property-read mixed $handler
52
 *
53
 */
54
class Route
55
{
56
    /**
57
     *
58
     * The route failed to match at isCustomMatch().
59
     *
60
     * @const string
61
     *
62
     */
63
    const FAILED_CUSTOM = 'FAILED_CUSTOM';
64
65
    /**
66
     *
67
     * Accepts these content types.
68
     *
69
     * @var array
70
     *
71
     */
72
    protected $accepts = [];
73
74
    /**
75
     *
76
     * Allows these HTTP methods.
77
     *
78
     * @var array
79
     *
80
     */
81
    protected $allows = [];
82
83
    /**
84
     *
85
     * Attribute values added by the rules.
86
     *
87
     * @var array
88
     *
89
     */
90
    protected $attributes = [];
91
92
    /**
93
     *
94
     * Authentication/authorization values.
95
     *
96
     * @var mixed
97
     *
98
     */
99
    protected $auth;
100
101
    /**
102
     *
103
     * Default attribute values.
104
     *
105
     * @var array
106
     *
107
     */
108
    protected $defaults = [];
109
110
    /**
111
     *
112
     * Extra key-value pairs to attach to the route; intended for use by
113
     * custom matching rules.
114
     *
115
     * @var array
116
     *
117
     */
118
    protected $extras = [];
119
120
    /**
121
     *
122
     * The rule that failed, if any, during matching.
123
     *
124
     * @var string
125
     *
126
     */
127
    protected $failedRule;
128
129
    /**
130
     *
131
     * The action, controller, callable, closure, etc. this route points to.
132
     *
133
     * @var mixed
134
     *
135
     */
136
    protected $handler;
137
138
    /**
139
     *
140
     * The host string this route responds to.
141
     *
142
     * @var string
143
     *
144
     */
145
    protected $host;
146
147
    /**
148
     *
149
     * The name for this route.
150
     *
151
     * @var string
152
     *
153
     */
154
    protected $name;
155
156
    /**
157
     *
158
     * Prefix the route name with this string.
159
     *
160
     * @var string
161
     *
162
     */
163
    protected $namePrefix;
164
165
    /**
166
     *
167
     * The path for this route.
168
     *
169
     * @var string
170
     *
171
     */
172
    protected $path;
173
174
    /**
175
     *
176
     * Prefix the route path with this string.
177
     *
178
     * @var string
179
     *
180
     */
181
    protected $pathPrefix;
182
183
    /**
184
     *
185
     * Should this route be used for matching?
186
     *
187
     * @var bool
188
     *
189
     */
190
    protected $isRoutable = true;
191
192
    /**
193
     *
194
     * Should this route respond on a secure protocol?
195
     *
196
     * @var bool
197
     *
198
     */
199
    protected $secure = null;
200
201
    /**
202
     *
203
     * Placeholder token names and regexes.
204
     *
205
     * @var array
206
     *
207
     */
208
    protected $tokens = [];
209
210
    /**
211
     *
212
     * Wildcard token name, if any.
213
     *
214
     * @var string
215
     *
216
     */
217
    protected $wildcard = null;
218
219
    /**
220
     *
221
     * Custom callable for isCustomMatch() logic.
222
     *
223
     * @var callable
224
     *
225
     */
226
    protected $is_match    = null;
227
228
    /**
229
     *
230
     * When cloning the Route, reset the `$attributes` to an empty array, and
231
     * clear the `$failedRule`.
232
     *
233
     */
234 32
    public function __clone()
235
    {
236
        // $this is the cloned instance, not the original
237 32
        $this->attributes = $this->defaults;
238 32
        $this->failedRule = null;
239 32
    }
240
241
    /**
242
     *
243
     * Magic read-only for all properties.
244
     *
245
     * @param string $key The property to read from.
246
     *
247
     * @return mixed
248
     *
249
     */
250 41
    public function __get($key)
251
    {
252 41
        return $this->$key;
253
    }
254
255
    /**
256
     *
257
     * Merges with the existing content types.
258
     *
259
     * @param string|array $accepts The content types.
260
     *
261
     * @return $this
262
     *
263
     */
264 1
    public function accepts($accepts)
265
    {
266 1
        $this->accepts = array_merge($this->accepts, (array) $accepts);
267 1
        return $this;
268
    }
269
270
    /**
271
     *
272
     * Merges with the existing allowed methods.
273
     *
274
     * @param string|array $allows The allowed HTTP methods.
275
     *
276
     * @return $this
277
     *
278
     */
279 7
    public function allows($allows)
280
    {
281 7
        $this->allows = array_merge($this->allows, (array) $allows);
282 7
        return $this;
283
    }
284
285
    /**
286
     *
287
     * Merges with the existing attributes.
288
     *
289
     * @param array $attributes The attributes to add.
290
     *
291
     * @return $this
292
     *
293
     */
294 19
    public function attributes(array $attributes)
295
    {
296 19
        $this->attributes = array_merge($this->attributes, $attributes);
297 19
        return $this;
298
    }
299
300
    /**
301
     *
302
     * Sets the auth value.
303
     *
304
     * @param mixed $auth The auth value to set.
305
     *
306
     * @return $this
307
     *
308
     */
309 1
    public function auth($auth)
310
    {
311 1
        $this->auth = $auth;
312 1
        return $this;
313
    }
314
315
    /**
316
     *
317
     * Merges with the existing default values for attributes.
318
     *
319
     * @param array $defaults Default values for attributes.
320
     *
321
     * @return $this
322
     *
323
     */
324 3
    public function defaults(array $defaults)
325
    {
326 3
        $this->defaults = array_merge($this->defaults, $defaults);
327 3
        return $this;
328
    }
329
330
    /**
331
     *
332
     * Merges with the existing extra key-value pairs; this merge is recursive,
333
     * so the values can be arbitrarily deep.
334
     *
335
     * @param array $extras The extra key-value pairs.
336
     *
337
     * @return $this
338
     *
339
     */
340 3
    public function extras(array $extras)
341
    {
342 3
        $this->extras = array_merge_recursive($this->extras, $extras);
343 3
        return $this;
344
    }
345
346
    /**
347
     *
348
     * Sets the failed rule.
349
     *
350
     * @param mixed $failedRule The failed rule.
351
     *
352
     * @return $this
353
     *
354
     */
355 6
    public function failedRule($failedRule)
356
    {
357 6
        $this->failedRule = $failedRule;
358 6
        return $this;
359
    }
360
361
    /**
362
     *
363
     * The route leads to this handler.
364
     *
365
     * @param mixed $handler The handler for this route; if null, uses the
366
     * route name.
367
     *
368
     * @return $this
369
     *
370
     */
371 23
    public function handler($handler)
372
    {
373 23
        if ($handler === null) {
374 21
            $handler = $this->name;
375 21
        }
376 23
        $this->handler = $handler;
377 23
        return $this;
378
    }
379
380
    /**
381
     *
382
     * Sets the host.
383
     *
384
     * @param mixed $host The host.
385
     *
386
     * @return $this
387
     *
388
     */
389 5
    public function host($host)
390
    {
391 5
        $this->host = $host;
392 5
        return $this;
393
    }
394
395
    /**
396
     *
397
     * Sets whether or not this route should be used for matching.
398
     *
399
     * @param bool $isRoutable If true, this route can be matched; if not, it
400
     * can be used only to generate a path.
401
     *
402
     * @return $this
403
     *
404
     */
405 4
    public function isRoutable($isRoutable = true)
406
    {
407 4
        $this->isRoutable = (bool) $isRoutable;
408 4
        return $this;
409
    }
410
411
    /**
412
     *
413
     * Sets the route name; immutable once set.
414
     *
415
     * @param string $name The route name.
416
     *
417
     * @return $this
418
     *
419
     * @throws Exception\ImmutableProperty when the name has already been set.
420
     *
421
     */
422 25
    public function name($name)
423
    {
424 25
        if ($this->name !== null) {
425 1
            $message = __CLASS__ . '::$name is immutable once set';
426 1
            throw new Exception\ImmutableProperty($message);
427
        }
428 25
        $this->name = $this->namePrefix . $name;
429 25
        return $this;
430
    }
431
432
    /**
433
     *
434
     * Appends to the existing name prefix; immutable once $name is set.
435
     *
436
     * @param string $namePrefix The name prefix to append.
437
     *
438
     * @return $this
439
     *
440
     * @throws Exception\ImmutableProperty when the name has already been set.
441
     *
442
     */
443 5
    public function namePrefix($namePrefix)
444
    {
445 5
        if ($this->name !== null) {
446 1
            $message = __CLASS__ . '::$namePrefix is immutable once $name is set';
447 1
            throw new Exception\ImmutableProperty($message);
448
        }
449 4
        $this->namePrefix = $namePrefix;
450 4
        return $this;
451
    }
452
453
    /**
454
     *
455
     * Sets the route path; immutable once set.
456
     *
457
     * @param string $path The route path.
458
     *
459
     * @return $this
460
     *
461
     * @throws Exception\ImmutableProperty when the name has already been set.
462
     *
463
     */
464 42
    public function path($path)
465
    {
466 42
        if ($this->path !== null) {
467 1
            $message = __CLASS__ . '::$path is immutable once set';
468 1
            throw new Exception\ImmutableProperty($message);
469
        }
470 42
        $this->path = $this->pathPrefix . $path;
471 42
        return $this;
472
    }
473
474
    /**
475
     *
476
     * Appends to the existing path prefix; immutable once $path is set.
477
     *
478
     * @param string $pathPrefix The path prefix to append.
479
     *
480
     * @return $this
481
     *
482
     * @throws Exception\ImmutableProperty when the path has already been set.
483
     *
484
     */
485 5
    public function pathPrefix($pathPrefix)
486
    {
487 5
        if ($this->path !== null) {
488 1
            $message = __CLASS__ . '::$pathPrefix is immutable once $path is set';
489 1
            throw new Exception\ImmutableProperty($message);
490
        }
491 4
        $this->pathPrefix = $pathPrefix;
492 4
        return $this;
493
    }
494
495
    /**
496
     *
497
     * Sets whether or not the route must be secure.
498
     *
499
     * @param bool|null $secure If true, the server must indicate an HTTPS request;
500
     * if false, it must *not* be HTTPS; if null, it doesn't matter.
501
     *
502
     * @return $this
503
     *
504
     */
505 5
    public function secure($secure = true)
506
    {
507 5
        $this->secure = ($secure === null) ? null : (bool) $secure;
508 5
        return $this;
509
    }
510
511
    /**
512
     *
513
     * Merges with the existing tokens.
514
     *
515
     * @param array $tokens The tokens.
516
     *
517
     * @return $this
518
     *
519
     */
520 10
    public function tokens(array $tokens)
521
    {
522 10
        $this->tokens = array_merge($this->tokens, $tokens);
523 10
        return $this;
524
    }
525
526
    /**
527
     *
528
     * Sets the name of the wildcard token, if any.
529
     *
530
     * @param string $wildcard The name of the wildcard token, if any.
531
     *
532
     * @return $this
533
     *
534
     */
535 3
    public function wildcard($wildcard)
536
    {
537 3
        $this->wildcard = $wildcard;
538 3
        return $this;
539
    }
540
541
    /**
542
     *
543
     * Sets a custom callable to evaluate the route for matching.
544
     *
545
     * @param callable $is_match A custom callable to evaluate the route.
546
     *
547
     * @return $this
548
     *
549
     */
550 2
    public function setIsMatchCallable($is_match)
551
    {
552 2
        $this->is_match = $is_match;
553 2
        return $this;
554
    }
555
556
    /**
557
     *
558
     * Checks that the custom Route `$is_match` callable returns true, given
559
     * the server values.
560
     *
561
     * @param ServerRequestInterface $request The incoming request.
562
     *
563
     * @return bool True on a match, false if not.
564
     *
565
     */
566 5
    public function isCustomMatch(ServerRequestInterface $request)
567
    {
568 5
        if (! $this->is_match) {
569 3
            return true;
570
        }
571
572
        // attempt the match
573 2
        $result = call_user_func($this->is_match, $request, $this);
574
575
        // did it match?
576 2
        if (! $result) {
577 1
            $this->failedRule(self::FAILED_CUSTOM);
578 1
            return false;
579
        }
580
581 2
        return true;
582
    }
583
}
584