/
Candidates.php
154 lines (135 loc) · 3.87 KB
/
Candidates.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
<?php
/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2015 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Cmf\Component\Routing\Candidates;
use Symfony\Component\HttpFoundation\Request;
/**
* A straightforward strategy that splits the URL on "/".
*
* If locales is set, additionally generates candidates removing the locale if
* it is one of the configured locales, for non-locale specific URLs.
*
* @author David Buchmann <mail@davidbu.ch>
*/
class Candidates implements CandidatesInterface
{
/**
* @var array
*/
protected $locales;
/**
* A limit to apply to the number of candidates generated.
*
* This is to prevent abusive requests with a lot of "/". The limit is per
* batch, that is if a locale matches you could get as many as 2 * $limit
* candidates if the URL has that many slashes.
*
* @var int
*/
protected $limit;
/**
* @param array $locales The locales to support.
* @param int $limit A limit to apply to the candidates generated.
*/
public function __construct(array $locales = array(), $limit = 20)
{
$this->setLocales($locales);
$this->limit = $limit;
}
/**
* Set the locales to support by this strategy.
*
* @param array $locales The locales to support.
*/
public function setLocales(array $locales)
{
$this->locales = $locales;
}
/**
* {@inheritDoc}
*
* Always returns true.
*/
public function isCandidate($name)
{
return true;
}
/**
* {@inheritDoc}
*
* Does nothing.
*/
public function restrictQuery($queryBuilder)
{
}
/**
* {@inheritDoc}
*/
public function getCandidates(Request $request)
{
$url = $request->getPathInfo();
$candidates = $this->getCandidatesFor($url);
$locale = $this->determineLocale($url);
if ($locale) {
$candidates = array_unique(array_merge($candidates, $this->getCandidatesFor(substr($url, strlen($locale) + 1))));
}
return $candidates;
}
/**
* Determine the locale of this URL.
*
* @param string $url The url to determine the locale from.
*
* @return string|boolean The locale if $url starts with one of the allowed locales.
*/
protected function determineLocale($url)
{
if (!count($this->locales)) {
return false;
}
$matches = array();
if (preg_match('#(' . implode('|', $this->locales) . ')(/|$)#', $url, $matches)) {
return $matches[1];
}
return false;
}
/**
* Handle a possible format extension and split the $url on "/".
*
* $prefix is prepended to every candidate generated.
*
* @param string $url The URL to split.
* @param string $prefix A prefix to prepend to every pattern.
*
* @return array Paths that could represent routes that match $url and are
* child of $prefix.
*/
protected function getCandidatesFor($url, $prefix = '')
{
$candidates = array();
if ('/' !== $url) {
// handle format extension, like .html or .json
if (preg_match('/(.+)\.[a-z]+$/i', $url, $matches)) {
$candidates[] = $prefix . $url;
$url = $matches[1];
}
$part = $url;
$count = 0;
while (false !== ($pos = strrpos($part, '/'))) {
if (++$count > $this->limit) {
return $candidates;
}
$candidates[] = $prefix . $part;
$part = substr($url, 0, $pos);
}
}
$candidates[] = $prefix ?: '/';
return $candidates;
}
}