Security update for Core, with self-updated composer
[yaffs-website] / vendor / symfony / routing / Tests / RouteCompilerTest.php
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\Tests;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Routing\Route;
16 use Symfony\Component\Routing\RouteCompiler;
17
18 class RouteCompilerTest extends TestCase
19 {
20     /**
21      * @dataProvider provideCompileData
22      */
23     public function testCompile($name, $arguments, $prefix, $regex, $variables, $tokens)
24     {
25         $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
26         $route = $r->newInstanceArgs($arguments);
27
28         $compiled = $route->compile();
29         $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
30         $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)');
31         $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
32         $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
33     }
34
35     public function provideCompileData()
36     {
37         return array(
38             array(
39                 'Static route',
40                 array('/foo'),
41                 '/foo', '#^/foo$#s', array(), array(
42                     array('text', '/foo'),
43                 ),
44             ),
45
46             array(
47                 'Route with a variable',
48                 array('/foo/{bar}'),
49                 '/foo', '#^/foo/(?P<bar>[^/]++)$#s', array('bar'), array(
50                     array('variable', '/', '[^/]++', 'bar'),
51                     array('text', '/foo'),
52                 ),
53             ),
54
55             array(
56                 'Route with a variable that has a default value',
57                 array('/foo/{bar}', array('bar' => 'bar')),
58                 '/foo', '#^/foo(?:/(?P<bar>[^/]++))?$#s', array('bar'), array(
59                     array('variable', '/', '[^/]++', 'bar'),
60                     array('text', '/foo'),
61                 ),
62             ),
63
64             array(
65                 'Route with several variables',
66                 array('/foo/{bar}/{foobar}'),
67                 '/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
68                     array('variable', '/', '[^/]++', 'foobar'),
69                     array('variable', '/', '[^/]++', 'bar'),
70                     array('text', '/foo'),
71                 ),
72             ),
73
74             array(
75                 'Route with several variables that have default values',
76                 array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')),
77                 '/foo', '#^/foo(?:/(?P<bar>[^/]++)(?:/(?P<foobar>[^/]++))?)?$#s', array('bar', 'foobar'), array(
78                     array('variable', '/', '[^/]++', 'foobar'),
79                     array('variable', '/', '[^/]++', 'bar'),
80                     array('text', '/foo'),
81                 ),
82             ),
83
84             array(
85                 'Route with several variables but some of them have no default values',
86                 array('/foo/{bar}/{foobar}', array('bar' => 'bar')),
87                 '/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
88                     array('variable', '/', '[^/]++', 'foobar'),
89                     array('variable', '/', '[^/]++', 'bar'),
90                     array('text', '/foo'),
91                 ),
92             ),
93
94             array(
95                 'Route with an optional variable as the first segment',
96                 array('/{bar}', array('bar' => 'bar')),
97                 '', '#^/(?P<bar>[^/]++)?$#s', array('bar'), array(
98                     array('variable', '/', '[^/]++', 'bar'),
99                 ),
100             ),
101
102             array(
103                 'Route with a requirement of 0',
104                 array('/{bar}', array('bar' => null), array('bar' => '0')),
105                 '', '#^/(?P<bar>0)?$#s', array('bar'), array(
106                     array('variable', '/', '0', 'bar'),
107                 ),
108             ),
109
110             array(
111                 'Route with an optional variable as the first segment with requirements',
112                 array('/{bar}', array('bar' => 'bar'), array('bar' => '(foo|bar)')),
113                 '', '#^/(?P<bar>(foo|bar))?$#s', array('bar'), array(
114                     array('variable', '/', '(foo|bar)', 'bar'),
115                 ),
116             ),
117
118             array(
119                 'Route with only optional variables',
120                 array('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar')),
121                 '', '#^/(?P<foo>[^/]++)?(?:/(?P<bar>[^/]++))?$#s', array('foo', 'bar'), array(
122                     array('variable', '/', '[^/]++', 'bar'),
123                     array('variable', '/', '[^/]++', 'foo'),
124                 ),
125             ),
126
127             array(
128                 'Route with a variable in last position',
129                 array('/foo-{bar}'),
130                 '/foo', '#^/foo\-(?P<bar>[^/]++)$#s', array('bar'), array(
131                     array('variable', '-', '[^/]++', 'bar'),
132                     array('text', '/foo'),
133                 ),
134             ),
135
136             array(
137                 'Route with nested placeholders',
138                 array('/{static{var}static}'),
139                 '/{static', '#^/\{static(?P<var>[^/]+)static\}$#s', array('var'), array(
140                     array('text', 'static}'),
141                     array('variable', '', '[^/]+', 'var'),
142                     array('text', '/{static'),
143                 ),
144             ),
145
146             array(
147                 'Route without separator between variables',
148                 array('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '(y|Y)')),
149                 '', '#^/(?P<w>[^/\.]+)(?P<x>[^/\.]+)(?P<y>(y|Y))(?:(?P<z>[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#s', array('w', 'x', 'y', 'z', '_format'), array(
150                     array('variable', '.', '[^/]++', '_format'),
151                     array('variable', '', '[^/\.]++', 'z'),
152                     array('variable', '', '(y|Y)', 'y'),
153                     array('variable', '', '[^/\.]+', 'x'),
154                     array('variable', '/', '[^/\.]+', 'w'),
155                 ),
156             ),
157
158             array(
159                 'Route with a format',
160                 array('/foo/{bar}.{_format}'),
161                 '/foo', '#^/foo/(?P<bar>[^/\.]++)\.(?P<_format>[^/]++)$#s', array('bar', '_format'), array(
162                     array('variable', '.', '[^/]++', '_format'),
163                     array('variable', '/', '[^/\.]++', 'bar'),
164                     array('text', '/foo'),
165                 ),
166             ),
167
168             array(
169                 'Static non UTF-8 route',
170                 array("/fo\xE9"),
171                 "/fo\xE9", "#^/fo\xE9$#s", array(), array(
172                     array('text', "/fo\xE9"),
173                 ),
174             ),
175
176             array(
177                 'Route with an explicit UTF-8 requirement',
178                 array('/{bar}', array('bar' => null), array('bar' => '.'), array('utf8' => true)),
179                 '', '#^/(?P<bar>.)?$#su', array('bar'), array(
180                     array('variable', '/', '.', 'bar', true),
181                 ),
182             ),
183         );
184     }
185
186     /**
187      * @group legacy
188      * @dataProvider provideCompileImplicitUtf8Data
189      * @expectedDeprecation Using UTF-8 route %s without setting the "utf8" option is deprecated %s.
190      */
191     public function testCompileImplicitUtf8Data($name, $arguments, $prefix, $regex, $variables, $tokens, $deprecationType)
192     {
193         $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
194         $route = $r->newInstanceArgs($arguments);
195
196         $compiled = $route->compile();
197         $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
198         $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)');
199         $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
200         $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
201     }
202
203     public function provideCompileImplicitUtf8Data()
204     {
205         return array(
206             array(
207                 'Static UTF-8 route',
208                 array('/foé'),
209                 '/foé', '#^/foé$#su', array(), array(
210                     array('text', '/foé'),
211                 ),
212                 'patterns',
213             ),
214
215             array(
216                 'Route with an implicit UTF-8 requirement',
217                 array('/{bar}', array('bar' => null), array('bar' => 'é')),
218                 '', '#^/(?P<bar>é)?$#su', array('bar'), array(
219                     array('variable', '/', 'é', 'bar', true),
220                 ),
221                 'requirements',
222             ),
223
224             array(
225                 'Route with a UTF-8 class requirement',
226                 array('/{bar}', array('bar' => null), array('bar' => '\pM')),
227                 '', '#^/(?P<bar>\pM)?$#su', array('bar'), array(
228                     array('variable', '/', '\pM', 'bar', true),
229                 ),
230                 'requirements',
231             ),
232
233             array(
234                 'Route with a UTF-8 separator',
235                 array('/foo/{bar}§{_format}', array(), array(), array('compiler_class' => Utf8RouteCompiler::class)),
236                 '/foo', '#^/foo/(?P<bar>[^/§]++)§(?P<_format>[^/]++)$#su', array('bar', '_format'), array(
237                     array('variable', '§', '[^/]++', '_format', true),
238                     array('variable', '/', '[^/§]++', 'bar', true),
239                     array('text', '/foo'),
240                 ),
241                 'patterns',
242             ),
243         );
244     }
245
246     /**
247      * @expectedException \LogicException
248      */
249     public function testRouteWithSameVariableTwice()
250     {
251         $route = new Route('/{name}/{name}');
252
253         $compiled = $route->compile();
254     }
255
256     /**
257      * @expectedException \LogicException
258      */
259     public function testRouteCharsetMismatch()
260     {
261         $route = new Route("/\xE9/{bar}", array(), array('bar' => '.'), array('utf8' => true));
262
263         $compiled = $route->compile();
264     }
265
266     /**
267      * @expectedException \LogicException
268      */
269     public function testRequirementCharsetMismatch()
270     {
271         $route = new Route('/foo/{bar}', array(), array('bar' => "\xE9"), array('utf8' => true));
272
273         $compiled = $route->compile();
274     }
275
276     /**
277      * @expectedException \InvalidArgumentException
278      */
279     public function testRouteWithFragmentAsPathParameter()
280     {
281         $route = new Route('/{_fragment}');
282
283         $compiled = $route->compile();
284     }
285
286     /**
287      * @dataProvider getVariableNamesStartingWithADigit
288      * @expectedException \DomainException
289      */
290     public function testRouteWithVariableNameStartingWithADigit($name)
291     {
292         $route = new Route('/{'.$name.'}');
293         $route->compile();
294     }
295
296     public function getVariableNamesStartingWithADigit()
297     {
298         return array(
299            array('09'),
300            array('123'),
301            array('1e2'),
302         );
303     }
304
305     /**
306      * @dataProvider provideCompileWithHostData
307      */
308     public function testCompileWithHost($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostRegex, $hostVariables, $hostTokens)
309     {
310         $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
311         $route = $r->newInstanceArgs($arguments);
312
313         $compiled = $route->compile();
314         $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
315         $this->assertEquals($regex, str_replace(array("\n", ' '), '', $compiled->getRegex()), $name.' (regex)');
316         $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
317         $this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)');
318         $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
319         $this->assertEquals($hostRegex, str_replace(array("\n", ' '), '', $compiled->getHostRegex()), $name.' (host regex)');
320         $this->assertEquals($hostVariables, $compiled->getHostVariables(), $name.' (host variables)');
321         $this->assertEquals($hostTokens, $compiled->getHostTokens(), $name.' (host tokens)');
322     }
323
324     public function provideCompileWithHostData()
325     {
326         return array(
327             array(
328                 'Route with host pattern',
329                 array('/hello', array(), array(), array(), 'www.example.com'),
330                 '/hello', '#^/hello$#s', array(), array(), array(
331                     array('text', '/hello'),
332                 ),
333                 '#^www\.example\.com$#si', array(), array(
334                     array('text', 'www.example.com'),
335                 ),
336             ),
337             array(
338                 'Route with host pattern and some variables',
339                 array('/hello/{name}', array(), array(), array(), 'www.example.{tld}'),
340                 '/hello', '#^/hello/(?P<name>[^/]++)$#s', array('tld', 'name'), array('name'), array(
341                     array('variable', '/', '[^/]++', 'name'),
342                     array('text', '/hello'),
343                 ),
344                 '#^www\.example\.(?P<tld>[^\.]++)$#si', array('tld'), array(
345                     array('variable', '.', '[^\.]++', 'tld'),
346                     array('text', 'www.example'),
347                 ),
348             ),
349             array(
350                 'Route with variable at beginning of host',
351                 array('/hello', array(), array(), array(), '{locale}.example.{tld}'),
352                 '/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
353                     array('text', '/hello'),
354                 ),
355                 '#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
356                     array('variable', '.', '[^\.]++', 'tld'),
357                     array('text', '.example'),
358                     array('variable', '', '[^\.]++', 'locale'),
359                 ),
360             ),
361             array(
362                 'Route with host variables that has a default value',
363                 array('/hello', array('locale' => 'a', 'tld' => 'b'), array(), array(), '{locale}.example.{tld}'),
364                 '/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
365                     array('text', '/hello'),
366                 ),
367                 '#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
368                     array('variable', '.', '[^\.]++', 'tld'),
369                     array('text', '.example'),
370                     array('variable', '', '[^\.]++', 'locale'),
371                 ),
372             ),
373         );
374     }
375
376     /**
377      * @expectedException \DomainException
378      */
379     public function testRouteWithTooLongVariableName()
380     {
381         $route = new Route(sprintf('/{%s}', str_repeat('a', RouteCompiler::VARIABLE_MAXIMUM_LENGTH + 1)));
382         $route->compile();
383     }
384 }
385
386 class Utf8RouteCompiler extends RouteCompiler
387 {
388     const SEPARATORS = '/§';
389 }