Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

UrlGenerator.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 13.36 KiB


001  <?php
002   
003  /*
004   * This file is part of the Symfony package.
005   *
006   * (c) Fabien Potencier <fabien@symfony.com>
007   *
008   * For the full copyright and license information, please view the LICENSE
009   * file that was distributed with this source code.
010   */
011   
012  namespace Symfony\Component\Routing\Generator;
013   
014  use Psr\Log\LoggerInterface;
015  use Symfony\Component\Routing\Exception\InvalidParameterException;
016  use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
017  use Symfony\Component\Routing\Exception\RouteNotFoundException;
018  use Symfony\Component\Routing\RequestContext;
019  use Symfony\Component\Routing\RouteCollection;
020   
021  /**
022   * UrlGenerator can generate a URL or a path for any route in the RouteCollection
023   * based on the passed parameters.
024   *
025   * @author Fabien Potencier <fabien@symfony.com>
026   * @author Tobias Schultze <http://tobion.de>
027   */
028  class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface
029  {
030      protected $routes;
031      protected $context;
032   
033      /**
034       * @var bool|null
035       */
036      protected $strictRequirements = true;
037   
038      protected $logger;
039   
040      /**
041       * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL.
042       *
043       * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars
044       * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g.
045       * "?" and "#" (would be interpreted wrongly as query and fragment identifier),
046       * "'" and """ (are used as delimiters in HTML).
047       */
048      protected $decodedChars = [
049          // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning
050          // some webservers don't allow the slash in encoded form in the path for security reasons anyway
051          // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss
052          '%2F' => '/',
053          // the following chars are general delimiters in the URI specification but have only special meaning in the authority component
054          // so they can safely be used in the path in unencoded form
055          '%40' => '@',
056          '%3A' => ':',
057          // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally
058          // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability
059          '%3B' => ';',
060          '%2C' => ',',
061          '%3D' => '=',
062          '%2B' => '+',
063          '%21' => '!',
064          '%2A' => '*',
065          '%7C' => '|',
066      ];
067   
068      public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null)
069      {
070          $this->routes = $routes;
071          $this->context = $context;
072          $this->logger = $logger;
073      }
074   
075      /**
076       * {@inheritdoc}
077       */
078      public function setContext(RequestContext $context)
079      {
080          $this->context = $context;
081      }
082   
083      /**
084       * {@inheritdoc}
085       */
086      public function getContext()
087      {
088          return $this->context;
089      }
090   
091      /**
092       * {@inheritdoc}
093       */
094      public function setStrictRequirements($enabled)
095      {
096          $this->strictRequirements = null === $enabled ? null : (bool) $enabled;
097      }
098   
099      /**
100       * {@inheritdoc}
101       */
102      public function isStrictRequirements()
103      {
104          return $this->strictRequirements;
105      }
106   
107      /**
108       * {@inheritdoc}
109       */
110      public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
111      {
112          if (null === $route = $this->routes->get($name)) {
113              throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
114          }
115   
116          // the Route has a cache of its own and is not recompiled as long as it does not get modified
117          $compiledRoute = $route->compile();
118   
119          return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes());
120      }
121   
122      /**
123       * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route
124       * @throws InvalidParameterException           When a parameter value for a placeholder is not correct because
125       *                                             it does not match the requirement
126       *
127       * @return string|null
128       */
129      protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, array $requiredSchemes = [])
130      {
131          $variables = array_flip($variables);
132          $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
133   
134          // all params must be given
135          if ($diff = array_diff_key($variables, $mergedParams)) {
136              throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name));
137          }
138   
139          $url = '';
140          $optional = true;
141          $message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.';
142          foreach ($tokens as $token) {
143              if ('variable' === $token[0]) {
144                  if (!$optional || !\array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) {
145                      // check requirement (while ignoring look-around patterns)
146                      if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
147                          if ($this->strictRequirements) {
148                              throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
149                          }
150   
151                          if ($this->logger) {
152                              $this->logger->error($message, ['parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]]);
153                          }
154   
155                          return null;
156                      }
157   
158                      $url = $token[1].$mergedParams[$token[3]].$url;
159                      $optional = false;
160                  }
161              } else {
162                  // static text
163                  $url = $token[1].$url;
164                  $optional = false;
165              }
166          }
167   
168          if ('' === $url) {
169              $url = '/';
170          }
171   
172          // the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request)
173          $url = strtr(rawurlencode($url), $this->decodedChars);
174   
175          // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3
176          // so we need to encode them as they are not used for this purpose here
177          // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route
178          $url = strtr($url, ['/../' => '/%2E%2E/', '/./' => '/%2E/']);
179          if ('/..' === substr($url, -3)) {
180              $url = substr($url, 0, -2).'%2E%2E';
181          } elseif ('/.' === substr($url, -2)) {
182              $url = substr($url, 0, -1).'%2E';
183          }
184   
185          $schemeAuthority = '';
186          $host = $this->context->getHost();
187          $scheme = $this->context->getScheme();
188   
189          if ($requiredSchemes) {
190              if (!\in_array($scheme, $requiredSchemes, true)) {
191                  $referenceType = self::ABSOLUTE_URL;
192                  $scheme = current($requiredSchemes);
193              }
194          }
195   
196          if ($hostTokens) {
197              $routeHost = '';
198              foreach ($hostTokens as $token) {
199                  if ('variable' === $token[0]) {
200                      // check requirement (while ignoring look-around patterns)
201                      if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|<!)((?:[^()\\\\]+|\\\\.|\((?1)\))*)\)/', '', $token[2]).'$#i'.(empty($token[4]) ? '' : 'u'), $mergedParams[$token[3]])) {
202                          if ($this->strictRequirements) {
203                              throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]]));
204                          }
205   
206                          if ($this->logger) {
207                              $this->logger->error($message, ['parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]]);
208                          }
209   
210                          return null;
211                      }
212   
213                      $routeHost = $token[1].$mergedParams[$token[3]].$routeHost;
214                  } else {
215                      $routeHost = $token[1].$routeHost;
216                  }
217              }
218   
219              if ($routeHost !== $host) {
220                  $host = $routeHost;
221                  if (self::ABSOLUTE_URL !== $referenceType) {
222                      $referenceType = self::NETWORK_PATH;
223                  }
224              }
225          }
226   
227          if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) {
228              if ('' !== $host || ('' !== $scheme && 'http' !== $scheme && 'https' !== $scheme)) {
229                  $port = '';
230                  if ('http' === $scheme && 80 !== $this->context->getHttpPort()) {
231                      $port = ':'.$this->context->getHttpPort();
232                  } elseif ('https' === $scheme && 443 !== $this->context->getHttpsPort()) {
233                      $port = ':'.$this->context->getHttpsPort();
234                  }
235   
236                  $schemeAuthority = self::NETWORK_PATH === $referenceType || '' === $scheme ? '//' : "$scheme://";
237                  $schemeAuthority .= $host.$port;
238              }
239          }
240   
241          if (self::RELATIVE_PATH === $referenceType) {
242              $url = self::getRelativePath($this->context->getPathInfo(), $url);
243          } else {
244              $url = $schemeAuthority.$this->context->getBaseUrl().$url;
245          }
246   
247          // add a query string if needed
248          $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) {
249              return $a == $b ? 0 : 1;
250          });
251   
252          // extract fragment
253          $fragment = '';
254          if (isset($defaults['_fragment'])) {
255              $fragment = $defaults['_fragment'];
256          }
257   
258          if (isset($extra['_fragment'])) {
259              $fragment = $extra['_fragment'];
260              unset($extra['_fragment']);
261          }
262   
263          if ($extra && $query = http_build_query($extra, '', '&', \PHP_QUERY_RFC3986)) {
264              // "/" and "?" can be left decoded for better user experience, see
265              // http://tools.ietf.org/html/rfc3986#section-3.4
266              $url .= '?'.strtr($query, ['%2F' => '/']);
267          }
268   
269          if ('' !== $fragment) {
270              $url .= '#'.strtr(rawurlencode($fragment), ['%2F' => '/', '%3F' => '?']);
271          }
272   
273          return $url;
274      }
275   
276      /**
277       * Returns the target path as relative reference from the base path.
278       *
279       * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash.
280       * Both paths must be absolute and not contain relative parts.
281       * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
282       * Furthermore, they can be used to reduce the link size in documents.
283       *
284       * Example target paths, given a base path of "/a/b/c/d":
285       * - "/a/b/c/d"     -> ""
286       * - "/a/b/c/"      -> "./"
287       * - "/a/b/"        -> "../"
288       * - "/a/b/c/other" -> "other"
289       * - "/a/x/y"       -> "../../x/y"
290       *
291       * @param string $basePath   The base path
292       * @param string $targetPath The target path
293       *
294       * @return string The relative target path
295       */
296      public static function getRelativePath($basePath, $targetPath)
297      {
298          if ($basePath === $targetPath) {
299              return '';
300          }
301   
302          $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
303          $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath);
304          array_pop($sourceDirs);
305          $targetFile = array_pop($targetDirs);
306   
307          foreach ($sourceDirs as $i => $dir) {
308              if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
309                  unset($sourceDirs[$i], $targetDirs[$i]);
310              } else {
311                  break;
312              }
313          }
314   
315          $targetDirs[] = $targetFile;
316          $path = str_repeat('../', \count($sourceDirs)).implode('/', $targetDirs);
317   
318          // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
319          // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
320          // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
321          // (see http://tools.ietf.org/html/rfc3986#section-4.2).
322          return '' === $path || '/' === $path[0]
323              || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
324              ? "./$path" : $path;
325      }
326  }
327