bug #25373 Use the PCRE_DOLLAR_ENDONLY modifier in route regexes (mpdude)

This PR was squashed before being merged into the 2.7 branch (closes #25373).

Discussion
----------

Use the PCRE_DOLLAR_ENDONLY modifier in route regexes

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

`UrlMatcher::match($pathinfo)` applies `rawurldecode()` to the `$pathinfo` before trying to match it against the routes.

If the URL contains a percent-encoded trailing newline (like in `/foo%0a`), the default PHP PCRE will still consider `#^/foo$#` a match, as the `$` metacharacter will also match *immediately before* the final character *if it is a newline*. This behavior can be changed by applying the [`PCRE_DOLLAR_ENDONLY` modifier](http://php.net/manual/en/reference.pcre.pattern.modifiers.php).

Without this change, URLs with trailing `%0a` lead to weird notices further down the road, for example when the `RedirectableUrlMatcher` or its equivalent in `PhpMatcherDumper` kick in, look at the last character (this time actually the newline), append a `/` and try to redirect to the resulting URL. Ultimately, PHP will complain with `Warning: Header may not contain more than a single header, new line detected` when sending the `Location` header.

Commits
-------

f713a3e Use the PCRE_DOLLAR_ENDONLY modifier in route regexes
This commit is contained in:
Nicolas Grekas 2018-02-04 16:51:07 +01:00
commit 478fbdc241
19 changed files with 146 additions and 125 deletions

View File

@ -18,6 +18,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\CompiledRoute;
use Symfony\Component\Routing\RouteCollection;
class ObjectsProvider
@ -35,7 +36,7 @@ class ObjectsProvider
public static function getRoutes()
{
return array(
'route_1' => new Route(
'route_1' => new RouteStub(
'/hello/{name}',
array('name' => 'Joseph'),
array('name' => '[a-z]+'),
@ -44,7 +45,7 @@ class ObjectsProvider
array('http', 'https'),
array('get', 'head')
),
'route_2' => new Route(
'route_2' => new RouteStub(
'/name/add',
array(),
array(),
@ -207,3 +208,11 @@ class ExtendedCallableClass extends CallableClass
{
}
}
class RouteStub extends Route
{
public function compile()
{
return new CompiledRoute('', '#PATH_REGEX#', array(), array(), '#HOST_REGEX#');
}
}

View File

@ -1,11 +1,11 @@
{
"path": "\/hello\/{name}",
"pathRegex": "#^\/hello(?:\/(?P<name>[a-z]+))?$#s",
"pathRegex": "#PATH_REGEX#",
"host": "localhost",
"hostRegex": "#^localhost$#si",
"hostRegex": "#HOST_REGEX#",
"scheme": "http|https",
"method": "GET|HEAD",
"class": "Symfony\\Component\\Routing\\Route",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub",
"defaults": {
"name": "Joseph"
},

View File

@ -1,10 +1,10 @@
- Path: /hello/{name}
- Path Regex: #^/hello(?:/(?P<name>[a-z]+))?$#s
- Path Regex: #PATH_REGEX#
- Host: localhost
- Host Regex: #^localhost$#si
- Host Regex: #HOST_REGEX#
- Scheme: http|https
- Method: GET|HEAD
- Class: Symfony\Component\Routing\Route
- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub
- Defaults:
- `name`: Joseph
- Requirements:

View File

@ -1,10 +1,10 @@
<comment>Path</comment> /hello/{name}
<comment>Path Regex</comment> #^/hello(?:/(?P<name>[a-z]+))?$#s
<comment>Path Regex</comment> #PATH_REGEX#
<comment>Host</comment> localhost
<comment>Host Regex</comment> #^localhost$#si
<comment>Host Regex</comment> #HOST_REGEX#
<comment>Scheme</comment> http|https
<comment>Method</comment> GET|HEAD
<comment>Class</comment> Symfony\Component\Routing\Route
<comment>Class</comment> Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub
<comment>Defaults</comment> name: Joseph
<comment>Requirements</comment> name: [a-z]+
<comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<route class="Symfony\Component\Routing\Route">
<path regex="#^/hello(?:/(?P&lt;name&gt;[a-z]+))?$#s">/hello/{name}</path>
<host regex="#^localhost$#si">localhost</host>
<route class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub">
<path regex="#PATH_REGEX#">/hello/{name}</path>
<host regex="#HOST_REGEX#">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>GET</method>

View File

@ -1,11 +1,11 @@
{
"path": "\/name\/add",
"pathRegex": "#^\/name\/add$#s",
"pathRegex": "#PATH_REGEX#",
"host": "localhost",
"hostRegex": "#^localhost$#si",
"hostRegex": "#HOST_REGEX#",
"scheme": "http|https",
"method": "PUT|POST",
"class": "Symfony\\Component\\Routing\\Route",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub",
"defaults": [],
"requirements": "NO CUSTOM",
"options": {

View File

@ -1,10 +1,10 @@
- Path: /name/add
- Path Regex: #^/name/add$#s
- Path Regex: #PATH_REGEX#
- Host: localhost
- Host Regex: #^localhost$#si
- Host Regex: #HOST_REGEX#
- Scheme: http|https
- Method: PUT|POST
- Class: Symfony\Component\Routing\Route
- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub
- Defaults: NONE
- Requirements: NO CUSTOM
- Options:

View File

@ -1,10 +1,10 @@
<comment>Path</comment> /name/add
<comment>Path Regex</comment> #^/name/add$#s
<comment>Path Regex</comment> #PATH_REGEX#
<comment>Host</comment> localhost
<comment>Host Regex</comment> #^localhost$#si
<comment>Host Regex</comment> #HOST_REGEX#
<comment>Scheme</comment> http|https
<comment>Method</comment> PUT|POST
<comment>Class</comment> Symfony\Component\Routing\Route
<comment>Class</comment> Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub
<comment>Defaults</comment> NONE
<comment>Requirements</comment> NO CUSTOM
<comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<route class="Symfony\Component\Routing\Route">
<path regex="#^/name/add$#s">/name/add</path>
<host regex="#^localhost$#si">localhost</host>
<route class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub">
<path regex="#PATH_REGEX#">/name/add</path>
<host regex="#HOST_REGEX#">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>PUT</method>

View File

@ -1,12 +1,12 @@
{
"route_1": {
"path": "\/hello\/{name}",
"pathRegex": "#^\/hello(?:\/(?P<name>[a-z]+))?$#s",
"pathRegex": "#PATH_REGEX#",
"host": "localhost",
"hostRegex": "#^localhost$#si",
"hostRegex": "#HOST_REGEX#",
"scheme": "http|https",
"method": "GET|HEAD",
"class": "Symfony\\Component\\Routing\\Route",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub",
"defaults": {
"name": "Joseph"
},
@ -21,12 +21,12 @@
},
"route_2": {
"path": "\/name\/add",
"pathRegex": "#^\/name\/add$#s",
"pathRegex": "#PATH_REGEX#",
"host": "localhost",
"hostRegex": "#^localhost$#si",
"hostRegex": "#HOST_REGEX#",
"scheme": "http|https",
"method": "PUT|POST",
"class": "Symfony\\Component\\Routing\\Route",
"class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub",
"defaults": [],
"requirements": "NO CUSTOM",
"options": {

View File

@ -2,12 +2,12 @@ route_1
-------
- Path: /hello/{name}
- Path Regex: #^/hello(?:/(?P<name>[a-z]+))?$#s
- Path Regex: #PATH_REGEX#
- Host: localhost
- Host Regex: #^localhost$#si
- Host Regex: #HOST_REGEX#
- Scheme: http|https
- Method: GET|HEAD
- Class: Symfony\Component\Routing\Route
- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub
- Defaults:
- `name`: Joseph
- Requirements:
@ -22,12 +22,12 @@ route_2
-------
- Path: /name/add
- Path Regex: #^/name/add$#s
- Path Regex: #PATH_REGEX#
- Host: localhost
- Host Regex: #^localhost$#si
- Host Regex: #HOST_REGEX#
- Scheme: http|https
- Method: PUT|POST
- Class: Symfony\Component\Routing\Route
- Class: Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub
- Defaults: NONE
- Requirements: NO CUSTOM
- Options:

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<routes>
<route name="route_1" class="Symfony\Component\Routing\Route">
<path regex="#^/hello(?:/(?P&lt;name&gt;[a-z]+))?$#s">/hello/{name}</path>
<host regex="#^localhost$#si">localhost</host>
<route name="route_1" class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub">
<path regex="#PATH_REGEX#">/hello/{name}</path>
<host regex="#HOST_REGEX#">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>GET</method>
@ -19,9 +19,9 @@
<option key="opt2">val2</option>
</options>
</route>
<route name="route_2" class="Symfony\Component\Routing\Route">
<path regex="#^/name/add$#s">/name/add</path>
<host regex="#^localhost$#si">localhost</host>
<route name="route_2" class="Symfony\Bundle\FrameworkBundle\Tests\Console\Descriptor\RouteStub">
<path regex="#PATH_REGEX#">/name/add</path>
<host regex="#HOST_REGEX#">localhost</host>
<scheme>http</scheme>
<scheme>https</scheme>
<method>PUT</method>

View File

@ -177,7 +177,7 @@ class RouteCompiler implements RouteCompilerInterface
return array(
'staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '',
'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s'.($isHost ? 'i' : ''),
'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'sD'.($isHost ? 'i' : ''),
'tokens' => array_reverse($tokens),
'variables' => $variables,
);

View File

@ -23,13 +23,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
$request = $this->request ?: $this->createRequest($pathinfo);
// foo
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
}
if (0 === strpos($pathinfo, '/bar')) {
// bar
if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/bar/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD'));
goto not_bar;
@ -40,7 +40,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
not_bar:
// barhead
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD'));
goto not_barhead;
@ -72,12 +72,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
// baz4
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
}
// baz5
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'POST') {
$allow[] = 'POST';
goto not_baz5;
@ -88,7 +88,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
not_baz5:
// baz.baz6
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'PUT') {
$allow[] = 'PUT';
goto not_bazbaz6;
@ -106,7 +106,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
// quoter
if (preg_match('#^/(?P<quoter>[\']+)$#s', $pathinfo, $matches)) {
if (preg_match('#^/(?P<quoter>[\']+)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
}
@ -118,30 +118,30 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
if (0 === strpos($pathinfo, '/a')) {
if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo1
if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
}
// bar1
if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
}
}
// overridden
if (preg_match('#^/a/(?P<var>.*)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/(?P<var>.*)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
}
if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo2
if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
}
// bar2
if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
}
@ -151,7 +151,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
if (0 === strpos($pathinfo, '/multi')) {
// helloWorld
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',));
}
@ -168,12 +168,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
// foo3
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
}
// bar3
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
}
@ -184,7 +184,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
// foo4
if (preg_match('#^/aba/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/aba/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
}
@ -192,7 +192,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
$host = $this->context->getHost();
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) {
// route1
if ('/route1' === $pathinfo) {
return array('_route' => 'route1');
@ -205,7 +205,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^b\\.example\\.com$#sDi', $host, $hostMatches)) {
// route3
if ('/c2/route3' === $pathinfo) {
return array('_route' => 'route3');
@ -213,7 +213,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) {
// route4
if ('/route4' === $pathinfo) {
return array('_route' => 'route4');
@ -221,7 +221,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) {
// route5
if ('/route5' === $pathinfo) {
return array('_route' => 'route5');
@ -234,7 +234,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
return array('_route' => 'route6');
}
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', $host, $hostMatches)) {
if (0 === strpos($pathinfo, '/route1')) {
// route11
if ('/route11' === $pathinfo) {
@ -247,12 +247,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
// route13
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
}
// route14
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',));
}
@ -260,9 +260,9 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) {
// route15
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
}
@ -270,7 +270,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
if (0 === strpos($pathinfo, '/route1')) {
// route16
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',));
}
@ -289,12 +289,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
if (0 === strpos($pathinfo, '/a/b')) {
// b
if (preg_match('#^/a/b/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b/(?P<var>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
}
// c
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
}

View File

@ -23,13 +23,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
$request = $this->request ?: $this->createRequest($pathinfo);
// foo
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?P<bar>baz|symfony)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
}
if (0 === strpos($pathinfo, '/bar')) {
// bar
if (preg_match('#^/bar/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/bar/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD'));
goto not_bar;
@ -40,7 +40,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
not_bar:
// barhead
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD'));
goto not_barhead;
@ -81,7 +81,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
// baz4
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) {
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#sD', $pathinfo, $matches)) {
if ('/' === substr($pathinfo, -1)) {
// no-op
} elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
@ -95,7 +95,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
not_baz4:
// baz5
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'POST') {
$allow[] = 'POST';
goto not_baz5;
@ -106,7 +106,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
not_baz5:
// baz.baz6
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) {
if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'PUT') {
$allow[] = 'PUT';
goto not_bazbaz6;
@ -124,7 +124,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
// quoter
if (preg_match('#^/(?P<quoter>[\']+)$#s', $pathinfo, $matches)) {
if (preg_match('#^/(?P<quoter>[\']+)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
}
@ -136,30 +136,30 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/a')) {
if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo1
if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
}
// bar1
if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<bar>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
}
}
// overridden
if (preg_match('#^/a/(?P<var>.*)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/(?P<var>.*)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
}
if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo2
if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<foo1>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
}
// bar2
if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b\'b/(?P<bar1>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
}
@ -169,7 +169,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/multi')) {
// helloWorld
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P<who>[^/]++))?$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',));
}
@ -195,12 +195,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
// foo3
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
}
// bar3
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P<bar>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
}
@ -211,7 +211,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
// foo4
if (preg_match('#^/aba/(?P<foo>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/aba/(?P<foo>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
}
@ -219,7 +219,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
$host = $this->context->getHost();
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) {
// route1
if ('/route1' === $pathinfo) {
return array('_route' => 'route1');
@ -232,7 +232,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^b\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^b\\.example\\.com$#sDi', $host, $hostMatches)) {
// route3
if ('/c2/route3' === $pathinfo) {
return array('_route' => 'route3');
@ -240,7 +240,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) {
// route4
if ('/route4' === $pathinfo) {
return array('_route' => 'route4');
@ -248,7 +248,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) {
// route5
if ('/route5' === $pathinfo) {
return array('_route' => 'route5');
@ -261,7 +261,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
return array('_route' => 'route6');
}
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', $host, $hostMatches)) {
if (0 === strpos($pathinfo, '/route1')) {
// route11
if ('/route11' === $pathinfo) {
@ -274,12 +274,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
// route13
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
}
// route14
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',));
}
@ -287,9 +287,9 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
}
if (preg_match('#^c\\.example\\.com$#si', $host, $hostMatches)) {
if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) {
// route15
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
}
@ -297,7 +297,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/route1')) {
// route16
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P<name>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',));
}
@ -316,12 +316,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
if (0 === strpos($pathinfo, '/a/b')) {
// b
if (preg_match('#^/a/b/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/a/b/(?P<var>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
}
// c
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P<var>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
}

View File

@ -29,7 +29,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
}
// dynamic
if (preg_match('#^/rootprefix/(?P<var>[^/]++)$#s', $pathinfo, $matches)) {
if (preg_match('#^/rootprefix/(?P<var>[^/]++)$#sD', $pathinfo, $matches)) {
return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array ());
}

View File

@ -162,6 +162,18 @@ class UrlMatcherTest extends TestCase
$this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar'));
}
/**
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
*/
public function testTrailingEncodedNewlineIsNotOverlooked()
{
$collection = new RouteCollection();
$collection->add('foo', new Route('/foo'));
$matcher = $this->getUrlMatcher($collection);
$matcher->match('/foo%0a');
}
public function testMatchNonAlpha()
{
$collection = new RouteCollection();

View File

@ -38,7 +38,7 @@ class RouteCompilerTest extends TestCase
array(
'Static route',
array('/foo'),
'/foo', '#^/foo$#s', array(), array(
'/foo', '#^/foo$#sD', array(), array(
array('text', '/foo'),
),
),
@ -46,7 +46,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with a variable',
array('/foo/{bar}'),
'/foo', '#^/foo/(?P<bar>[^/]++)$#s', array('bar'), array(
'/foo', '#^/foo/(?P<bar>[^/]++)$#sD', array('bar'), array(
array('variable', '/', '[^/]++', 'bar'),
array('text', '/foo'),
),
@ -55,7 +55,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with a variable that has a default value',
array('/foo/{bar}', array('bar' => 'bar')),
'/foo', '#^/foo(?:/(?P<bar>[^/]++))?$#s', array('bar'), array(
'/foo', '#^/foo(?:/(?P<bar>[^/]++))?$#sD', array('bar'), array(
array('variable', '/', '[^/]++', 'bar'),
array('text', '/foo'),
),
@ -64,7 +64,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with several variables',
array('/foo/{bar}/{foobar}'),
'/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
'/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#sD', array('bar', 'foobar'), array(
array('variable', '/', '[^/]++', 'foobar'),
array('variable', '/', '[^/]++', 'bar'),
array('text', '/foo'),
@ -74,7 +74,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with several variables that have default values',
array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')),
'/foo', '#^/foo(?:/(?P<bar>[^/]++)(?:/(?P<foobar>[^/]++))?)?$#s', array('bar', 'foobar'), array(
'/foo', '#^/foo(?:/(?P<bar>[^/]++)(?:/(?P<foobar>[^/]++))?)?$#sD', array('bar', 'foobar'), array(
array('variable', '/', '[^/]++', 'foobar'),
array('variable', '/', '[^/]++', 'bar'),
array('text', '/foo'),
@ -84,7 +84,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with several variables but some of them have no default values',
array('/foo/{bar}/{foobar}', array('bar' => 'bar')),
'/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#s', array('bar', 'foobar'), array(
'/foo', '#^/foo/(?P<bar>[^/]++)/(?P<foobar>[^/]++)$#sD', array('bar', 'foobar'), array(
array('variable', '/', '[^/]++', 'foobar'),
array('variable', '/', '[^/]++', 'bar'),
array('text', '/foo'),
@ -94,7 +94,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with an optional variable as the first segment',
array('/{bar}', array('bar' => 'bar')),
'', '#^/(?P<bar>[^/]++)?$#s', array('bar'), array(
'', '#^/(?P<bar>[^/]++)?$#sD', array('bar'), array(
array('variable', '/', '[^/]++', 'bar'),
),
),
@ -102,7 +102,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with a requirement of 0',
array('/{bar}', array('bar' => null), array('bar' => '0')),
'', '#^/(?P<bar>0)?$#s', array('bar'), array(
'', '#^/(?P<bar>0)?$#sD', array('bar'), array(
array('variable', '/', '0', 'bar'),
),
),
@ -110,7 +110,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with an optional variable as the first segment with requirements',
array('/{bar}', array('bar' => 'bar'), array('bar' => '(foo|bar)')),
'', '#^/(?P<bar>(foo|bar))?$#s', array('bar'), array(
'', '#^/(?P<bar>(foo|bar))?$#sD', array('bar'), array(
array('variable', '/', '(foo|bar)', 'bar'),
),
),
@ -118,7 +118,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with only optional variables',
array('/{foo}/{bar}', array('foo' => 'foo', 'bar' => 'bar')),
'', '#^/(?P<foo>[^/]++)?(?:/(?P<bar>[^/]++))?$#s', array('foo', 'bar'), array(
'', '#^/(?P<foo>[^/]++)?(?:/(?P<bar>[^/]++))?$#sD', array('foo', 'bar'), array(
array('variable', '/', '[^/]++', 'bar'),
array('variable', '/', '[^/]++', 'foo'),
),
@ -127,7 +127,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with a variable in last position',
array('/foo-{bar}'),
'/foo', '#^/foo\-(?P<bar>[^/]++)$#s', array('bar'), array(
'/foo', '#^/foo\-(?P<bar>[^/]++)$#sD', array('bar'), array(
array('variable', '-', '[^/]++', 'bar'),
array('text', '/foo'),
),
@ -136,7 +136,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with nested placeholders',
array('/{static{var}static}'),
'/{static', '#^/\{static(?P<var>[^/]+)static\}$#s', array('var'), array(
'/{static', '#^/\{static(?P<var>[^/]+)static\}$#sD', array('var'), array(
array('text', 'static}'),
array('variable', '', '[^/]+', 'var'),
array('text', '/{static'),
@ -146,7 +146,7 @@ class RouteCompilerTest extends TestCase
array(
'Route without separator between variables',
array('/{w}{x}{y}{z}.{_format}', array('z' => 'default-z', '_format' => 'html'), array('y' => '(y|Y)')),
'', '#^/(?P<w>[^/\.]+)(?P<x>[^/\.]+)(?P<y>(y|Y))(?:(?P<z>[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#s', array('w', 'x', 'y', 'z', '_format'), array(
'', '#^/(?P<w>[^/\.]+)(?P<x>[^/\.]+)(?P<y>(y|Y))(?:(?P<z>[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#sD', array('w', 'x', 'y', 'z', '_format'), array(
array('variable', '.', '[^/]++', '_format'),
array('variable', '', '[^/\.]++', 'z'),
array('variable', '', '(y|Y)', 'y'),
@ -158,7 +158,7 @@ class RouteCompilerTest extends TestCase
array(
'Route with a format',
array('/foo/{bar}.{_format}'),
'/foo', '#^/foo/(?P<bar>[^/\.]++)\.(?P<_format>[^/]++)$#s', array('bar', '_format'), array(
'/foo', '#^/foo/(?P<bar>[^/\.]++)\.(?P<_format>[^/]++)$#sD', array('bar', '_format'), array(
array('variable', '.', '[^/]++', '_format'),
array('variable', '/', '[^/\.]++', 'bar'),
array('text', '/foo'),
@ -221,21 +221,21 @@ class RouteCompilerTest extends TestCase
array(
'Route with host pattern',
array('/hello', array(), array(), array(), 'www.example.com'),
'/hello', '#^/hello$#s', array(), array(), array(
'/hello', '#^/hello$#sD', array(), array(), array(
array('text', '/hello'),
),
'#^www\.example\.com$#si', array(), array(
'#^www\.example\.com$#sDi', array(), array(
array('text', 'www.example.com'),
),
),
array(
'Route with host pattern and some variables',
array('/hello/{name}', array(), array(), array(), 'www.example.{tld}'),
'/hello', '#^/hello/(?P<name>[^/]++)$#s', array('tld', 'name'), array('name'), array(
'/hello', '#^/hello/(?P<name>[^/]++)$#sD', array('tld', 'name'), array('name'), array(
array('variable', '/', '[^/]++', 'name'),
array('text', '/hello'),
),
'#^www\.example\.(?P<tld>[^\.]++)$#si', array('tld'), array(
'#^www\.example\.(?P<tld>[^\.]++)$#sDi', array('tld'), array(
array('variable', '.', '[^\.]++', 'tld'),
array('text', 'www.example'),
),
@ -243,10 +243,10 @@ class RouteCompilerTest extends TestCase
array(
'Route with variable at beginning of host',
array('/hello', array(), array(), array(), '{locale}.example.{tld}'),
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
'/hello', '#^/hello$#sD', array('locale', 'tld'), array(), array(
array('text', '/hello'),
),
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#sDi', array('locale', 'tld'), array(
array('variable', '.', '[^\.]++', 'tld'),
array('text', '.example'),
array('variable', '', '[^\.]++', 'locale'),
@ -255,10 +255,10 @@ class RouteCompilerTest extends TestCase
array(
'Route with host variables that has a default value',
array('/hello', array('locale' => 'a', 'tld' => 'b'), array(), array(), '{locale}.example.{tld}'),
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
'/hello', '#^/hello$#sD', array('locale', 'tld'), array(), array(
array('text', '/hello'),
),
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#si', array('locale', 'tld'), array(
'#^(?P<locale>[^\.]++)\.example\.(?P<tld>[^\.]++)$#sDi', array('locale', 'tld'), array(
array('variable', '.', '[^\.]++', 'tld'),
array('text', '.example'),
array('variable', '', '[^\.]++', 'locale'),

View File

@ -272,7 +272,7 @@ class RouteTest extends TestCase
*/
public function testSerializedRepresentationKeepsWorking()
{
$serialized = 'C:31:"Symfony\Component\Routing\Route":934:{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":569:{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:30:"#^/prefix(?:/(?P<foo>\d+))?$#s";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:39:"#^(?P<locale>[^\.]++)\.example\.net$#si";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}', array('foo' => 'default'), array('foo' => '\d+'));