UrlGenerator.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Routing\Generator;
  11. use Symfony\Component\Routing\Route;
  12. use Symfony\Component\Routing\RouteCollection;
  13. /**
  14. * UrlGenerator generates URL based on a set of routes.
  15. *
  16. * @author Fabien Potencier <fabien@symfony.com>
  17. */
  18. class UrlGenerator implements UrlGeneratorInterface
  19. {
  20. protected $defaults;
  21. protected $context;
  22. private $routes;
  23. private $cache;
  24. /**
  25. * Constructor.
  26. *
  27. * @param RouteCollection $routes A RouteCollection instance
  28. * @param array $context The context
  29. * @param array $defaults The default values
  30. */
  31. public function __construct(RouteCollection $routes, array $context = array(), array $defaults = array())
  32. {
  33. $this->routes = $routes;
  34. $this->context = $context;
  35. $this->defaults = $defaults;
  36. $this->cache = array();
  37. }
  38. /**
  39. * Sets the request context.
  40. *
  41. * @param array $context The context
  42. */
  43. public function setContext(array $context = array())
  44. {
  45. $this->context = $context;
  46. }
  47. /**
  48. * Generates a URL from the given parameters.
  49. *
  50. * @param string $name The name of the route
  51. * @param array $parameters An array of parameters
  52. * @param Boolean $absolute Whether to generate an absolute URL
  53. *
  54. * @return string The generated URL
  55. *
  56. * @throws \InvalidArgumentException When route doesn't exist
  57. */
  58. public function generate($name, array $parameters, $absolute = false)
  59. {
  60. if (null === $route = $this->routes->get($name)) {
  61. throw new \InvalidArgumentException(sprintf('Route "%s" does not exist.', $name));
  62. }
  63. if (!isset($this->cache[$name])) {
  64. $this->cache[$name] = $route->compile();
  65. }
  66. return $this->doGenerate($this->cache[$name]->getVariables(), $route->getDefaults(), $route->getRequirements(), $this->cache[$name]->getTokens(), $parameters, $name, $absolute);
  67. }
  68. /**
  69. * @throws \InvalidArgumentException When route has some missing mandatory parameters
  70. */
  71. protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
  72. {
  73. $defaults = array_merge($this->defaults, $defaults);
  74. $tparams = array_merge($defaults, $parameters);
  75. // all params must be given
  76. if ($diff = array_diff_key($variables, $tparams)) {
  77. throw new \InvalidArgumentException(sprintf('The "%s" route has some missing mandatory parameters (%s).', $name, implode(', ', $diff)));
  78. }
  79. $url = '';
  80. $optional = true;
  81. foreach ($tokens as $token) {
  82. if ('variable' === $token[0]) {
  83. if (false === $optional || !isset($defaults[$token[3]]) || (isset($parameters[$token[3]]) && $parameters[$token[3]] != $defaults[$token[3]])) {
  84. // check requirement
  85. if (isset($requirements[$token[3]]) && !preg_match('#^'.$requirements[$token[3]].'$#', $tparams[$token[3]])) {
  86. throw new \InvalidArgumentException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $requirements[$token[3]], $tparams[$token[3]]));
  87. }
  88. // %2F is not valid in a URL, so we don't encode it (which is fine as the requirements explicitely allowed it)
  89. $url = $token[1].str_replace('%2F', '/', urlencode($tparams[$token[3]])).$url;
  90. $optional = false;
  91. }
  92. } elseif ('text' === $token[0]) {
  93. $url = $token[1].$token[2].$url;
  94. $optional = false;
  95. } else {
  96. // handle custom tokens
  97. if ($segment = call_user_func_array(array($this, 'generateFor'.ucfirst(array_shift($token))), array_merge(array($optional, $tparams), $token))) {
  98. $url = $segment.$url;
  99. $optional = false;
  100. }
  101. }
  102. }
  103. if (!$url) {
  104. $url = '/';
  105. }
  106. // add a query string if needed
  107. if ($extra = array_diff_key($parameters, $variables, $defaults)) {
  108. $url .= '?'.http_build_query($extra);
  109. }
  110. $url = (isset($this->context['base_url']) ? $this->context['base_url'] : '').$url;
  111. if ($absolute && isset($this->context['host'])) {
  112. $isSecure = (isset($this->context['is_secure']) && $this->context['is_secure']);
  113. $port = isset($this->context['port']) ? $this->context['port'] : 80;
  114. $urlBeginning = 'http'.($isSecure ? 's' : '').'://'.$this->context['host'];
  115. if (($isSecure && $port != 443) || (!$isSecure && $port != 80)) {
  116. $urlBeginning .= ':'.$port;
  117. }
  118. $url = $urlBeginning.$url;
  119. }
  120. return $url;
  121. }
  122. }