From 75f59ebe01acc6f510b32b5ea8d3173d3d8e8458 Mon Sep 17 00:00:00 2001 From: Tobias Schultze Date: Mon, 16 Apr 2012 10:25:33 +0200 Subject: [PATCH] [Routing] add support for path-relative and scheme-relative URL generation --- .../Twig/Extension/RoutingExtension.php | 8 +- .../FrameworkBundle/Controller/Controller.php | 13 +- .../Controller/RedirectController.php | 5 +- .../Templating/Helper/RouterHelper.php | 10 +- .../Templating/Helper/LogoutUrlHelper.php | 28 ++-- .../Security/LocalizedFormFailureHandler.php | 5 +- .../Generator/Dumper/PhpGeneratorDumper.php | 4 +- .../Routing/Generator/UrlGenerator.php | 122 +++++++++++++++--- .../Generator/UrlGeneratorInterface.php | 22 +++- .../Component/Routing/RequestContext.php | 25 +++- src/Symfony/Component/Routing/Router.php | 4 +- .../Tests/Generator/UrlGeneratorTest.php | 117 ++++++++++++++++- .../Component/Security/Http/HttpUtils.php | 11 +- 13 files changed, 306 insertions(+), 68 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php index 65d7dd22e8..54befc2cf9 100644 --- a/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/RoutingExtension.php @@ -40,14 +40,14 @@ class RoutingExtension extends \Twig_Extension ); } - public function getPath($name, $parameters = array()) + public function getPath($name, $parameters = array(), $relative = false) { - return $this->generator->generate($name, $parameters, false); + return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); } - public function getUrl($name, $parameters = array()) + public function getUrl($name, $parameters = array(), $schemeRelative = false) { - return $this->generator->generate($name, $parameters, true); + return $this->generator->generate($name, $parameters, $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php index 87900a1242..139b9ff403 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -19,8 +20,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Form\FormTypeInterface; use Symfony\Component\Form\Form; use Symfony\Component\Form\FormBuilder; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Doctrine\Bundle\DoctrineBundle\Registry; -use Symfony\Component\HttpFoundation\Request; /** * Controller is a simple implementation of a Controller. @@ -34,15 +35,15 @@ class Controller extends ContainerAware /** * Generates a URL from the given parameters. * - * @param string $route The name of the route - * @param mixed $parameters An array of parameters - * @param Boolean $absolute Whether to generate an absolute URL + * @param string $route The name of the route + * @param mixed $parameters An array of parameters + * @param string $referenceType The type of reference (see UrlGeneratorInterface) * * @return string The generated URL */ - public function generateUrl($route, $parameters = array(), $absolute = false) + public function generateUrl($route, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) { - return $this->container->get('router')->generate($route, $parameters, $absolute); + return $this->container->get('router')->generate($route, $parameters, $referenceType); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php index 513d4de3f5..5d8b71906d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php +++ b/src/Symfony/Bundle/FrameworkBundle/Controller/RedirectController.php @@ -12,8 +12,9 @@ namespace Symfony\Bundle\FrameworkBundle\Controller; use Symfony\Component\DependencyInjection\ContainerAware; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** * Redirects a request to another URL. @@ -45,7 +46,7 @@ class RedirectController extends ContainerAware $attributes = $this->container->get('request')->attributes->get('_route_params'); unset($attributes['route'], $attributes['permanent']); - return new RedirectResponse($this->container->get('router')->generate($route, $attributes, true), $permanent ? 301 : 302); + return new RedirectResponse($this->container->get('router')->generate($route, $attributes, UrlGeneratorInterface::ABSOLUTE_URL), $permanent ? 301 : 302); } /** diff --git a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php index 6ce78960df..f2429e25c1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/RouterHelper.php @@ -36,15 +36,15 @@ class RouterHelper extends Helper /** * Generates a URL from the given parameters. * - * @param string $name The name of the route - * @param mixed $parameters An array of parameters - * @param Boolean $absolute Whether to generate an absolute URL + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param string $referenceType The type of reference (see UrlGeneratorInterface) * * @return string The generated URL */ - public function generate($name, $parameters = array(), $absolute = false) + public function generate($name, $parameters = array(), $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH) { - return $this->generator->generate($name, $parameters, $absolute); + return $this->generator->generate($name, $parameters, $referenceType); } /** diff --git a/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php b/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php index 4b79957c3b..d5c6a0101e 100644 --- a/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php +++ b/src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php @@ -55,36 +55,40 @@ class LogoutUrlHelper extends Helper } /** - * Generate the relative logout URL for the firewall. + * Generates the absolute logout path for the firewall. * * @param string $key The firewall key - * @return string The relative logout URL + * + * @return string The logout path */ public function getLogoutPath($key) { - return $this->generateLogoutUrl($key, false); + return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_PATH); } /** - * Generate the absolute logout URL for the firewall. + * Generates the absolute logout URL for the firewall. * * @param string $key The firewall key - * @return string The absolute logout URL + * + * @return string The logout URL */ public function getLogoutUrl($key) { - return $this->generateLogoutUrl($key, true); + return $this->generateLogoutUrl($key, UrlGeneratorInterface::ABSOLUTE_URL); } /** - * Generate the logout URL for the firewall. + * Generates the logout URL for the firewall. + * + * @param string $key The firewall key + * @param string $referenceType The type of reference (see UrlGeneratorInterface) * - * @param string $key The firewall key - * @param Boolean $absolute Whether to generate an absolute URL * @return string The logout URL + * * @throws \InvalidArgumentException if no LogoutListener is registered for the key */ - private function generateLogoutUrl($key, $absolute) + private function generateLogoutUrl($key, $referenceType) { if (!array_key_exists($key, $this->listeners)) { throw new \InvalidArgumentException(sprintf('No LogoutListener found for firewall key "%s".', $key)); @@ -97,13 +101,13 @@ class LogoutUrlHelper extends Helper if ('/' === $logoutPath[0]) { $request = $this->container->get('request'); - $url = ($absolute ? $request->getUriForPath($logoutPath) : $request->getBasePath() . $logoutPath); + $url = UrlGeneratorInterface::ABSOLUTE_URL === $referenceType ? $request->getUriForPath($logoutPath) : $request->getBasePath() . $logoutPath; if (!empty($parameters)) { $url .= '?' . http_build_query($parameters); } } else { - $url = $this->router->generate($logoutPath, $parameters, $absolute); + $url = $this->router->generate($logoutPath, $parameters, $referenceType); } return $url; diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php index fda394d8cc..7b97199065 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Security/LocalizedFormFailureHandler.php @@ -12,9 +12,10 @@ namespace Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\FormLoginBundle\Security; use Symfony\Component\HttpFoundation\RedirectResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; class LocalizedFormFailureHandler implements AuthenticationFailureHandlerInterface @@ -28,6 +29,6 @@ class LocalizedFormFailureHandler implements AuthenticationFailureHandlerInterfa public function onAuthenticationFailure(Request $request, AuthenticationException $exception) { - return new RedirectResponse($this->router->generate('localized_login_path', array(), true)); + return new RedirectResponse($this->router->generate('localized_login_path', array(), UrlGeneratorInterface::ABSOLUTE_URL)); } } diff --git a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php index c85f0201e4..abdf309d1b 100644 --- a/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php +++ b/src/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php @@ -108,7 +108,7 @@ EOF; private function generateGenerateMethod() { return <<doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute, \$hostnameTokens); + return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$referenceType, \$hostnameTokens); } EOF; } diff --git a/src/Symfony/Component/Routing/Generator/UrlGenerator.php b/src/Symfony/Component/Routing/Generator/UrlGenerator.php index aa0a3ee8f2..0dc38ddf89 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGenerator.php +++ b/src/Symfony/Component/Routing/Generator/UrlGenerator.php @@ -127,7 +127,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt /** * {@inheritDoc} */ - public function generate($name, $parameters = array(), $absolute = false) + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) { if (null === $route = $this->routes->get($name)) { throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name)); @@ -136,15 +136,40 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt // the Route has a cache of its own and is not recompiled as long as it does not get modified $compiledRoute = $route->compile(); - return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute, $compiledRoute->getHostnameTokens()); + return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostnameTokens()); + } + + /** + * This method converts the reference type to the new value introduced in Symfony 2.2. It can be used by + * other UrlGenerator implementations to be BC with Symfony 2.1. Reference type was a Boolean called + * $absolute in Symfony 2.1 and only supported two reference types. + * + * @param Boolean $absolute Whether to generate an absolute URL + * + * @return string The new reference type + * + * @deprecated Deprecated since version 2.2, to be removed in 2.3. + */ + public static function convertReferenceType($absolute) + { + if (false === $absolute) { + return self::ABSOLUTE_PATH; + } + if (true === $absolute) { + return self::ABSOLUTE_URL; + } + + return $absolute; } /** * @throws MissingMandatoryParametersException When route has some missing mandatory parameters * @throws InvalidParameterException When a parameter value is not correct */ - protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute, $hostnameTokens) + protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostnameTokens) { + $referenceType = self::convertReferenceType($referenceType); + $variables = array_flip($variables); $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters); @@ -186,8 +211,8 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt $url = '/'; } - // do not encode the contexts base url as it is already encoded (see Symfony\Component\HttpFoundation\Request) - $url = $this->context->getBaseUrl().strtr(rawurlencode($url), $this->decodedChars); + // the contexts base url is already encoded (see Symfony\Component\HttpFoundation\Request) + $url = strtr(rawurlencode($url), $this->decodedChars); // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3 // so we need to encode them as they are not used for this purpose here @@ -199,16 +224,11 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt $url = substr($url, 0, -1) . '%2E'; } - // add a query string if needed - $extra = array_diff_key($parameters, $variables); - if ($extra && $query = http_build_query($extra, '', '&')) { - $url .= '?'.$query; - } - + $schemeAuthority = ''; if ($host = $this->context->getHost()) { $scheme = $this->context->getScheme(); - if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) { - $absolute = true; + if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme !== $req) { + $referenceType = self::ABSOLUTE_URL; $scheme = $req; } @@ -231,18 +251,20 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt } $routeHost = $token[1].$mergedParams[$token[3]].$routeHost; - } elseif ('text' === $token[0]) { + } else { $routeHost = $token[1].$routeHost; } } - if ($routeHost != $host) { + if ($routeHost !== $host) { $host = $routeHost; - $absolute = true; + if (self::ABSOLUTE_URL !== $referenceType) { + $referenceType = self::NETWORK_PATH; + } } } - if ($absolute) { + if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) { $port = ''; if ('http' === $scheme && 80 != $this->context->getHttpPort()) { $port = ':'.$this->context->getHttpPort(); @@ -250,10 +272,74 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt $port = ':'.$this->context->getHttpsPort(); } - $url = $scheme.'://'.$host.$port.$url; + $schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://"; + $schemeAuthority .= $host.$port; } } + if (self::RELATIVE_PATH === $referenceType) { + $url = self::getRelativePath($this->context->getPathInfo(), $url); + } else { + $url = $schemeAuthority.$this->context->getBaseUrl().$url; + } + + // add a query string if needed + $extra = array_diff_key($parameters, $variables); + if ($extra && $query = http_build_query($extra, '', '&')) { + $url .= '?'.$query; + } + return $url; } + + /** + * Returns the target path as relative reference from the base path. + * + * Only the URIs path component (no schema, hostname etc.) is relevant and must be given, starting with a slash. + * Both paths must be absolute and not contain relative parts. + * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. + * Furthermore, they can be used to reduce the link size in documents. + * + * Example target paths, given a base path of "/a/b/c/d": + * - "/a/b/c/d" -> "" + * - "/a/b/c/" -> "./" + * - "/a/b/" -> "../" + * - "/a/b/c/other" -> "other" + * - "/a/x/y" -> "../../x/y" + * + * @param string $basePath The base path + * @param string $targetPath The target path + * + * @return string The relative target path + */ + public static function getRelativePath($basePath, $targetPath) + { + if ($basePath === $targetPath) { + return ''; + } + + $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); + $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath); + array_pop($sourceDirs); + $targetFile = array_pop($targetDirs); + + foreach ($sourceDirs as $i => $dir) { + if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { + unset($sourceDirs[$i], $targetDirs[$i]); + } else { + break; + } + } + + $targetDirs[] = $targetFile; + $path = str_repeat('../', count($sourceDirs)) . implode('/', $targetDirs); + + // A reference to the same base directory or an empty subdirectory must be prefixed with "./". + // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used + // as the first segment of a relative-path reference, as it would be mistaken for a scheme name + // (see http://tools.ietf.org/html/rfc3986#section-4.2). + return '' === $path || '/' === $path[0] + || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) + ? "./$path" : $path; + } } diff --git a/src/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php b/src/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php index e28c79d2d6..e24030b32a 100644 --- a/src/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php +++ b/src/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php @@ -18,20 +18,34 @@ use Symfony\Component\Routing\Exception\RouteNotFoundException; * UrlGeneratorInterface is the interface that all URL generator classes must implement. * * @author Fabien Potencier + * @author Tobias Schultze * * @api */ interface UrlGeneratorInterface extends RequestContextAwareInterface { + /** + * These constants define the different types of resource references that are declared + * in RFC 3986: http://tools.ietf.org/html/rfc3986 + * We are using the term "URL" instead of "URI" as this is more common in web applications + * and we do not need to distinguish them as the difference is mostly semantical and + * less technical. Generating URIs, i.e. representation-independent resource identifiers, + * is still possible. + */ + const ABSOLUTE_URL = 'url'; + const ABSOLUTE_PATH = 'path'; + const RELATIVE_PATH = 'relative'; + const NETWORK_PATH = 'network'; + /** * Generates a URL from the given parameters. * * If the generator is not able to generate the url, it must throw the RouteNotFoundException * as documented below. * - * @param string $name The name of the route - * @param mixed $parameters An array of parameters - * @param Boolean $absolute Whether to generate an absolute URL + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param string $referenceType The type of reference to be generated (see defined constants) * * @return string The generated URL * @@ -39,5 +53,5 @@ interface UrlGeneratorInterface extends RequestContextAwareInterface * * @api */ - public function generate($name, $parameters = array(), $absolute = false); + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH); } diff --git a/src/Symfony/Component/Routing/RequestContext.php b/src/Symfony/Component/Routing/RequestContext.php index 1f9cf3c02c..0286e842de 100644 --- a/src/Symfony/Component/Routing/RequestContext.php +++ b/src/Symfony/Component/Routing/RequestContext.php @@ -23,6 +23,7 @@ use Symfony\Component\HttpFoundation\Request; class RequestContext { private $baseUrl; + private $pathInfo; private $method; private $host; private $scheme; @@ -46,7 +47,7 @@ class RequestContext * * @api */ - public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443) + public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/') { $this->baseUrl = $baseUrl; $this->method = strtoupper($method); @@ -54,11 +55,13 @@ class RequestContext $this->scheme = strtolower($scheme); $this->httpPort = $httpPort; $this->httpsPort = $httpsPort; + $this->pathInfo = $path; } public function fromRequest(Request $request) { $this->setBaseUrl($request->getBaseUrl()); + $this->setPathInfo($request->getPathInfo()); $this->setMethod($request->getMethod()); $this->setHost($request->getHost()); $this->setScheme($request->getScheme()); @@ -88,6 +91,26 @@ class RequestContext $this->baseUrl = $baseUrl; } + /** + * Gets the path info. + * + * @return string The path info + */ + public function getPathInfo() + { + return $this->pathInfo; + } + + /** + * Sets the path info. + * + * @param string $pathInfo The path info + */ + public function setPathInfo($pathInfo) + { + $this->pathInfo = $pathInfo; + } + /** * Gets the HTTP method. * diff --git a/src/Symfony/Component/Routing/Router.php b/src/Symfony/Component/Routing/Router.php index 68d73ab24d..a85054a450 100644 --- a/src/Symfony/Component/Routing/Router.php +++ b/src/Symfony/Component/Routing/Router.php @@ -202,9 +202,9 @@ class Router implements RouterInterface /** * {@inheritdoc} */ - public function generate($name, $parameters = array(), $absolute = false) + public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH) { - return $this->getGenerator()->generate($name, $parameters, $absolute); + return $this->getGenerator()->generate($name, $parameters, $referenceType); } /** diff --git a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php index a3f50b6aca..5c44307b84 100644 --- a/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php +++ b/src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php @@ -379,7 +379,6 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase */ public function testDefaultRequirementOfVariableDisallowsNextSeparator() { - $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); $this->getGenerator($routes)->generate('test', array('page' => 'do.t', '_format' => 'html')); } @@ -388,7 +387,7 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase { $routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com')); - $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', array('name' =>'Fabien', 'locale' => 'fr'))); + $this->assertEquals('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', array('name' =>'Fabien', 'locale' => 'fr'))); } public function testWithHostnameSameAsContext() @@ -440,6 +439,120 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase $this->assertNull($generator->generate('test', array('foo' => 'baz'), false)); } + /** + * @dataProvider provideRelativePaths + */ + public function testGetRelativePath($sourcePath, $targetPath, $expectedPath) + { + $this->assertSame($expectedPath, UrlGenerator::getRelativePath($sourcePath, $targetPath)); + } + + public function provideRelativePaths() + { + return array( + array( + '/same/dir/', + '/same/dir/', + '' + ), + array( + '/same/file', + '/same/file', + '' + ), + array( + '/', + '/file', + 'file' + ), + array( + '/', + '/dir/file', + 'dir/file' + ), + array( + '/dir/file.html', + '/dir/different-file.html', + 'different-file.html' + ), + array( + '/same/dir/extra-file', + '/same/dir/', + './' + ), + array( + '/parent/dir/', + '/parent/', + '../' + ), + array( + '/parent/dir/extra-file', + '/parent/', + '../' + ), + array( + '/a/b/', + '/x/y/z/', + '../../x/y/z/' + ), + array( + '/a/b/c/d/e', + '/a/c/d', + '../../../c/d' + ), + array( + '/a/b/c//', + '/a/b/c/', + '../' + ), + array( + '/a/b/c/', + '/a/b/c//', + './/' + ), + array( + '/root/a/b/c/', + '/root/x/b/c/', + '../../../x/b/c/' + ), + array( + '/a/b/c/d/', + '/a', + '../../../../a' + ), + array( + '/special-chars/sp%20ce/1€/mäh/e=mc²', + '/special-chars/sp%20ce/1€/<µ>/e=mc²', + '../<µ>/e=mc²' + ), + array( + 'not-rooted', + 'dir/file', + 'dir/file' + ), + array( + '//dir/', + '', + '../../' + ), + array( + '/dir/', + '/dir/file:with-colon', + './file:with-colon' + ), + array( + '/dir/', + '/dir/subdir/file:with-colon', + 'subdir/file:with-colon' + ), + array( + '/dir/', + '/dir/:subdir/', + './:subdir/' + ), + ); + } + protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null) { $context = new RequestContext('/app.php'); diff --git a/src/Symfony/Component/Security/Http/HttpUtils.php b/src/Symfony/Component/Security/Http/HttpUtils.php index 76cfc6af09..6a2da08bd4 100644 --- a/src/Symfony/Component/Security/Http/HttpUtils.php +++ b/src/Symfony/Component/Security/Http/HttpUtils.php @@ -67,8 +67,8 @@ class HttpUtils public function createRequest(Request $request, $path) { $newRequest = Request::create($this->generateUri($request, $path), 'get', array(), $request->cookies->all(), array(), $request->server->all()); - if ($session = $request->getSession()) { - $newRequest->setSession($session); + if ($request->hasSession()) { + $newRequest->setSession($request->getSession()); } if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) { @@ -127,15 +127,10 @@ class HttpUtils return $request->getUriForPath($path); } - return $this->generateUrl($path, true); - } - - private function generateUrl($route, $absolute = false) - { if (null === $this->urlGenerator) { throw new \LogicException('You must provide a UrlGeneratorInterface instance to be able to use routes.'); } - return $this->urlGenerator->generate($route, array(), $absolute); + return $this->urlGenerator->generate($path, array(), UrlGeneratorInterface::ABSOLUTE_URL); } }