This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * Scabbia2 Router Component |
||
4 | * https://github.com/eserozvataf/scabbia2 |
||
5 | * |
||
6 | * For the full copyright and license information, please view the LICENSE |
||
7 | * file that was distributed with this source code. |
||
8 | * |
||
9 | * @link https://github.com/eserozvataf/scabbia2-router for the canonical source repository |
||
10 | * @copyright 2010-2016 Eser Ozvataf. (http://eser.ozvataf.com/) |
||
11 | * @license http://www.apache.org/licenses/LICENSE-2.0 - Apache License, Version 2.0 |
||
12 | */ |
||
13 | |||
14 | namespace Scabbia\Router; |
||
15 | |||
16 | /** |
||
17 | * RouteCollection |
||
18 | * |
||
19 | * @package Scabbia\Router |
||
20 | * @author Eser Ozvataf <[email protected]> |
||
21 | * @since 2.0.0 |
||
22 | * |
||
23 | * Routing related code based on the nikic's FastRoute solution: |
||
24 | * http://nikic.github.io/2014/02/18/Fast-request-routing-using-regular-expressions.html |
||
25 | */ |
||
26 | class RouteCollection |
||
27 | { |
||
28 | /** @type string VARIABLE_REGEX Regex expression of variables */ |
||
29 | const VARIABLE_REGEX = <<<'REGEX' |
||
30 | ~\{ |
||
31 | \s* ([a-zA-Z][a-zA-Z0-9_]*) \s* |
||
32 | (?: |
||
33 | : \s* ([^{}]*(?:\{(?-1)\}[^{}*])*) |
||
34 | )? |
||
35 | \}~x |
||
36 | REGEX; |
||
37 | |||
38 | /** @type string DEFAULT_DISPATCH_REGEX Regex expression of default dispatch */ |
||
39 | const DEFAULT_DISPATCH_REGEX = "[^/]+"; |
||
40 | |||
41 | /** @type string FILTER_VALIDATE_BOOLEAN a symbolic constant for boolean validation */ |
||
42 | const APPROX_CHUNK_SIZE = 10; |
||
43 | |||
44 | |||
45 | /** @type array route definitions */ |
||
46 | public $routes = [ |
||
47 | "static" => [], |
||
48 | "variable" => null, |
||
49 | "named" => [] |
||
50 | ]; |
||
51 | /** @type array regex to routes map */ |
||
52 | public $regexToRoutesMap = []; |
||
53 | |||
54 | |||
55 | /** |
||
56 | * Parses routes of the following form: |
||
57 | * "/user/{name}/{id:[0-9]+}" |
||
58 | * |
||
59 | * @param string $uRoute route pattern |
||
60 | * |
||
61 | * @return array |
||
62 | */ |
||
63 | public function parse($uRoute) |
||
64 | { |
||
65 | if (!preg_match_all(self::VARIABLE_REGEX, $uRoute, $tMatches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) { |
||
66 | return [$uRoute]; |
||
67 | } |
||
68 | |||
69 | $tOffset = 0; |
||
70 | $tRouteData = []; |
||
71 | foreach ($tMatches as $tMatch) { |
||
72 | if ($tMatch[0][1] > $tOffset) { |
||
73 | $tRouteData[] = substr($uRoute, $tOffset, $tMatch[0][1] - $tOffset); |
||
74 | } |
||
75 | |||
76 | $tRouteData[] = [ |
||
77 | $tMatch[1][0], |
||
78 | isset($tMatch[2]) ? trim($tMatch[2][0]) : self::DEFAULT_DISPATCH_REGEX |
||
79 | ]; |
||
80 | |||
81 | $tOffset = $tMatch[0][1] + strlen($tMatch[0][0]); |
||
82 | } |
||
83 | |||
84 | if ($tOffset !== strlen($uRoute)) { |
||
85 | $tRouteData[] = substr($uRoute, $tOffset); |
||
86 | } |
||
87 | |||
88 | return $tRouteData; |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Adds specified route |
||
93 | * |
||
94 | * @param string|array $uMethods http methods |
||
95 | * @param string $uRoute route |
||
96 | * @param callable $uCallback callback |
||
97 | * @param string|null $uName name of route |
||
98 | * |
||
99 | * @return void |
||
100 | */ |
||
101 | public function addRoute($uMethods, $uRoute, $uCallback, $uName = null) |
||
102 | { |
||
103 | $tRouteData = $this->parse($uRoute); |
||
104 | $tMethods = (array)$uMethods; |
||
105 | |||
106 | if (count($tRouteData) === 1 && is_string($tRouteData[0])) { |
||
107 | $this->addStaticRoute($tMethods, $tRouteData, $uCallback, $uName); |
||
108 | } else { |
||
109 | $this->addVariableRoute($tMethods, $tRouteData, $uCallback, $uName); |
||
110 | } |
||
111 | } |
||
112 | |||
113 | /** |
||
114 | * Adds a static route |
||
115 | * |
||
116 | * @param array $uMethods http methods |
||
117 | * @param array $uRouteData route data |
||
118 | * @param callable $uCallback callback |
||
119 | * @param string|null $uName name of route |
||
120 | * |
||
121 | * @throws UnexpectedValueException if an routing problem occurs |
||
122 | * @return void |
||
123 | */ |
||
124 | public function addStaticRoute(array $uMethods, $uRouteData, $uCallback, $uName = null) |
||
125 | { |
||
126 | $tRouteStr = $uRouteData[0]; |
||
127 | |||
128 | foreach ($uMethods as $tMethod) { |
||
129 | if (isset($this->routes["static"][$tRouteStr][$tMethod])) { |
||
130 | throw new UnexpectedValueException(sprintf( |
||
131 | "Cannot register two routes matching \"%s\" for method \"%s\"", |
||
132 | $tRouteStr, |
||
133 | $tMethod |
||
134 | )); |
||
135 | } |
||
136 | } |
||
137 | |||
138 | foreach ($uMethods as $tMethod) { |
||
139 | if (isset($this->regexToRoutesMap[$tMethod])) { |
||
140 | foreach ($this->regexToRoutesMap[$tMethod] as $tRoute) { |
||
141 | if (preg_match("~^{$tRoute["regex"]}$~", $tRouteStr) === 1) { |
||
142 | throw new UnexpectedValueException(sprintf( |
||
143 | "Static route \"%s\" is shadowed by previously defined variable " . |
||
144 | "route \"%s\" for method \"%s\"", |
||
145 | $tRouteStr, |
||
146 | $tRoute["regex"], |
||
147 | $tMethod |
||
148 | )); |
||
149 | } |
||
150 | } |
||
151 | } |
||
152 | |||
153 | $this->routes["static"][$tRouteStr][$tMethod] = $uCallback; |
||
154 | |||
155 | /* |
||
0 ignored issues
–
show
|
|||
156 | if ($uName !== null) { |
||
157 | if (!isset($this->routes["named"][$tMethod])) { |
||
158 | $this->routes["named"][$tMethod] = []; |
||
159 | } |
||
160 | |||
161 | $this->routes["named"][$tMethod][$uName] = [$tRouteStr, []]; |
||
162 | } |
||
163 | */ |
||
164 | View Code Duplication | if ($uName !== null && !isset($this->routes["named"][$uName])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
165 | $this->routes["named"][$uName] = [$tRouteStr, []]; |
||
166 | } |
||
167 | } |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Adds a variable route |
||
172 | * |
||
173 | * @param array $uMethods http method |
||
174 | * @param array $uRouteData route data |
||
175 | * @param callable $uCallback callback |
||
176 | * @param string|null $uName name of route |
||
177 | * |
||
178 | * @throws UnexpectedValueException if an routing problem occurs |
||
179 | * @return void |
||
180 | */ |
||
181 | public function addVariableRoute(array $uMethods, $uRouteData, $uCallback, $uName = null) |
||
182 | { |
||
183 | $tRegex = ""; |
||
184 | $tReverseRegex = ""; |
||
185 | $tVariables = []; |
||
186 | |||
187 | foreach ($uRouteData as $tPart) { |
||
188 | if (is_string($tPart)) { |
||
189 | $tRegex .= preg_quote($tPart, "~"); |
||
190 | $tReverseRegex .= preg_quote($tPart, "~"); |
||
191 | continue; |
||
192 | } |
||
193 | |||
194 | list($tVariableName, $tRegexPart) = $tPart; |
||
195 | |||
196 | if (isset($tVariables[$tVariableName])) { |
||
197 | throw new UnexpectedValueException(sprintf( |
||
198 | "Cannot use the same placeholder \"%s\" twice", |
||
199 | $tVariableName |
||
200 | )); |
||
201 | } |
||
202 | |||
203 | $tVariables[$tVariableName] = $tVariableName; |
||
204 | $tRegex .= "({$tRegexPart})"; |
||
205 | $tReverseRegex .= "{{$tVariableName}}"; |
||
206 | } |
||
207 | |||
208 | foreach ($uMethods as $tMethod) { |
||
209 | if (isset($this->regexToRoutesMap[$tMethod][$tRegex])) { |
||
210 | throw new UnexpectedValueException( |
||
211 | sprintf("Cannot register two routes matching \"%s\" for method \"%s\"", $tRegex, $tMethod) |
||
212 | ); |
||
213 | } |
||
214 | } |
||
215 | |||
216 | foreach ($uMethods as $tMethod) { |
||
217 | if (!isset($this->regexToRoutesMap[$tMethod])) { |
||
218 | $this->regexToRoutesMap[$tMethod] = []; |
||
219 | } |
||
220 | |||
221 | $this->regexToRoutesMap[$tMethod][$tRegex] = [ |
||
222 | // "method" => $tMethod, |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
58% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
223 | "callback" => $uCallback, |
||
224 | "regex" => $tRegex, |
||
225 | "variables" => $tVariables |
||
226 | ]; |
||
227 | |||
228 | /* |
||
0 ignored issues
–
show
Unused Code
Comprehensibility
introduced
by
69% of this comment could be valid code. Did you maybe forget this after debugging?
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it. The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production. This check looks for comments that seem to be mostly valid code and reports them. ![]() |
|||
229 | if ($uName !== null) { |
||
230 | if (!isset($this->routes["named"][$tMethod])) { |
||
231 | $this->routes["named"][$tMethod] = []; |
||
232 | } |
||
233 | |||
234 | $this->routes["named"][$tMethod][$uName] = [$tRegex, $tVariables]; |
||
235 | } |
||
236 | */ |
||
237 | View Code Duplication | if ($uName !== null && !isset($this->routes["named"][$uName])) { |
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
238 | $this->routes["named"][$uName] = [$tReverseRegex, array_values($tVariables)]; |
||
239 | } |
||
240 | } |
||
241 | |||
242 | $this->routes["variable"] = null; |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Combines all route data |
||
247 | * |
||
248 | * @return void |
||
249 | */ |
||
250 | public function compile() |
||
251 | { |
||
252 | $this->routes["variable"] = []; |
||
253 | foreach ($this->regexToRoutesMap as $tMethod => $tRegexToRoutesMapOfMethod) { |
||
254 | $tRegexToRoutesMapOfMethodCount = count($tRegexToRoutesMapOfMethod); |
||
255 | |||
256 | $tNumParts = max(1, round($tRegexToRoutesMapOfMethodCount / self::APPROX_CHUNK_SIZE)); |
||
257 | $tChunkSize = ceil($tRegexToRoutesMapOfMethodCount / $tNumParts); |
||
258 | |||
259 | $tChunks = array_chunk($tRegexToRoutesMapOfMethod, $tChunkSize, true); |
||
260 | $this->routes["variable"][$tMethod] = array_map([$this, "processChunk"], $tChunks); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * Returns route information in order to store it |
||
266 | * |
||
267 | * @return array |
||
268 | */ |
||
269 | public function save() |
||
270 | { |
||
271 | if ($this->routes["variable"] === null) { |
||
272 | $this->compile(); |
||
273 | } |
||
274 | |||
275 | return $this->routes; |
||
276 | } |
||
277 | |||
278 | /** |
||
279 | * Splits variable routes into chunks |
||
280 | * |
||
281 | * @param array $uRegexToRoutesMap route definitions |
||
282 | * |
||
283 | * @return array chunked |
||
284 | */ |
||
285 | protected function processChunk(array $uRegexToRoutesMap) |
||
286 | { |
||
287 | $tRouteMap = []; |
||
288 | $tRegexes = []; |
||
289 | $tNumGroups = 0; |
||
290 | |||
291 | foreach ($uRegexToRoutesMap as $tRegex => $tRoute) { |
||
292 | $tNumVariables = count($tRoute["variables"]); |
||
293 | $tNumGroups = max($tNumGroups, $tNumVariables); |
||
294 | |||
295 | $tRegexes[] = $tRegex . str_repeat("()", $tNumGroups - $tNumVariables); |
||
296 | $tRouteMap[$tNumGroups + 1] = [$tRoute["callback"], $tRoute["variables"]]; |
||
297 | |||
298 | ++$tNumGroups; |
||
299 | } |
||
300 | |||
301 | return [ |
||
302 | "regex" => "~^(?|" . implode("|", $tRegexes) . ")$~", |
||
303 | "routeMap" => $tRouteMap |
||
304 | ]; |
||
305 | } |
||
306 | } |
||
307 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.