8000 [Routing] Allow force-generation of trailing parameters using eg \"/e… · symfony/symfony@9fab3d6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 9fab3d6

Browse files
luchaninovnicolas-grekas
authored andcommitted
[Routing] Allow force-generation of trailing parameters using eg \"/exports/news.{!_format}\"
1 parent f2590d1 commit 9fab3d6

File tree

4 files changed

+48
-6
lines changed

4 files changed

+48
-6
lines changed

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

Lines changed: 10 additions & 5 deletion 8000 s
Original file line numberDiff line numberDiff line change
@@ -156,21 +156,26 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
156156
$message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.';
157157
foreach ($tokens as $token) {
158158
if ('variable' === $token[0]) {
159-
if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
159+
$varName = $token[3];
160+
if ($important = ('!' === $varName[0])) {
161+
$varName = substr($varName, 1);
162+
}
163+
164+
if (!$optional || $important || !array_key_exists($varName, $defaults) || (null !== $mergedParams[$varName] && (string) $mergedParams[$varName] !== (string) $defaults[$varName])) {
160165
// check requirement
161-
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
166+
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$varName])) {
162167
if ($this->strictRequirements) {
163-
throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]])));
168+
throw new InvalidParameterException(strtr($message, array('{parameter}' => $varName, '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$varName])));
164169
}
165170

166171
if ($this->logger) {
167-
$this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]));
172+
$this->logger->error($message, array('parameter' => $varName, 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$varName]));
168173
}
169174

170175
return;
171176
}
172177

173-
$url = $token[1].$mergedParams[$token[3]].$url;
178+
$url = $token[1].$mergedParams[$varName].$url;
174179
$optional = false;
175180
}
176181
} else {

src/Symfony/Component/Routing/RouteCompiler.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ private static function compilePattern(Route $route, $pattern, $isHost)
111111

112112
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
113113
// in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
114-
preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
114+
preg_match_all('#\{!?\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
115115
foreach ($matches as $match) {
116116
$varName = substr($match[0][0], 1, -1);
117117
// get all static text preceding the current variable
@@ -184,6 +184,9 @@ private static function compilePattern(Route $route, $pattern, $isHost)
184184
}
185185

186186
$tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName);
187+
if ('!' === $varName[0]) {
188+
$varName = substr($varName, 1);
189+
}
187190
$variables[] = $varName;
188191
}
189192

@@ -283,6 +286,10 @@ private static function computeRegexp(array $tokens, int $index, int $firstOptio
283286
// Text tokens
284287
return preg_quote($token[1], self::REGEX_DELIMITER);
285288
} else {
289+
if ('variable' === $token[0] && '!' === $token[3][0]) {
290+
$token[3] = substr($token[3], 1);
291+
}
292+
286293
// Variable tokens
287294
if (0 === $index && 0 === $firstOptional) {
288295
// When the only token is an optional variable token, the separator is required

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,27 @@ public function testDefaultRequirementOfVariable()
397397
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
398398
}
399399

400+
public function testImportantVariable()
401+
{
402+
$routes = $this->getRoutes('test', (new Route('/{page}.{!_format}'))->addDefaults(array('_format' => 'mobile.html')));
403+
$generator = $this->getGenerator($routes);
404+
405+
$this->assertSame('/app.php/index.xml', $generator->generate('test', array('page' => 'index', '_format' => 'xml')));
406+
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html')));
407+
$this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index')));
408+
}
409+
410+
/**
411+
* @expectedException \Symfony\Component\Routing\Exception\MissingMandatoryParametersException
412+
*/
413+
public function testImportantVariableWithNoDefault()
414+
{
415+
$routes = $this->getRoutes('test', new Route('/{page}.{!_format}'));
416+
$generator = $this->getGenerator($routes);
417+
418+
$generator->generate('test', array('page' => 'index'));
419+
}
420+
400421
/**
401422
* @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
402423
*/

src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,15 @@ public function testMatchSpecialRouteName()
177177
$this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar'));
178178
}
179179

180+
public function testMatchImportantVariable()
181+
{
182+
$collection = new RouteCollection();
183+
$collection->add('index', new Route('/index.{!_format}', array('_format' => 'xml')));
184+
185+
$matcher = $this->getUrlMatcher($collection);
186+
$this->assertEquals(array('_route' => 'index', '_format' => 'xml'), $matcher->match('/index.xml'));
187+
}
188+
180189
/**
181190
* @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException
182191
*/

0 commit comments

Comments
 (0)
0