Subversion Repositories php-qbpwcf

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3 liveuser 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\Routing;
13
 
14
/**
15
 * A Route describes a route and its parameters.
16
 *
17
 * @author Fabien Potencier <fabien@symfony.com>
18
 * @author Tobias Schultze <http://tobion.de>
19
 */
20
class Route implements \Serializable
21
{
22
    private $path = '/';
23
    private $host = '';
24
    private $schemes = [];
25
    private $methods = [];
26
    private $defaults = [];
27
    private $requirements = [];
28
    private $options = [];
29
    private $condition = '';
30
 
31
    /**
32
     * @var CompiledRoute|null
33
     */
34
    private $compiled;
35
 
36
    /**
37
     * Constructor.
38
     *
39
     * Available options:
40
     *
41
     *  * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
42
     *  * utf8:           Whether UTF-8 matching is enforced ot not
43
     *
44
     * @param string          $path         The path pattern to match
45
     * @param array           $defaults     An array of default parameter values
46
     * @param array           $requirements An array of requirements for parameters (regexes)
47
     * @param array           $options      An array of options
48
     * @param string|null     $host         The host pattern to match
49
     * @param string|string[] $schemes      A required URI scheme or an array of restricted schemes
50
     * @param string|string[] $methods      A required HTTP method or an array of restricted methods
51
     * @param string|null     $condition    A condition that should evaluate to true for the route to match
52
     */
53
    public function __construct(string $path, array $defaults = [], array $requirements = [], array $options = [], ?string $host = '', $schemes = [], $methods = [], ?string $condition = '')
54
    {
55
        $this->setPath($path);
56
        $this->addDefaults($defaults);
57
        $this->addRequirements($requirements);
58
        $this->setOptions($options);
59
        $this->setHost($host);
60
        $this->setSchemes($schemes);
61
        $this->setMethods($methods);
62
        $this->setCondition($condition);
63
    }
64
 
65
    public function __serialize(): array
66
    {
67
        return [
68
            'path' => $this->path,
69
            'host' => $this->host,
70
            'defaults' => $this->defaults,
71
            'requirements' => $this->requirements,
72
            'options' => $this->options,
73
            'schemes' => $this->schemes,
74
            'methods' => $this->methods,
75
            'condition' => $this->condition,
76
            'compiled' => $this->compiled,
77
        ];
78
    }
79
 
80
    /**
81
     * @internal
82
     */
83
    final public function serialize(): string
84
    {
85
        return serialize($this->__serialize());
86
    }
87
 
88
    public function __unserialize(array $data): void
89
    {
90
        $this->path = $data['path'];
91
        $this->host = $data['host'];
92
        $this->defaults = $data['defaults'];
93
        $this->requirements = $data['requirements'];
94
        $this->options = $data['options'];
95
        $this->schemes = $data['schemes'];
96
        $this->methods = $data['methods'];
97
 
98
        if (isset($data['condition'])) {
99
            $this->condition = $data['condition'];
100
        }
101
        if (isset($data['compiled'])) {
102
            $this->compiled = $data['compiled'];
103
        }
104
    }
105
 
106
    /**
107
     * @internal
108
     */
109
    final public function unserialize($serialized)
110
    {
111
        $this->__unserialize(unserialize($serialized));
112
    }
113
 
114
    /**
115
     * Returns the pattern for the path.
116
     *
117
     * @return string The path pattern
118
     */
119
    public function getPath()
120
    {
121
        return $this->path;
122
    }
123
 
124
    /**
125
     * Sets the pattern for the path.
126
     *
127
     * This method implements a fluent interface.
128
     *
129
     * @return $this
130
     */
131
    public function setPath(string $pattern)
132
    {
133
        if (false !== strpbrk($pattern, '?<')) {
134
            $pattern = preg_replace_callback('#\{(!?\w++)(<.*?>)?(\?[^\}]*+)?\}#', function ($m) {
135
                if (isset($m[3][0])) {
136
                    $this->setDefault($m[1], '?' !== $m[3] ? substr($m[3], 1) : null);
137
                }
138
                if (isset($m[2][0])) {
139
                    $this->setRequirement($m[1], substr($m[2], 1, -1));
140
                }
141
 
142
                return '{'.$m[1].'}';
143
            }, $pattern);
144
        }
145
 
146
        // A pattern must start with a slash and must not have multiple slashes at the beginning because the
147
        // generated path for this route would be confused with a network path, e.g. '//domain.com/path'.
148
        $this->path = '/'.ltrim(trim($pattern), '/');
149
        $this->compiled = null;
150
 
151
        return $this;
152
    }
153
 
154
    /**
155
     * Returns the pattern for the host.
156
     *
157
     * @return string The host pattern
158
     */
159
    public function getHost()
160
    {
161
        return $this->host;
162
    }
163
 
164
    /**
165
     * Sets the pattern for the host.
166
     *
167
     * This method implements a fluent interface.
168
     *
169
     * @return $this
170
     */
171
    public function setHost(?string $pattern)
172
    {
173
        $this->host = (string) $pattern;
174
        $this->compiled = null;
175
 
176
        return $this;
177
    }
178
 
179
    /**
180
     * Returns the lowercased schemes this route is restricted to.
181
     * So an empty array means that any scheme is allowed.
182
     *
183
     * @return string[] The schemes
184
     */
185
    public function getSchemes()
186
    {
187
        return $this->schemes;
188
    }
189
 
190
    /**
191
     * Sets the schemes (e.g. 'https') this route is restricted to.
192
     * So an empty array means that any scheme is allowed.
193
     *
194
     * This method implements a fluent interface.
195
     *
196
     * @param string|string[] $schemes The scheme or an array of schemes
197
     *
198
     * @return $this
199
     */
200
    public function setSchemes($schemes)
201
    {
202
        $this->schemes = array_map('strtolower', (array) $schemes);
203
        $this->compiled = null;
204
 
205
        return $this;
206
    }
207
 
208
    /**
209
     * Checks if a scheme requirement has been set.
210
     *
211
     * @return bool true if the scheme requirement exists, otherwise false
212
     */
213
    public function hasScheme(string $scheme)
214
    {
215
        return \in_array(strtolower($scheme), $this->schemes, true);
216
    }
217
 
218
    /**
219
     * Returns the uppercased HTTP methods this route is restricted to.
220
     * So an empty array means that any method is allowed.
221
     *
222
     * @return string[] The methods
223
     */
224
    public function getMethods()
225
    {
226
        return $this->methods;
227
    }
228
 
229
    /**
230
     * Sets the HTTP methods (e.g. 'POST') this route is restricted to.
231
     * So an empty array means that any method is allowed.
232
     *
233
     * This method implements a fluent interface.
234
     *
235
     * @param string|string[] $methods The method or an array of methods
236
     *
237
     * @return $this
238
     */
239
    public function setMethods($methods)
240
    {
241
        $this->methods = array_map('strtoupper', (array) $methods);
242
        $this->compiled = null;
243
 
244
        return $this;
245
    }
246
 
247
    /**
248
     * Returns the options.
249
     *
250
     * @return array The options
251
     */
252
    public function getOptions()
253
    {
254
        return $this->options;
255
    }
256
 
257
    /**
258
     * Sets the options.
259
     *
260
     * This method implements a fluent interface.
261
     *
262
     * @return $this
263
     */
264
    public function setOptions(array $options)
265
    {
266
        $this->options = [
267
            'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
268
        ];
269
 
270
        return $this->addOptions($options);
271
    }
272
 
273
    /**
274
     * Adds options.
275
     *
276
     * This method implements a fluent interface.
277
     *
278
     * @return $this
279
     */
280
    public function addOptions(array $options)
281
    {
282
        foreach ($options as $name => $option) {
283
            $this->options[$name] = $option;
284
        }
285
        $this->compiled = null;
286
 
287
        return $this;
288
    }
289
 
290
    /**
291
     * Sets an option value.
292
     *
293
     * This method implements a fluent interface.
294
     *
295
     * @param mixed $value The option value
296
     *
297
     * @return $this
298
     */
299
    public function setOption(string $name, $value)
300
    {
301
        $this->options[$name] = $value;
302
        $this->compiled = null;
303
 
304
        return $this;
305
    }
306
 
307
    /**
308
     * Get an option value.
309
     *
310
     * @return mixed The option value or null when not given
311
     */
312
    public function getOption(string $name)
313
    {
314
        return isset($this->options[$name]) ? $this->options[$name] : null;
315
    }
316
 
317
    /**
318
     * Checks if an option has been set.
319
     *
320
     * @return bool true if the option is set, false otherwise
321
     */
322
    public function hasOption(string $name)
323
    {
324
        return \array_key_exists($name, $this->options);
325
    }
326
 
327
    /**
328
     * Returns the defaults.
329
     *
330
     * @return array The defaults
331
     */
332
    public function getDefaults()
333
    {
334
        return $this->defaults;
335
    }
336
 
337
    /**
338
     * Sets the defaults.
339
     *
340
     * This method implements a fluent interface.
341
     *
342
     * @param array $defaults The defaults
343
     *
344
     * @return $this
345
     */
346
    public function setDefaults(array $defaults)
347
    {
348
        $this->defaults = [];
349
 
350
        return $this->addDefaults($defaults);
351
    }
352
 
353
    /**
354
     * Adds defaults.
355
     *
356
     * This method implements a fluent interface.
357
     *
358
     * @param array $defaults The defaults
359
     *
360
     * @return $this
361
     */
362
    public function addDefaults(array $defaults)
363
    {
364
        if (isset($defaults['_locale']) && $this->isLocalized()) {
365
            unset($defaults['_locale']);
366
        }
367
 
368
        foreach ($defaults as $name => $default) {
369
            $this->defaults[$name] = $default;
370
        }
371
        $this->compiled = null;
372
 
373
        return $this;
374
    }
375
 
376
    /**
377
     * Gets a default value.
378
     *
379
     * @return mixed The default value or null when not given
380
     */
381
    public function getDefault(string $name)
382
    {
383
        return isset($this->defaults[$name]) ? $this->defaults[$name] : null;
384
    }
385
 
386
    /**
387
     * Checks if a default value is set for the given variable.
388
     *
389
     * @return bool true if the default value is set, false otherwise
390
     */
391
    public function hasDefault(string $name)
392
    {
393
        return \array_key_exists($name, $this->defaults);
394
    }
395
 
396
    /**
397
     * Sets a default value.
398
     *
399
     * @param mixed $default The default value
400
     *
401
     * @return $this
402
     */
403
    public function setDefault(string $name, $default)
404
    {
405
        if ('_locale' === $name && $this->isLocalized()) {
406
            return $this;
407
        }
408
 
409
        $this->defaults[$name] = $default;
410
        $this->compiled = null;
411
 
412
        return $this;
413
    }
414
 
415
    /**
416
     * Returns the requirements.
417
     *
418
     * @return array The requirements
419
     */
420
    public function getRequirements()
421
    {
422
        return $this->requirements;
423
    }
424
 
425
    /**
426
     * Sets the requirements.
427
     *
428
     * This method implements a fluent interface.
429
     *
430
     * @param array $requirements The requirements
431
     *
432
     * @return $this
433
     */
434
    public function setRequirements(array $requirements)
435
    {
436
        $this->requirements = [];
437
 
438
        return $this->addRequirements($requirements);
439
    }
440
 
441
    /**
442
     * Adds requirements.
443
     *
444
     * This method implements a fluent interface.
445
     *
446
     * @param array $requirements The requirements
447
     *
448
     * @return $this
449
     */
450
    public function addRequirements(array $requirements)
451
    {
452
        if (isset($requirements['_locale']) && $this->isLocalized()) {
453
            unset($requirements['_locale']);
454
        }
455
 
456
        foreach ($requirements as $key => $regex) {
457
            $this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
458
        }
459
        $this->compiled = null;
460
 
461
        return $this;
462
    }
463
 
464
    /**
465
     * Returns the requirement for the given key.
466
     *
467
     * @return string|null The regex or null when not given
468
     */
469
    public function getRequirement(string $key)
470
    {
471
        return isset($this->requirements[$key]) ? $this->requirements[$key] : null;
472
    }
473
 
474
    /**
475
     * Checks if a requirement is set for the given key.
476
     *
477
     * @return bool true if a requirement is specified, false otherwise
478
     */
479
    public function hasRequirement(string $key)
480
    {
481
        return \array_key_exists($key, $this->requirements);
482
    }
483
 
484
    /**
485
     * Sets a requirement for the given key.
486
     *
487
     * @return $this
488
     */
489
    public function setRequirement(string $key, string $regex)
490
    {
491
        if ('_locale' === $key && $this->isLocalized()) {
492
            return $this;
493
        }
494
 
495
        $this->requirements[$key] = $this->sanitizeRequirement($key, $regex);
496
        $this->compiled = null;
497
 
498
        return $this;
499
    }
500
 
501
    /**
502
     * Returns the condition.
503
     *
504
     * @return string The condition
505
     */
506
    public function getCondition()
507
    {
508
        return $this->condition;
509
    }
510
 
511
    /**
512
     * Sets the condition.
513
     *
514
     * This method implements a fluent interface.
515
     *
516
     * @return $this
517
     */
518
    public function setCondition(?string $condition)
519
    {
520
        $this->condition = (string) $condition;
521
        $this->compiled = null;
522
 
523
        return $this;
524
    }
525
 
526
    /**
527
     * Compiles the route.
528
     *
529
     * @return CompiledRoute A CompiledRoute instance
530
     *
531
     * @throws \LogicException If the Route cannot be compiled because the
532
     *                         path or host pattern is invalid
533
     *
534
     * @see RouteCompiler which is responsible for the compilation process
535
     */
536
    public function compile()
537
    {
538
        if (null !== $this->compiled) {
539
            return $this->compiled;
540
        }
541
 
542
        $class = $this->getOption('compiler_class');
543
 
544
        return $this->compiled = $class::compile($this);
545
    }
546
 
547
    private function sanitizeRequirement(string $key, string $regex)
548
    {
549
        if ('' !== $regex && '^' === $regex[0]) {
550
            $regex = (string) substr($regex, 1); // returns false for a single character
551
        }
552
 
553
        if ('$' === substr($regex, -1)) {
554
            $regex = substr($regex, 0, -1);
555
        }
556
 
557
        if ('' === $regex) {
558
            throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key));
559
        }
560
 
561
        return $regex;
562
    }
563
 
564
    private function isLocalized(): bool
565
    {
566
        return isset($this->defaults['_locale']) && isset($this->defaults['_canonical_route']) && ($this->requirements['_locale'] ?? null) === preg_quote($this->defaults['_locale']);
567
    }
568
}