Skip to content

Commit

Permalink
[Routing] adds _fragment special option to url generation for documen…
Browse files Browse the repository at this point in the history
…t fragment
  • Loading branch information
rodnaph committed Mar 26, 2016
1 parent fa01e84 commit 6d79a56
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 4 deletions.
12 changes: 11 additions & 1 deletion src/Symfony/Component/Routing/Generator/UrlGenerator.php
Expand Up @@ -257,14 +257,24 @@ protected function doGenerate($variables, $defaults, $requirements, $tokens, $pa
$url = $schemeAuthority.$this->context->getBaseUrl().$url;
}

// add a query string if needed
// extract unused parameters
$extra = array_diff_key($parameters, $variables, $defaults);

// extract fragment
$fragment = isset($extra['_fragment']) ? $extra['_fragment'] : '';
unset($extra['_fragment']);

// add a query string if needed
if ($extra && $query = http_build_query($extra, '', '&')) {
// "/" and "?" can be left decoded for better user experience, see
// http://tools.ietf.org/html/rfc3986#section-3.4
$url .= '?'.strtr($query, array('%2F' => '/'));
}

if ('' !== $fragment) {
$url .= '#'.strtr(rawurlencode($fragment), array('%2F' => '/', '%3F' => '?'));
}

return $url;
}

Expand Down
Expand Up @@ -69,6 +69,8 @@ interface UrlGeneratorInterface extends RequestContextAwareInterface
*
* If there is no route with the given name, the generator must throw the RouteNotFoundException.
*
* The special parameter _fragment will be used as the document fragment suffixed to the final URL.
*
* @param string $name The name of the route
* @param mixed $parameters An array of parameters
* @param int $referenceType The type of reference to be generated (one of the constants)
Expand Down
14 changes: 11 additions & 3 deletions src/Symfony/Component/Routing/RouteCompiler.php
Expand Up @@ -31,9 +31,10 @@ class RouteCompiler implements RouteCompilerInterface
/**
* {@inheritdoc}
*
* @throws \LogicException If a variable is referenced more than once
* @throws \DomainException If a variable name is numeric because PHP raises an error for such
* subpatterns in PCRE and thus would break matching, e.g. "(?P<123>.+)".
* @throws \InvalidArgumentException If a path variable is named _fragment
* @throws \LogicException If a variable is referenced more than once
* @throws \DomainException If a variable name is numeric because PHP raises an error for such
* subpatterns in PCRE and thus would break matching, e.g. "(?P<123>.+)".
*/
public static function compile(Route $route)
{
Expand All @@ -59,6 +60,13 @@ public static function compile(Route $route)
$staticPrefix = $result['staticPrefix'];

$pathVariables = $result['variables'];

foreach ($pathVariables as $pathParam) {
if ('_fragment' === $pathParam) {
throw new \InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath()));
}
}

$variables = array_merge($variables, $pathVariables);

$tokens = $result['tokens'];
Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
Expand Up @@ -622,6 +622,25 @@ public function provideRelativePaths()
);
}

public function testFragmentsCanBeAppendedToUrls()
{
$routes = $this->getRoutes('test', new Route('/testing'));

$url = $this->getGenerator($routes)->generate('test', array('_fragment' => 'frag ment'), true);
$this->assertEquals('/app.php/testing#frag%20ment', $url);

$url = $this->getGenerator($routes)->generate('test', array('_fragment' => '0'), true);
$this->assertEquals('/app.php/testing#0', $url);
}

public function testFragmentsDoNotEscapeValidCharacters()
{
$routes = $this->getRoutes('test', new Route('/testing'));
$url = $this->getGenerator($routes)->generate('test', array('_fragment' => '?/'), true);

$this->assertEquals('/app.php/testing#?/', $url);
}

protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
{
$context = new RequestContext('/app.php');
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/Routing/Tests/RouteCompilerTest.php
Expand Up @@ -175,6 +175,16 @@ public function testRouteWithSameVariableTwice()
$compiled = $route->compile();
}

/**
* @expectedException \InvalidArgumentException
*/
public function testRouteWithFragmentAsPathParameter()
{
$route = new Route('/{_fragment}');

$compiled = $route->compile();
}

/**
* @dataProvider getNumericVariableNames
* @expectedException \DomainException
Expand Down

0 comments on commit 6d79a56

Please sign in to comment.