* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Generator; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RequestContext; /** * UrlGenerator generates URL based on a set of routes. * * @author Fabien Potencier */ class UrlGenerator implements UrlGeneratorInterface { protected $context; private $routes; private $cache; /** * Constructor. * * @param RouteCollection $routes A RouteCollection instance * @param RequestContext $context The context */ public function __construct(RouteCollection $routes, RequestContext $context) { $this->routes = $routes; $this->context = $context; $this->cache = array(); } /** * Sets the request context. * * @param RequestContext $context The context */ public function setContext(RequestContext $context) { $this->context = $context; } /** * Gets the request context. * * @return RequestContext The context */ public function getContext() { return $this->context; } /** * Generates a URL from the given parameters. * * @param string $name The name of the route * @param array $parameters An array of parameters * @param Boolean $absolute Whether to generate an absolute URL * * @return string The generated URL * * @throws \InvalidArgumentException When route doesn't exist */ public function generate($name, array $parameters = array(), $absolute = false) { if (null === $route = $this->routes->get($name)) { throw new \InvalidArgumentException(sprintf('Route "%s" does not exist.', $name)); } if (!isset($this->cache[$name])) { $this->cache[$name] = $route->compile(); } return $this->doGenerate($this->cache[$name]->getVariables(), $route->getDefaults(), $route->getRequirements(), $this->cache[$name]->getTokens(), $parameters, $name, $absolute); } /** * @throws \InvalidArgumentException When route has some missing mandatory parameters */ protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute) { $originParameters = $parameters; $parameters = array_replace($this->context->getParameters(), $parameters); $tparams = array_replace($defaults, $parameters); // all params must be given if ($diff = array_diff_key($variables, $tparams)) { throw new \InvalidArgumentException(sprintf('The "%s" route has some missing mandatory parameters (%s).', $name, implode(', ', $diff))); } $url = ''; $optional = true; foreach ($tokens as $token) { if ('variable' === $token[0]) { if (false === $optional || !isset($defaults[$token[3]]) || (isset($parameters[$token[3]]) && $parameters[$token[3]] != $defaults[$token[3]])) { // check requirement if (isset($requirements[$token[3]]) && !preg_match('#^'.$requirements[$token[3]].'$#', $tparams[$token[3]])) { throw new \InvalidArgumentException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $requirements[$token[3]], $tparams[$token[3]])); } if (isset($tparams[$token[3]])) { // %2F is not valid in a URL, so we don't encode it (which is fine as the requirements explicitly allowed it) $url = $token[1].str_replace('%2F', '/', urlencode($tparams[$token[3]])).$url; } $optional = false; } } elseif ('text' === $token[0]) { $url = $token[1].$token[2].$url; $optional = false; } else { // handle custom tokens if ($segment = call_user_func_array(array($this, 'generateFor'.ucfirst(array_shift($token))), array_merge(array($optional, $tparams), $token))) { $url = $segment.$url; $optional = false; } } } if (!$url) { $url = '/'; } // add a query string if needed if ($extra = array_diff_key($originParameters, $variables, $defaults)) { $url .= '?'.http_build_query($extra); } $url = $this->context->getBaseUrl().$url; if ($this->context->getHost()) { $scheme = $this->context->getScheme(); if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) { $absolute = true; $scheme = $req; } if ($absolute) { $port = ''; if ('http' === $scheme && 80 != $this->context->getHttpPort()) { $port = ':'.$this->context->getHttpPort(); } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { $port = ':'.$this->context->getHttpsPort(); } $url = $scheme.'://'.$this->context->getHost().$port.$url; } } return $url; } }