[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/routing/Symfony/Component/Routing/Generator/ -> UrlGenerator.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <fabien@symfony.com>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\Routing\Generator;
  13  
  14  use Symfony\Component\Routing\RouteCollection;
  15  use Symfony\Component\Routing\RequestContext;
  16  use Symfony\Component\Routing\Exception\InvalidParameterException;
  17  use Symfony\Component\Routing\Exception\RouteNotFoundException;
  18  use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
  19  use Psr\Log\LoggerInterface;
  20  
  21  /**
  22   * UrlGenerator can generate a URL or a path for any route in the RouteCollection
  23   * based on the passed parameters.
  24   *
  25   * @author Fabien Potencier <fabien@symfony.com>
  26   * @author Tobias Schultze <http://tobion.de>
  27   */
  28  class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface
  29  {
  30      /**
  31       * @var RouteCollection
  32       */
  33      protected $routes;
  34  
  35      /**
  36       * @var RequestContext
  37       */
  38      protected $context;
  39  
  40      /**
  41       * @var bool|null
  42       */
  43      protected $strictRequirements = true;
  44  
  45      /**
  46       * @var LoggerInterface|null
  47       */
  48      protected $logger;
  49  
  50      /**
  51       * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
  52       *
  53       * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars
  54       * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g.
  55       * "?" and "#" (would be interpreted wrongly as query and fragment identifier),
  56       * "'" and """ (are used as delimiters in HTML).
  57       */
  58      protected $decodedChars = array(
  59          // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
  60          // some webservers don't allow the slash in encoded form in the path for security reasons anyway
  61          // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
  62          '%2F' => '/',
  63          // the following chars are general delimiters in the URI specification but have only special meaning in the authority component
  64          // so they can safely be used in the path in unencoded form
  65          '%40' => '@',
  66          '%3A' => ':',
  67          // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally
  68          // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability
  69          '%3B' => ';',
  70          '%2C' => ',',
  71          '%3D' => '=',
  72          '%2B' => '+',
  73          '%21' => '!',
  74          '%2A' => '*',
  75          '%7C' => '|',
  76      );
  77  
  78      /**
  79       * Constructor.
  80       *
  81       * @param RouteCollection      $routes  A RouteCollection instance
  82       * @param RequestContext       $context The context
  83       * @param LoggerInterface|null $logger  A logger instance
  84       */
  85      public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
  86      {
  87          $this->routes = $routes;
  88          $this->context = $context;
  89          $this->logger = $logger;
  90      }
  91  
  92      /**
  93       * {@inheritdoc}
  94       */
  95      public function setContext(RequestContext $context)
  96      {
  97          $this->context = $context;
  98      }
  99  
 100      /**
 101       * {@inheritdoc}
 102       */
 103      public function getContext()
 104      {
 105          return $this->context;
 106      }
 107  
 108      /**
 109       * {@inheritdoc}
 110       */
 111      public function setStrictRequirements($enabled)
 112      {
 113          $this->strictRequirements = null === $enabled ? null : (bool) $enabled;
 114      }
 115  
 116      /**
 117       * {@inheritdoc}
 118       */
 119      public function isStrictRequirements()
 120      {
 121          return $this->strictRequirements;
 122      }
 123  
 124      /**
 125       * {@inheritdoc}
 126       */
 127      public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
 128      {
 129          if (null === $route = $this->routes->get($name)) {
 130              throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
 131          }
 132  
 133          // the Route has a cache of its own and is not recompiled as long as it does not get modified
 134          $compiledRoute = $route->compile();
 135  
 136          return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens());
 137      }
 138  
 139      /**
 140       * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
 141       * @throws InvalidParameterException           When a parameter value for a placeholder is not correct because
 142       *                                             it does not match the requirement
 143       */
 144      protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens)
 145      {
 146          $variables = array_flip($variables);
 147          $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
 148  
 149          // all params must be given
 150          if ($diff = array_diff_key($variables, $mergedParams)) {
 151              throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name));
 152          }
 153  
 154          $url = '';
 155          $optional = true;
 156          foreach ($tokens as $token) {
 157              if ('variable' === $token[0]) {
 158                  if (!$optional || !array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
 159                      // check requirement
 160                      if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
 161                          $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]);
 162                          if ($this->strictRequirements) {
 163                              throw new InvalidParameterException($message);
 164                          }
 165  
 166                          if ($this->logger) {
 167                              $this->logger->error($message);
 168                          }
 169  
 170                          return;
 171                      }
 172  
 173                      $url = $token[1].$mergedParams[$token[3]].$url;
 174                      $optional = false;
 175                  }
 176              } else {
 177                  // static text
 178                  $url = $token[1].$url;
 179                  $optional = false;
 180              }
 181          }
 182  
 183          if ('' === $url) {
 184              $url = '/';
 185          }
 186  
 187          // the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request)
 188          $url = strtr(rawurlencode($url), $this->decodedChars);
 189  
 190          // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
 191          // so we need to encode them as they are not used for this purpose here
 192          // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route
 193          $url = strtr($url, array('/../' => '/%2E%2E/', '/./' => '/%2E/'));
 194          if ('/..' === substr($url, -3)) {
 195              $url = substr($url, 0, -2).'%2E%2E';
 196          } elseif ('/.' === substr($url, -2)) {
 197              $url = substr($url, 0, -1).'%2E';
 198          }
 199  
 200          $schemeAuthority = '';
 201          if ($host = $this->context->getHost()) {
 202              $scheme = $this->context->getScheme();
 203              if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme !== $req) {
 204                  $referenceType = self::ABSOLUTE_URL;
 205                  $scheme = $req;
 206              }
 207  
 208              if ($hostTokens) {
 209                  $routeHost = '';
 210                  foreach ($hostTokens as $token) {
 211                      if ('variable' === $token[0]) {
 212                          if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#i', $mergedParams[$token[3]])) {
 213                              $message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given) to generate a corresponding URL.', $token[3], $name, $token[2], $mergedParams[$token[3]]);
 214  
 215                              if ($this->strictRequirements) {
 216                                  throw new InvalidParameterException($message);
 217                              }
 218  
 219                              if ($this->logger) {
 220                                  $this->logger->error($message);
 221                              }
 222  
 223                              return;
 224                          }
 225  
 226                          $routeHost = $token[1].$mergedParams[$token[3]].$routeHost;
 227                      } else {
 228                          $routeHost = $token[1].$routeHost;
 229                      }
 230                  }
 231  
 232                  if ($routeHost !== $host) {
 233                      $host = $routeHost;
 234                      if (self::ABSOLUTE_URL !== $referenceType) {
 235                          $referenceType = self::NETWORK_PATH;
 236                      }
 237                  }
 238              }
 239  
 240              if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) {
 241                  $port = '';
 242                  if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
 243                      $port = ':'.$this->context->getHttpPort();
 244                  } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) {
 245                      $port = ':'.$this->context->getHttpsPort();
 246                  }
 247  
 248                  $schemeAuthority = self::NETWORK_PATH === $referenceType ? '//' : "$scheme://";
 249                  $schemeAuthority .= $host.$port;
 250              }
 251          }
 252  
 253          if (self::RELATIVE_PATH === $referenceType) {
 254              $url = self::getRelativePath($this->context->getPathInfo(), $url);
 255          } else {
 256              $url = $schemeAuthority.$this->context->getBaseUrl().$url;
 257          }
 258  
 259          // add a query string if needed
 260          $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) {
 261              return $a == $b ? 0 : 1;
 262          });
 263  
 264          if ($extra && $query = http_build_query($extra, '', '&')) {
 265              $url .= '?'.$query;
 266          }
 267  
 268          return $url;
 269      }
 270  
 271      /**
 272       * Returns the target path as relative reference from the base path.
 273       *
 274       * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash.
 275       * Both paths must be absolute and not contain relative parts.
 276       * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
 277       * Furthermore, they can be used to reduce the link size in documents.
 278       *
 279       * Example target paths, given a base path of "/a/b/c/d":
 280       * - "/a/b/c/d"     -> ""
 281       * - "/a/b/c/"      -> "./"
 282       * - "/a/b/"        -> "../"
 283       * - "/a/b/c/other" -> "other"
 284       * - "/a/x/y"       -> "../../x/y"
 285       *
 286       * @param string $basePath   The base path
 287       * @param string $targetPath The target path
 288       *
 289       * @return string The relative target path
 290       */
 291      public static function getRelativePath($basePath, $targetPath)
 292      {
 293          if ($basePath === $targetPath) {
 294              return '';
 295          }
 296  
 297          $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
 298          $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath);
 299          array_pop($sourceDirs);
 300          $targetFile = array_pop($targetDirs);
 301  
 302          foreach ($sourceDirs as $i => $dir) {
 303              if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
 304                  unset($sourceDirs[$i], $targetDirs[$i]);
 305              } else {
 306                  break;
 307              }
 308          }
 309  
 310          $targetDirs[] = $targetFile;
 311          $path = str_repeat('../', count($sourceDirs)).implode('/', $targetDirs);
 312  
 313          // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
 314          // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
 315          // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
 316          // (see http://tools.ietf.org/html/rfc3986#section-4.2).
 317          return '' === $path || '/' === $path[0]
 318              || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
 319              ? "./$path" : $path;
 320      }
 321  }


Generated: Thu Jan 11 00:25:41 2018 Cross-referenced by PHPXref 0.7.1