[Routing] deprecate RouteCompiler::REGEX_DELIMITER

This commit is contained in:
Nicolas Grekas 2020-02-25 11:55:47 +01:00
parent ff9b8dae88
commit 21b8a64446
12 changed files with 57 additions and 53 deletions

View File

@ -59,6 +59,7 @@ Routing
* Deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
* Added argument `$priority` to `RouteCollection::add()`
* Deprecated the `RouteCompiler::REGEX_DELIMITER` constant
Yaml
----

View File

@ -48,3 +48,4 @@ Routing
* Removed `RouteCollectionBuilder`.
* Added argument `$priority` to `RouteCollection::add()`
* Removed the `RouteCompiler::REGEX_DELIMITER` constant

View File

@ -8,6 +8,7 @@ CHANGELOG
* deprecated `RouteCollectionBuilder` in favor of `RoutingConfigurator`.
* added "priority" option to annotated routes
* added argument `$priority` to `RouteCollection::add()`
* deprecated the `RouteCompiler::REGEX_DELIMITER` constant
5.0.0
-----

View File

@ -18,7 +18,6 @@ use Symfony\Component\Config\Resource\FileResource;
use Symfony\Component\Routing\Annotation\Route as RouteAnnotation;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouteCompiler;
/**
* AnnotationClassLoader loads routing information from a PHP class and its methods.
@ -206,7 +205,7 @@ abstract class AnnotationClassLoader implements LoaderInterface
$this->configureRoute($route, $class, $method, $annot);
if (0 !== $locale) {
$route->setDefault('_locale', $locale);
$route->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$route->setRequirement('_locale', preg_quote($locale));
$route->setDefault('_canonical_route', $name);
$collection->add($name.'.'.$locale, $route, $priority);
} else {

View File

@ -13,7 +13,6 @@ namespace Symfony\Component\Routing\Loader\Configurator\Traits;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouteCompiler;
/**
* @internal
@ -64,7 +63,7 @@ trait LocalizedRouteTrait
$routes->add($name.'.'.$locale, $route = $this->createRoute($path));
$collection->add($namePrefix.$name.'.'.$locale, $route);
$route->setDefault('_locale', $locale);
$route->setRequirement('_locale', preg_quote($locale, RouteCompiler::REGEX_DELIMITER));
$route->setRequirement('_locale', preg_quote($locale));
$route->setDefault('_canonical_route', $namePrefix.$name);
}

View File

@ -93,10 +93,10 @@ trait CompiledUrlMatcherTrait
}
if ($requiredHost) {
if ('#' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) {
if ('{' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) {
continue;
}
if ('#' === $requiredHost[0] && $hostMatches) {
if ('{' === $requiredHost[0] && $hostMatches) {
$hostMatches['_route'] = $ret['_route'];
$ret = $this->mergeDefaults($hostMatches, $ret);
}

View File

@ -19,6 +19,9 @@ namespace Symfony\Component\Routing;
*/
class RouteCompiler implements RouteCompilerInterface
{
/**
* @deprecated since Symfony 5.1, to be removed in 6.0
*/
const REGEX_DELIMITER = '#';
/**
@ -161,8 +164,8 @@ class RouteCompiler implements RouteCompilerInterface
$nextSeparator = self::findNextSeparator($followingPattern, $useUtf8);
$regexp = sprintf(
'[^%s%s]+',
preg_quote($defaultSeparator, self::REGEX_DELIMITER),
$defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : ''
preg_quote($defaultSeparator),
$defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator) : ''
);
if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) {
// When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
@ -217,7 +220,7 @@ class RouteCompiler implements RouteCompilerInterface
for ($i = 0, $nbToken = \count($tokens); $i < $nbToken; ++$i) {
$regexp .= self::computeRegexp($tokens, $i, $firstOptional);
}
$regexp = self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'sD'.($isHost ? 'i' : '');
$regexp = '{^'.$regexp.'$}sD'.($isHost ? 'i' : '');
// enable Utf8 matching if really required
if ($needsUtf8) {
@ -289,14 +292,14 @@ class RouteCompiler implements RouteCompilerInterface
$token = $tokens[$index];
if ('text' === $token[0]) {
// Text tokens
return preg_quote($token[1], self::REGEX_DELIMITER);
return preg_quote($token[1]);
} else {
// Variable tokens
if (0 === $index && 0 === $firstOptional) {
// When the only token is an optional variable token, the separator is required
return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
return sprintf('%s(?P<%s>%s)?', preg_quote($token[1]), $token[3], $token[2]);
} else {
$regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]);
$regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1]), $token[3], $token[2]);
if ($index >= $firstOptional) {
// Enclose each optional token in a subpattern to make it optional.
// "?:" means it is non-capturing, i.e. the portion of the subject string that

View File

@ -22,8 +22,8 @@ return [
'/c2/route3' => [[['_route' => 'route3'], 'b.example.com', null, null, false, false, null]],
'/route5' => [[['_route' => 'route5'], 'c.example.com', null, null, false, false, null]],
'/route6' => [[['_route' => 'route6'], null, null, null, false, false, null]],
'/route11' => [[['_route' => 'route11'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
'/route12' => [[['_route' => 'route12', 'var1' => 'val'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
'/route11' => [[['_route' => 'route11'], '{^(?P<var1>[^\\.]++)\\.example\\.com$}sDi', null, null, false, false, null]],
'/route12' => [[['_route' => 'route12', 'var1' => 'val'], '{^(?P<var1>[^\\.]++)\\.example\\.com$}sDi', null, null, false, false, null]],
'/route17' => [[['_route' => 'route17'], null, null, null, false, false, null]],
],
[ // $regexpList

View File

@ -22,8 +22,8 @@ return [
'/c2/route3' => [[['_route' => 'route3'], 'b.example.com', null, null, false, false, null]],
'/route5' => [[['_route' => 'route5'], 'c.example.com', null, null, false, false, null]],
'/route6' => [[['_route' => 'route6'], null, null, null, false, false, null]],
'/route11' => [[['_route' => 'route11'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
'/route12' => [[['_route' => 'route12', 'var1' => 'val'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
'/route11' => [[['_route' => 'route11'], '{^(?P<var1>[^\\.]++)\\.example\\.com$}sDi', null, null, false, false, null]],
'/route12' => [[['_route' => 'route12', 'var1' => 'val'], '{^(?P<var1>[^\\.]++)\\.example\\.com$}sDi', null, null, false, false, null]],
'/route17' => [[['_route' => 'route17'], null, null, null, false, false, null]],
'/secure' => [[['_route' => 'secure'], null, null, ['https' => 0], false, false, null]],
'/nonsecure' => [[['_route' => 'nonsecure'], null, null, ['http' => 0], false, false, null]],

View File

@ -9,8 +9,8 @@ return [
true, // $matchHost
[ // $staticRoutes
'/' => [
[['_route' => 'a'], '#^(?P<d>[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', null, null, false, false, null],
[['_route' => 'c'], '#^(?P<e>[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', null, null, false, false, null],
[['_route' => 'a'], '{^(?P<d>[^\\.]++)\\.e\\.c\\.b\\.a$}sDi', null, null, false, false, null],
[['_route' => 'c'], '{^(?P<e>[^\\.]++)\\.e\\.c\\.b\\.a$}sDi', null, null, false, false, null],
[['_route' => 'b'], 'd.c.b.a', null, null, false, false, null],
],
],

View File

@ -38,7 +38,7 @@ class RouteCompilerTest extends TestCase
[
'Static route',
['/foo'],
'/foo', '#^/foo$#sD', [], [
'/foo', '{^/foo$}sD', [], [
['text', '/foo'],
],
],
@ -46,7 +46,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a variable',
['/foo/{bar}'],
'/foo', '#^/foo/(?P<bar>[^/]++)$#sD', ['bar'], [
'/foo', '{^/foo/(?P<bar>[^/]++)$}sD', ['bar'], [
['variable', '/', '[^/]++', 'bar'],
['text', '/foo'],
],
@ -55,7 +55,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a variable that has a default value',
['/foo/{bar}', ['bar' => 'bar']],
'/foo', '#^/foo(?:/(?P<bar>[^/]++))?$#sD', ['bar'], [
'/foo', '{^/foo(?:/(?P<bar>[^/]++))?$}sD', ['bar'], [
['variable', '/', '[^/]++', 'bar'],
['text', '/foo'],
],
@ -64,7 +64,7 @@ class RouteCompilerTest extends TestCase
[
'Route with several variables',
['/foo/{bar}/{foobar}'],
'/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#sD', ['bar', 'foobar'], [
'/foo', '{^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$}sD', ['bar', 'foobar'], [
['variable', '/', '[^/]++', 'foobar'],
['variable', '/', '[^/]++', 'bar'],
['text', '/foo'],
@ -74,7 +74,7 @@ class RouteCompilerTest extends TestCase
[
'Route with several variables that have default values',
['/foo/{bar}/{foobar}', ['bar' => 'bar', 'foobar' => '']],
'/foo', '#^/foo(?:/(?P<bar>[^/]++)(?:/(?P<foobar>[^/]++))?)?$#sD', ['bar', 'foobar'], [
'/foo', '{^/foo(?:/(?P<bar>[^/]++)(?:/(?P<foobar>[^/]++))?)?$}sD', ['bar', 'foobar'], [
['variable', '/', '[^/]++', 'foobar'],
['variable', '/', '[^/]++', 'bar'],
['text', '/foo'],
@ -84,7 +84,7 @@ class RouteCompilerTest extends TestCase
[
'Route with several variables but some of them have no default values',
['/foo/{bar}/{foobar}', ['bar' => 'bar']],
'/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#sD', ['bar', 'foobar'], [
'/foo', '{^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$}sD', ['bar', 'foobar'], [
['variable', '/', '[^/]++', 'foobar'],
['variable', '/', '[^/]++', 'bar'],
['text', '/foo'],
@ -94,7 +94,7 @@ class RouteCompilerTest extends TestCase
[
'Route with an optional variable as the first segment',
['/{bar}', ['bar' => 'bar']],
'', '#^/(?P<bar>[^/]++)?$#sD', ['bar'], [
'', '{^/(?P<bar>[^/]++)?$}sD', ['bar'], [
['variable', '/', '[^/]++', 'bar'],
],
],
@ -102,7 +102,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a requirement of 0',
['/{bar}', ['bar' => null], ['bar' => '0']],
'', '#^/(?P<bar>0)?$#sD', ['bar'], [
'', '{^/(?P<bar>0)?$}sD', ['bar'], [
['variable', '/', '0', 'bar'],
],
],
@ -110,7 +110,7 @@ class RouteCompilerTest extends TestCase
[
'Route with an optional variable as the first segment with requirements',
['/{bar}', ['bar' => 'bar'], ['bar' => '(foo|bar)']],
'', '#^/(?P<bar>(?:foo|bar))?$#sD', ['bar'], [
'', '{^/(?P<bar>(?:foo|bar))?$}sD', ['bar'], [
['variable', '/', '(?:foo|bar)', 'bar'],
],
],
@ -118,7 +118,7 @@ class RouteCompilerTest extends TestCase
[
'Route with only optional variables',
['/{foo}/{bar}', ['foo' => 'foo', 'bar' => 'bar']],
'', '#^/(?P<foo>[^/]++)?(?:/(?P<bar>[^/]++))?$#sD', ['foo', 'bar'], [
'', '{^/(?P<foo>[^/]++)?(?:/(?P<bar>[^/]++))?$}sD', ['foo', 'bar'], [
['variable', '/', '[^/]++', 'bar'],
['variable', '/', '[^/]++', 'foo'],
],
@ -127,7 +127,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a variable in last position',
['/foo-{bar}'],
'/foo-', '#^/foo\-(?P<bar>[^/]++)$#sD', ['bar'], [
'/foo-', '{^/foo\-(?P<bar>[^/]++)$}sD', ['bar'], [
['variable', '-', '[^/]++', 'bar'],
['text', '/foo'],
],
@ -136,7 +136,7 @@ class RouteCompilerTest extends TestCase
[
'Route with nested placeholders',
['/{static{var}static}'],
'/{static', '#^/\{static(?P<var>[^/]+)static\}$#sD', ['var'], [
'/{static', '{^/\{static(?P<var>[^/]+)static\}$}sD', ['var'], [
['text', 'static}'],
['variable', '', '[^/]+', 'var'],
['text', '/{static'],
@ -146,7 +146,7 @@ class RouteCompilerTest extends TestCase
[
'Route without separator between variables',
['/{w}{x}{y}{z}.{_format}', ['z' => 'default-z', '_format' => 'html'], ['y' => '(y|Y)']],
'', '#^/(?P<w>[^/\.]+)(?P<x>[^/\.]+)(?P<y>(?:y|Y))(?:(?P<z>[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#sD', ['w', 'x', 'y', 'z', '_format'], [
'', '{^/(?P<w>[^/\.]+)(?P<x>[^/\.]+)(?P<y>(?:y|Y))(?:(?P<z>[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$}sD', ['w', 'x', 'y', 'z', '_format'], [
['variable', '.', '[^/]++', '_format'],
['variable', '', '[^/\.]++', 'z'],
['variable', '', '(?:y|Y)', 'y'],
@ -158,7 +158,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a format',
['/foo/{bar}.{_format}'],
'/foo', '#^/foo/(?P<bar>[^/\.]++)\.(?P<_format>[^/]++)$#sD', ['bar', '_format'], [
'/foo', '{^/foo/(?P<bar>[^/\.]++)\.(?P<_format>[^/]++)$}sD', ['bar', '_format'], [
['variable', '.', '[^/]++', '_format'],
['variable', '/', '[^/\.]++', 'bar'],
['text', '/foo'],
@ -168,7 +168,7 @@ class RouteCompilerTest extends TestCase
[
'Static non UTF-8 route',
["/fo\xE9"],
"/fo\xE9", "#^/fo\xE9$#sD", [], [
"/fo\xE9", "{^/fo\xE9$}sD", [], [
['text', "/fo\xE9"],
],
],
@ -176,7 +176,7 @@ class RouteCompilerTest extends TestCase
[
'Route with an explicit UTF-8 requirement',
['/{bar}', ['bar' => null], ['bar' => '.'], ['utf8' => true]],
'', '#^/(?P<bar>.)?$#sDu', ['bar'], [
'', '{^/(?P<bar>.)?$}sDu', ['bar'], [
['variable', '/', '.', 'bar', true],
],
],
@ -205,7 +205,7 @@ class RouteCompilerTest extends TestCase
[
'Static UTF-8 route',
['/foé'],
'/foé', '#^/foé$#sDu', [], [
'/foé', '{^/foé$}sDu', [], [
['text', '/foé'],
],
'patterns',
@ -214,7 +214,7 @@ class RouteCompilerTest extends TestCase
[
'Route with an implicit UTF-8 requirement',
['/{bar}', ['bar' => null], ['bar' => 'é']],
'', '#^/(?P<bar>é)?$#sDu', ['bar'], [
'', '{^/(?P<bar>é)?$}sDu', ['bar'], [
['variable', '/', 'é', 'bar', true],
],
'requirements',
@ -223,7 +223,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a UTF-8 class requirement',
['/{bar}', ['bar' => null], ['bar' => '\pM']],
'', '#^/(?P<bar>\pM)?$#sDu', ['bar'], [
'', '{^/(?P<bar>\pM)?$}sDu', ['bar'], [
['variable', '/', '\pM', 'bar', true],
],
'requirements',
@ -232,7 +232,7 @@ class RouteCompilerTest extends TestCase
[
'Route with a UTF-8 separator',
['/foo/{bar}§{_format}', [], [], ['compiler_class' => Utf8RouteCompiler::class]],
'/foo', '#^/foo/(?P<bar>[^/§]++)§(?P<_format>[^/]++)$#sDu', ['bar', '_format'], [
'/foo', '{^/foo/(?P<bar>[^/§]++)§(?P<_format>[^/]++)$}sDu', ['bar', '_format'], [
['variable', '§', '[^/]++', '_format', true],
['variable', '/', '[^/§]++', 'bar', true],
['text', '/foo'],
@ -318,21 +318,21 @@ class RouteCompilerTest extends TestCase
[
'Route with host pattern',
['/hello', [], [], [], 'www.example.com'],
'/hello', '#^/hello$#sD', [], [], [
'/hello', '{^/hello$}sD', [], [], [
['text', '/hello'],
],
'#^www\.example\.com$#sDi', [], [
'{^www\.example\.com$}sDi', [], [
['text', 'www.example.com'],
],
],
[
'Route with host pattern and some variables',
['/hello/{name}', [], [], [], 'www.example.{tld}'],
'/hello', '#^/hello/(?P<name>[^/]++)$#sD', ['tld', 'name'], ['name'], [
'/hello', '{^/hello/(?P<name>[^/]++)$}sD', ['tld', 'name'], ['name'], [
['variable', '/', '[^/]++', 'name'],
['text', '/hello'],
],
'#^www\.example\.(?P<tld>[^\.]++)$#sDi', ['tld'], [
'{^www\.example\.(?P<tld>[^\.]++)$}sDi', ['tld'], [
['variable', '.', '[^\.]++', 'tld'],
['text', 'www.example'],
],
@ -340,10 +340,10 @@ class RouteCompilerTest extends TestCase
[
'Route with variable at beginning of host',
['/hello', [], [], [], '{locale}.example.{tld}'],
'/hello', '#^/hello$#sD', ['locale', 'tld'], [], [
'/hello', '{^/hello$}sD', ['locale', 'tld'], [], [
['text', '/hello'],
],
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#sDi', ['locale', 'tld'], [
'{^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$}sDi', ['locale', 'tld'], [
['variable', '.', '[^\.]++', 'tld'],
['text', '.example'],
['variable', '', '[^\.]++', 'locale'],
@ -352,10 +352,10 @@ class RouteCompilerTest extends TestCase
[
'Route with host variables that has a default value',
['/hello', ['locale' => 'a', 'tld' => 'b'], [], [], '{locale}.example.{tld}'],
'/hello', '#^/hello$#sD', ['locale', 'tld'], [], [
'/hello', '{^/hello$}sD', ['locale', 'tld'], [], [
['text', '/hello'],
],
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#sDi', ['locale', 'tld'], [
'{^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$}sDi', ['locale', 'tld'], [
['variable', '.', '[^\.]++', 'tld'],
['text', '.example'],
['variable', '', '[^\.]++', 'locale'],
@ -383,12 +383,12 @@ class RouteCompilerTest extends TestCase
public function provideRemoveCapturingGroup()
{
yield ['#^/(?P<foo>a(?:b|c)(?:d|e)f)$#sD', 'a(b|c)(d|e)f'];
yield ['#^/(?P<foo>a\(b\)c)$#sD', 'a\(b\)c'];
yield ['#^/(?P<foo>(?:b))$#sD', '(?:b)'];
yield ['#^/(?P<foo>(?(b)b))$#sD', '(?(b)b)'];
yield ['#^/(?P<foo>(*F))$#sD', '(*F)'];
yield ['#^/(?P<foo>(?:(?:foo)))$#sD', '((foo))'];
yield ['{^/(?P<foo>a(?:b|c)(?:d|e)f)$}sD', 'a(b|c)(d|e)f'];
yield ['{^/(?P<foo>a\(b\)c)$}sD', 'a\(b\)c'];
yield ['{^/(?P<foo>(?:b))$}sD', '(?:b)'];
yield ['{^/(?P<foo>(?(b)b))$}sD', '(?(b)b)'];
yield ['{^/(?P<foo>(*F))$}sD', '(*F)'];
yield ['{^/(?P<foo>(?:(?:foo)))$}sD', '((foo))'];
}
}

View File

@ -260,7 +260,7 @@ class RouteTest extends TestCase
*/
public function testSerializedRepresentationKeepsWorking()
{
$serialized = 'C:31:"Symfony\Component\Routing\Route":936:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":571:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:31:"#^/prefix(?:/(?P<foo>\d+))?$#sD";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:40:"#^(?P<locale>[^\.]++)\.example\.net$#sDi";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}';
$serialized = 'C:31:"Symfony\Component\Routing\Route":936:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":571:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:31:"{^/prefix(?:/(?P<foo>\d+))?$}sD";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:40:"{^(?P<locale>[^\.]++)\.example\.net$}sDi";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}';
$unserialized = unserialize($serialized);
$route = new Route('/prefix/{foo}', ['foo' => 'default'], ['foo' => '\d+']);