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\DependencyInjection\Reference;
use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Routing\Route; use Symfony\Component\Routing\Route;
use Symfony\Component\Routing\CompiledRoute;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollection;
class ObjectsProvider class ObjectsProvider
@ -35,7 +36,7 @@ class ObjectsProvider
public static function getRoutes() public static function getRoutes()
{ {
return array( return array(
'route_1' => new Route( 'route_1' => new RouteStub(
'/hello/{name}', '/hello/{name}',
array('name' => 'Joseph'), array('name' => 'Joseph'),
array('name' => '[a-z]+'), array('name' => '[a-z]+'),
@ -44,7 +45,7 @@ class ObjectsProvider
array('http', 'https'), array('http', 'https'),
array('get', 'head') array('get', 'head')
), ),
'route_2' => new Route( 'route_2' => new RouteStub(
'/name/add', '/name/add',
array(), array(),
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}", "path": "\/hello\/{name}",
"pathRegex": "#^\/hello(?:\/(?P<name>[a-z]+))?$#s", "pathRegex": "#PATH_REGEX#",
"host": "localhost", "host": "localhost",
"hostRegex": "#^localhost$#si", "hostRegex": "#HOST_REGEX#",
"scheme": "http|https", "scheme": "http|https",
"method": "GET|HEAD", "method": "GET|HEAD",
"class": "Symfony\\Component\\Routing\\Route", "class": "Symfony\\Bundle\\FrameworkBundle\\Tests\\Console\\Descriptor\\RouteStub",
"defaults": { "defaults": {
"name": "Joseph" "name": "Joseph"
}, },

View File

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

View File

@ -1,10 +1,10 @@
<comment>Path</comment> /hello/{name} <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</comment> localhost
<comment>Host Regex</comment> #^localhost$#si <comment>Host Regex</comment> #HOST_REGEX#
<comment>Scheme</comment> http|https <comment>Scheme</comment> http|https
<comment>Method</comment> GET|HEAD <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>Defaults</comment> name: Joseph
<comment>Requirements</comment> name: [a-z]+ <comment>Requirements</comment> name: [a-z]+
<comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler <comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler

View File

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

View File

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

View File

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

View File

@ -1,10 +1,10 @@
<comment>Path</comment> /name/add <comment>Path</comment> /name/add
<comment>Path Regex</comment> #^/name/add$#s <comment>Path Regex</comment> #PATH_REGEX#
<comment>Host</comment> localhost <comment>Host</comment> localhost
<comment>Host Regex</comment> #^localhost$#si <comment>Host Regex</comment> #HOST_REGEX#
<comment>Scheme</comment> http|https <comment>Scheme</comment> http|https
<comment>Method</comment> PUT|POST <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>Defaults</comment> NONE
<comment>Requirements</comment> NO CUSTOM <comment>Requirements</comment> NO CUSTOM
<comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler <comment>Options</comment> compiler_class: Symfony\Component\Routing\RouteCompiler

View File

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

View File

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

View File

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

View File

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

View File

@ -177,7 +177,7 @@ class RouteCompiler implements RouteCompilerInterface
return array( return array(
'staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '', '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), 'tokens' => array_reverse($tokens),
'variables' => $variables, 'variables' => $variables,
); );

View File

@ -23,13 +23,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
$request = $this->request ?: $this->createRequest($pathinfo); $request = $this->request ?: $this->createRequest($pathinfo);
// foo // 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',)); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
} }
if (0 === strpos($pathinfo, '/bar')) { if (0 === strpos($pathinfo, '/bar')) {
// 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'))) { if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD')); $allow = array_merge($allow, array('GET', 'HEAD'));
goto not_bar; goto not_bar;
@ -40,7 +40,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
not_bar: not_bar:
// barhead // 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'))) { if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD')); $allow = array_merge($allow, array('GET', 'HEAD'));
goto not_barhead; goto not_barhead;
@ -72,12 +72,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
// baz4 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
} }
// baz5 // baz5
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) { if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'POST') { if ($this->context->getMethod() != 'POST') {
$allow[] = 'POST'; $allow[] = 'POST';
goto not_baz5; goto not_baz5;
@ -88,7 +88,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
not_baz5: not_baz5:
// baz.baz6 // 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') { if ($this->context->getMethod() != 'PUT') {
$allow[] = 'PUT'; $allow[] = 'PUT';
goto not_bazbaz6; goto not_bazbaz6;
@ -106,7 +106,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
// quoter // 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 ()); 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')) {
if (0 === strpos($pathinfo, '/a/b\'b')) { if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo1 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
} }
// bar1 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
} }
} }
// overridden // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
} }
if (0 === strpos($pathinfo, '/a/b\'b')) { if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo2 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
} }
// bar2 // 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 ()); 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')) { if (0 === strpos($pathinfo, '/multi')) {
// helloWorld // 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!',)); 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 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
} }
// bar3 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
} }
@ -184,7 +184,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
// foo4 // 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 ()); 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(); $host = $this->context->getHost();
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) {
// route1 // route1
if ('/route1' === $pathinfo) { if ('/route1' === $pathinfo) {
return array('_route' => 'route1'); 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 // route3
if ('/c2/route3' === $pathinfo) { if ('/c2/route3' === $pathinfo) {
return array('_route' => 'route3'); 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 // route4
if ('/route4' === $pathinfo) { if ('/route4' === $pathinfo) {
return array('_route' => 'route4'); 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 // route5
if ('/route5' === $pathinfo) { if ('/route5' === $pathinfo) {
return array('_route' => 'route5'); return array('_route' => 'route5');
@ -234,7 +234,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
return array('_route' => 'route6'); 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')) { if (0 === strpos($pathinfo, '/route1')) {
// route11 // route11
if ('/route11' === $pathinfo) { if ('/route11' === $pathinfo) {
@ -247,12 +247,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
} }
// route13 // 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 ()); return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
} }
// route14 // 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',)); 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 // 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 ()); 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')) { if (0 === strpos($pathinfo, '/route1')) {
// route16 // 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',)); 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')) { if (0 === strpos($pathinfo, '/a/b')) {
// 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
} }
// c // 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 ()); 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); $request = $this->request ?: $this->createRequest($pathinfo);
// foo // 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',)); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
} }
if (0 === strpos($pathinfo, '/bar')) { if (0 === strpos($pathinfo, '/bar')) {
// 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'))) { if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD')); $allow = array_merge($allow, array('GET', 'HEAD'));
goto not_bar; goto not_bar;
@ -40,7 +40,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
not_bar: not_bar:
// barhead // 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'))) { if (!in_array($this->context->getMethod(), array('GET', 'HEAD'))) {
$allow = array_merge($allow, array('GET', 'HEAD')); $allow = array_merge($allow, array('GET', 'HEAD'));
goto not_barhead; goto not_barhead;
@ -81,7 +81,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
// baz4 // baz4
if (preg_match('#^/test/(?P<foo>[^/]++)/?$#s', $pathinfo, $matches)) { if (preg_match('#^/test/(?P<foo>[^/]++)/?$#sD', $pathinfo, $matches)) {
if ('/' === substr($pathinfo, -1)) { if ('/' === substr($pathinfo, -1)) {
// no-op // no-op
} elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) { } elseif (!in_array($this->context->getMethod(), array('HEAD', 'GET'))) {
@ -95,7 +95,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
not_baz4: not_baz4:
// baz5 // baz5
if (preg_match('#^/test/(?P<foo>[^/]++)/$#s', $pathinfo, $matches)) { if (preg_match('#^/test/(?P<foo>[^/]++)/$#sD', $pathinfo, $matches)) {
if ($this->context->getMethod() != 'POST') { if ($this->context->getMethod() != 'POST') {
$allow[] = 'POST'; $allow[] = 'POST';
goto not_baz5; goto not_baz5;
@ -106,7 +106,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
not_baz5: not_baz5:
// baz.baz6 // 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') { if ($this->context->getMethod() != 'PUT') {
$allow[] = 'PUT'; $allow[] = 'PUT';
goto not_bazbaz6; goto not_bazbaz6;
@ -124,7 +124,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
// quoter // 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 ()); 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')) {
if (0 === strpos($pathinfo, '/a/b\'b')) { if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo1 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
} }
// bar1 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
} }
} }
// overridden // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
} }
if (0 === strpos($pathinfo, '/a/b\'b')) { if (0 === strpos($pathinfo, '/a/b\'b')) {
// foo2 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
} }
// bar2 // 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 ()); 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')) { if (0 === strpos($pathinfo, '/multi')) {
// helloWorld // 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!',)); 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 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
} }
// bar3 // 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
} }
@ -211,7 +211,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
// foo4 // 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 ()); 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(); $host = $this->context->getHost();
if (preg_match('#^a\\.example\\.com$#si', $host, $hostMatches)) { if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) {
// route1 // route1
if ('/route1' === $pathinfo) { if ('/route1' === $pathinfo) {
return array('_route' => 'route1'); 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 // route3
if ('/c2/route3' === $pathinfo) { if ('/c2/route3' === $pathinfo) {
return array('_route' => 'route3'); 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 // route4
if ('/route4' === $pathinfo) { if ('/route4' === $pathinfo) {
return array('_route' => 'route4'); 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 // route5
if ('/route5' === $pathinfo) { if ('/route5' === $pathinfo) {
return array('_route' => 'route5'); return array('_route' => 'route5');
@ -261,7 +261,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
return array('_route' => 'route6'); 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')) { if (0 === strpos($pathinfo, '/route1')) {
// route11 // route11
if ('/route11' === $pathinfo) { if ('/route11' === $pathinfo) {
@ -274,12 +274,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
} }
// route13 // 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 ()); return $this->mergeDefaults(array_replace($hostMatches, $matches, array('_route' => 'route13')), array ());
} }
// route14 // 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',)); 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 // 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 ()); 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')) { if (0 === strpos($pathinfo, '/route1')) {
// route16 // 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',)); 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')) { if (0 === strpos($pathinfo, '/a/b')) {
// 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 ()); return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
} }
// c // 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 ()); 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 // 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 ()); 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')); $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() public function testMatchNonAlpha()
{ {
$collection = new RouteCollection(); $collection = new RouteCollection();

View File

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

View File

@ -272,7 +272,7 @@ class RouteTest extends TestCase
*/ */
public function testSerializedRepresentationKeepsWorking() 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); $unserialized = unserialize($serialized);
$route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+')); $route = new Route('/prefix/{foo}', array('foo' => 'default'), array('foo' => '\d+'));