From 9fab3d62ecd1f8c338bbcc1b1a800999723a0402 Mon Sep 17 00:00:00 2001 From: Vladimir Luchaninov Date: Fri, 14 Dec 2018 01:52:56 +0200 Subject: [PATCH] [Routing] Allow force-generation of trailing parameters using eg \"/exports/news.{!_format}\" --- .../Routing/Generator/UrlGenerator.php | 15 ++++++++----- .../Component/Routing/RouteCompiler.php | 9 +++++++- .../Tests/Generator/UrlGeneratorTest.php | 21 +++++++++++++++++++ .../Routing/Tests/Matcher/UrlMatcherTest.php | 9 ++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php index 91794423234fe..fbb5cca0b0a0f 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php @@ -156,21 +156,26 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa $message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.'; 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]]) { + $varName = $token[3]; + if ($important = ('!' === $varName[0])) { + $varName = substr($varName, 1); + } + + if (!$optional || $important || !array_key_exists($varName, $defaults) || (null !== $mergedParams[$varName] && (string) $mergedParams[$varName] !== (string) $defaults[$varName])) { // check requirement - if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) { + if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$varName])) { if ($this->strictRequirements) { - throw new InvalidParameterException(strtr($message, array('{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]))); + throw new InvalidParameterException(strtr($message, array('{parameter}' => $varName, '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$varName]))); } if ($this->logger) { - $this->logger->error($message, array('parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]])); + $this->logger->error($message, array('parameter' => $varName, 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$varName])); } return; } - $url = $token[1].$mergedParams[$token[3]].$url; + $url = $token[1].$mergedParams[$varName].$url; $optional = false; } } else { diff --git a/src/Symfony/Component/Routing/RouteCompiler.php b/src/Symfony/Component/Routing/RouteCompiler.php index 66b291b831adc..abab86be7a95b 100644 --- a/src/Symfony/Component/Routing/RouteCompiler.php +++ b/src/Symfony/Component/Routing/RouteCompiler.php @@ -111,7 +111,7 @@ private static function compilePattern(Route $route, $pattern, $isHost) // Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable // in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself. - preg_match_all('#\{\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + preg_match_all('#\{!?\w+\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); foreach ($matches as $match) { $varName = substr($match[0][0], 1, -1); // get all static text preceding the current variable @@ -184,6 +184,9 @@ private static function compilePattern(Route $route, $pattern, $isHost) } $tokens[] = array('variable', $isSeparator ? $precedingChar : '', $regexp, $varName); + if ('!' === $varName[0]) { + $varName = substr($varName, 1); + } $variables[] = $varName; } @@ -283,6 +286,10 @@ private static function computeRegexp(array $tokens, int $index, int $firstOptio // Text tokens return preg_quote($token[1], self::REGEX_DELIMITER); } else { + if ('variable' === $token[0] && '!' === $token[3][0]) { + $token[3] = substr($token[3], 1); + } + // Variable tokens if (0 === $index && 0 === $firstOptional) { // When the only token is an optional variable token, the separator is required diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php index d4bf18ccac929..bb248d0fcd997 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php @@ -397,6 +397,27 @@ public function testDefaultRequirementOfVariable() $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html'))); } + public function testImportantVariable() + { + $routes = $this->getRoutes('test', (new Route('/{page}.{!_format}'))->addDefaults(array('_format' => 'mobile.html'))); + $generator = $this->getGenerator($routes); + + $this->assertSame('/app.php/index.xml', $generator->generate('test', array('page' => 'index', '_format' => 'xml'))); + $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index', '_format' => 'mobile.html'))); + $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', array('page' => 'index'))); + } + + /** + * @expectedException \Symfony\Component\Routing\Exception\MissingMandatoryParametersException + */ + public function testImportantVariableWithNoDefault() + { + $routes = $this->getRoutes('test', new Route('/{page}.{!_format}')); + $generator = $this->getGenerator($routes); + + $generator->generate('test', array('page' => 'index')); + } + /** * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException */ diff --git a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php index bcaf3dee7218a..ecf6c5926bc5c 100644 --- a/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php +++ b/src/Symfony/Component/Routing/Tests/Matcher/UrlMatcherTest.php @@ -177,6 +177,15 @@ public function testMatchSpecialRouteName() $this->assertEquals(array('_route' => '$péß^a|'), $matcher->match('/bar')); } + public function testMatchImportantVariable() + { + $collection = new RouteCollection(); + $collection->add('index', new Route('/index.{!_format}', array('_format' => 'xml'))); + + $matcher = $this->getUrlMatcher($collection); + $this->assertEquals(array('_route' => 'index', '_format' => 'xml'), $matcher->match('/index.xml')); + } + /** * @expectedException \Symfony\Component\Routing\Exception\ResourceNotFoundException */ pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy