Skip to content

Commit 9aa09e1

Browse files
nasimnabavinicolas-grekas
authored andcommitted
[Routing] fix URL generation with look-around requirements
1 parent 7f310b4 commit 9aa09e1

File tree

2 files changed

+45
-3
lines changed

2 files changed

+45
-3
lines changed

src/Symfony/Component/Routing/Generator/UrlGenerator.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,14 +140,18 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
140140
foreach ($tokens as $token) {
141141
if ('variable' === $token[0]) {
142142
if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
143+
$variablePattern = $token[2];
144+
if ($this->hasLookAround($token[2])) {
145+
$variablePattern = $this->removeLookAround($token[2]);
146+
}
143147
// check requirement
144-
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
148+
if (null !== $this->strictRequirements && !preg_match('#^'.$variablePattern.'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
145149
if ($this->strictRequirements) {
146-
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]])));
150+
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $variablePattern, '{given}' => $mergedParams[$token[3]])));
147151
}
148152

149153
if ($this->logger) {
150-
$this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]));
154+
$this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $variablePattern, 'given' => $mergedParams[$token[3]]));
151155
}
152156

153157
return;
@@ -318,4 +322,18 @@ public static function getRelativePath($basePath, $targetPath)
318322
|| false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
319323
? "./$path" : $path;
320324
}
325+
326+
private function hasLookAround($path)
327+
{
328+
if (false === $i = strpos($path, '(?')) {
329+
return false;
330+
}
331+
332+
return false !== strpos($path, '(?=', $i) || false !== strpos($path, '(?<=', $i) || false !== strpos($path, '(?!', $i) || false !== strpos($path, '(?<!', $i);
333+
}
334+
335+
private function removeLookAround($path)
336+
{
337+
return preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $path);
338+
}
321339
}

src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,30 @@ public function testFragmentsCanBeDefinedAsDefaults()
703703
$this->assertEquals('/app.php/testing#fragment', $url);
704704
}
705705

706+
public function testLookAheadPositiveInRequirements()
707+
{
708+
$routes = $this->getRoutes('test', new Route('/{foo}/b(ar/{baz}', array(), array('foo' => '.+(?=/b\\(ar/)', 'baz' => '.+?')));
709+
$this->assertSame('/app.php/a/b/b%28ar/c/d/e', $this->getGenerator($routes)->generate('test', array('foo' => 'a/b', 'baz' => 'c/d/e')));
710+
}
711+
712+
public function testLookAheadNegativeInRequirementss()
713+
{
714+
$routes = $this->getRoutes('test', new Route('/{foo}/bar/{baz}', array(), array('foo' => '.+(?!$)', 'baz' => '.+?')));
715+
$this->assertSame('/app.php/a/b/bar/c/d/e', $this->getGenerator($routes)->generate('test', array('foo' => 'a/b', 'baz' => 'c/d/e')));
716+
}
717+
718+
public function testLookBehindPositiveInRequirements()
719+
{
720+
$routes = $this->getRoutes('test', new Route('/bar/{foo}/bam/{baz}', array(), array('foo' => '(?<=/bar/).+', 'baz' => '.+?')));
721+
$this->assertSame('/app.php/bar/a/b/bam/c/d/e', $this->getGenerator($routes)->generate('test', array('foo' => 'a/b', 'baz' => 'c/d/e')));
722+
}
723+
724+
public function testLookBehindNegativeInRequirements()
725+
{
726+
$routes = $this->getRoutes('test', new Route('/bar/{foo}/bam/{baz}', array(), array('foo' => '(?<!^).+', 'baz' => '.+?')));
727+
$this->assertSame('/app.php/bar/a/b/bam/c/d/e', $this->getGenerator($routes)->generate('test', array('foo' => 'a/b', 'baz' => 'c/d/e')));
728+
}
729+
706730
protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
707731
{
708732
$context = new RequestContext('/app.php');

0 commit comments

Comments
 (0)