4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Routing\Tests;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Routing\Route;
16 use Symfony\Component\Routing\RouteCompiler;
18 class RouteCompilerTest extends TestCase
21 * @dataProvider provideCompileData
23 public function testCompile($name, $arguments, $prefix, $regex, $variables, $tokens)
25 $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
26 $route = $r->newInstanceArgs($arguments);
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)');
35 public function provideCompileData()
41 '/foo', '#^/foo$#s', array(), array(
42 array('text', '/foo'),
47 'Route with a variable',
49 '/foo', '#^/foo/(?P<bar>[^/]++)$#s', array('bar'), array(
50 array('variable', '/', '[^/]++', 'bar'),
51 array('text', '/foo'),
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'),
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'),
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'),
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'),
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'),
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'),
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'),
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'),
128 'Route with a variable in last position',
130 '/foo', '#^/foo\-(?P<bar>[^/]++)$#s', array('bar'), array(
131 array('variable', '-', '[^/]++', 'bar'),
132 array('text', '/foo'),
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'),
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'),
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'),
169 'Static non UTF-8 route',
171 "/fo\xE9", "#^/fo\xE9$#s", array(), array(
172 array('text', "/fo\xE9"),
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),
188 * @dataProvider provideCompileImplicitUtf8Data
189 * @expectedDeprecation Using UTF-8 route %s without setting the "utf8" option is deprecated %s.
191 public function testCompileImplicitUtf8Data($name, $arguments, $prefix, $regex, $variables, $tokens, $deprecationType)
193 $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
194 $route = $r->newInstanceArgs($arguments);
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)');
203 public function provideCompileImplicitUtf8Data()
207 'Static UTF-8 route',
209 '/foé', '#^/foé$#su', array(), array(
210 array('text', '/foé'),
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),
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),
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'),
247 * @expectedException \LogicException
249 public function testRouteWithSameVariableTwice()
251 $route = new Route('/{name}/{name}');
253 $compiled = $route->compile();
257 * @expectedException \LogicException
259 public function testRouteCharsetMismatch()
261 $route = new Route("/\xE9/{bar}", array(), array('bar' => '.'), array('utf8' => true));
263 $compiled = $route->compile();
267 * @expectedException \LogicException
269 public function testRequirementCharsetMismatch()
271 $route = new Route('/foo/{bar}', array(), array('bar' => "\xE9"), array('utf8' => true));
273 $compiled = $route->compile();
277 * @expectedException \InvalidArgumentException
279 public function testRouteWithFragmentAsPathParameter()
281 $route = new Route('/{_fragment}');
283 $compiled = $route->compile();
287 * @dataProvider getVariableNamesStartingWithADigit
288 * @expectedException \DomainException
290 public function testRouteWithVariableNameStartingWithADigit($name)
292 $route = new Route('/{'.$name.'}');
296 public function getVariableNamesStartingWithADigit()
306 * @dataProvider provideCompileWithHostData
308 public function testCompileWithHost($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostRegex, $hostVariables, $hostTokens)
310 $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
311 $route = $r->newInstanceArgs($arguments);
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)');
324 public function provideCompileWithHostData()
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'),
333 '#^www\.example\.com$#si', array(), array(
334 array('text', 'www.example.com'),
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'),
344 '#^www\.example\.(?P<tld>[^\.]++)$#si', array('tld'), array(
345 array('variable', '.', '[^\.]++', 'tld'),
346 array('text', 'www.example'),
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'),
355 '#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
356 array('variable', '.', '[^\.]++', 'tld'),
357 array('text', '.example'),
358 array('variable', '', '[^\.]++', 'locale'),
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'),
367 '#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
368 array('variable', '.', '[^\.]++', 'tld'),
369 array('text', '.example'),
370 array('variable', '', '[^\.]++', 'locale'),
377 * @expectedException \DomainException
379 public function testRouteWithTooLongVariableName()
381 $route = new Route(sprintf('/{%s}', str_repeat('a', RouteCompiler::VARIABLE_MAXIMUM_LENGTH + 1)));
386 class Utf8RouteCompiler extends RouteCompiler
388 const SEPARATORS = '/§';