Skip to content

Commit

Permalink
bug #27601 [Routing] fix URL generation with look-around requirements…
Browse files Browse the repository at this point in the history
… (nasimnabavi)

This PR was merged into the 3.4 branch.

Discussion
----------

[Routing] fix URL generation with look-around requirements

| Q             | A
| ------------- | ---
| Branch?       | 2.8 up to 4.1 for bug fixes
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #10548
| License       | MIT
| Doc PR        |

If you have a non-catchable pattern in requirements like f.e. a positive lookahead (.+(?=/foo/)), the generator will not accept the parameter as the parameter itself cannot fulfil the requirement, but only matches in the context of the entire path.
This fix looks for lookAround in the path and ignores checking the requirements if any lookAround exists.

Commits
-------

c474451 [Routing] fix URL generation with look-around requirements
  • Loading branch information
fabpot committed Feb 21, 2019
2 parents 4b56617 + c474451 commit 5ac4c2a
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 3 deletions.
7 changes: 4 additions & 3 deletions src/Symfony/Component/Routing/Generator/UrlGenerator.php
Expand Up @@ -140,8 +140,8 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
foreach ($tokens as $token) {
if ('variable' === $token[0]) {
if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
// check requirement
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
// check requirement (while ignoring look-around patterns)
if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
if ($this->strictRequirements) {
throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
}
Expand Down Expand Up @@ -195,7 +195,8 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$routeHost = '';
foreach ($hostTokens as $token) {
if ('variable' === $token[0]) {
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
// check requirement (while ignoring look-around patterns)
if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
if ($this->strictRequirements) {
throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
}
Expand Down
17 changes: 17 additions & 0 deletions src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
Expand Up @@ -703,6 +703,23 @@ public function testFragmentsCanBeDefinedAsDefaults()
$this->assertEquals('/app.php/testing#fragment', $url);
}

/**
* @dataProvider provideLookAroundRequirementsInPath
*/
public function testLookRoundRequirementsInPath($expected, $path, $requirement)
{
$routes = $this->getRoutes('test', new Route($path, array(), array('foo' => $requirement, 'baz' => '.+?')));
$this->assertSame($expected, $this->getGenerator($routes)->generate('test', array('foo' => 'a/b', 'baz' => 'c/d/e')));
}

public function provideLookAroundRequirementsInPath()
{
yield array('/app.php/a/b/b%28ar/c/d/e', '/{foo}/b(ar/{baz}', '.+(?=/b\\(ar/)');
yield array('/app.php/a/b/bar/c/d/e', '/{foo}/bar/{baz}', '.+(?!$)');
yield array('/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?<=/bar/).+');
yield array('/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?<!^).+');
}

protected function getGenerator(RouteCollection $routes, array $parameters = [], $logger = null)
{
$context = new RequestContext('/app.php');
Expand Down

0 comments on commit 5ac4c2a

Please sign in to comment.