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

PhpMatcherDumper.php

Zuletzt modifiziert: 02.04.2025, 15:03 - Dateigröße: 14.76 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\Matcher\Dumper;
013   
014  use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
015  use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
016  use Symfony\Component\Routing\Route;
017  use Symfony\Component\Routing\RouteCollection;
018   
019  /**
020   * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
021   *
022   * @author Fabien Potencier <fabien@symfony.com>
023   * @author Tobias Schultze <http://tobion.de>
024   * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
025   */
026  class PhpMatcherDumper extends MatcherDumper
027  {
028      private $expressionLanguage;
029   
030      /**
031       * @var ExpressionFunctionProviderInterface[]
032       */
033      private $expressionLanguageProviders = [];
034   
035      /**
036       * Dumps a set of routes to a PHP class.
037       *
038       * Available options:
039       *
040       *  * class:      The class name
041       *  * base_class: The base class name
042       *
043       * @param array $options An array of options
044       *
045       * @return string A PHP class representing the matcher class
046       */
047      public function dump(array $options = [])
048      {
049          $options = array_replace([
050              'class' => 'ProjectUrlMatcher',
051              'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
052          ], $options);
053   
054          // trailing slash support is only enabled if we know how to redirect the user
055          $interfaces = class_implements($options['base_class']);
056          $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
057   
058          return <<<EOF
059  <?php
060   
061  use Symfony\Component\Routing\Exception\MethodNotAllowedException;
062  use Symfony\Component\Routing\Exception\ResourceNotFoundException;
063  use Symfony\Component\Routing\RequestContext;
064   
065  /**
066   * This class has been auto-generated
067   * by the Symfony Routing Component.
068   */
069  class {$options['class']} extends {$options['base_class']}
070  {
071      public function __construct(RequestContext \$context)
072      {
073          \$this->context = \$context;
074      }
075   
076  {$this->generateMatchMethod($supportsRedirections)}
077  }
078   
079  EOF;
080   
081      }
082   
083      public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
084      {
085          $this->expressionLanguageProviders[] = $provider;
086      }
087   
088      /**
089       * Generates the code for the match method implementing UrlMatcherInterface.
090       *
091       * @param bool $supportsRedirections Whether redirections are supported by the base class
092       *
093       * @return string Match method as PHP code
094       */
095      private function generateMatchMethod($supportsRedirections)
096      {
097          $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
098   
099          return <<<EOF
100      public function match(\$rawPathinfo)
101      {
102          \$allow = [];
103          \$pathinfo = rawurldecode(\$rawPathinfo);
104          \$trimmedPathinfo = rtrim(\$pathinfo, '/');
105          \$context = \$this->context;
106          \$request = \$this->request ?: \$this->createRequest(\$pathinfo);
107          \$requestMethod = \$canonicalMethod = \$context->getMethod();
108   
109          if ('HEAD' === \$requestMethod) {
110              \$canonicalMethod = 'GET';
111          }
112   
113  $code
114   
115          throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
116      }
117  EOF;
118   
119      }
120   
121      /**
122       * Generates PHP code to match a RouteCollection with all its routes.
123       *
124       * @param RouteCollection $routes               A RouteCollection instance
125       * @param bool            $supportsRedirections Whether redirections are supported by the base class
126       *
127       * @return string PHP code
128       */
129      private function compileRoutes(RouteCollection $routes, $supportsRedirections)
130      {
131          $fetchedHost = false;
132          $groups = $this->groupRoutesByHostRegex($routes);
133          $code = '';
134   
135          foreach ($groups as $collection) {
136              if (null !== $regex = $collection->getAttribute('host_regex')) {
137                  if (!$fetchedHost) {
138                      $code .= "        \$host = \$context->getHost();\n\n";
139                      $fetchedHost = true;
140                  }
141   
142                  $code .= sprintf("        if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
143              }
144   
145              $tree = $this->buildStaticPrefixCollection($collection);
146              $groupCode = $this->compileStaticPrefixRoutes($tree, $supportsRedirections);
147   
148              if (null !== $regex) {
149                  // apply extra indention at each line (except empty ones)
150                  $groupCode = preg_replace('/^.{2,}$/m', '    $0', $groupCode);
151                  $code .= $groupCode;
152                  $code .= "        }\n\n";
153              } else {
154                  $code .= $groupCode;
155              }
156          }
157   
158          // used to display the Welcome Page in apps that don't define a homepage
159          $code .= "        if ('/' === \$pathinfo && !\$allow) {\n";
160          $code .= "            throw new Symfony\Component\Routing\Exception\NoConfigurationException();\n";
161          $code .= "        }\n";
162   
163          return $code;
164      }
165   
166      private function buildStaticPrefixCollection(DumperCollection $collection)
167      {
168          $prefixCollection = new StaticPrefixCollection();
169   
170          foreach ($collection as $dumperRoute) {
171              $prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix();
172              $prefixCollection->addRoute($prefix, $dumperRoute);
173          }
174   
175          $prefixCollection->optimizeGroups();
176   
177          return $prefixCollection;
178      }
179   
180      /**
181       * Generates PHP code to match a tree of routes.
182       *
183       * @param StaticPrefixCollection $collection           A StaticPrefixCollection instance
184       * @param bool                   $supportsRedirections Whether redirections are supported by the base class
185       * @param string                 $ifOrElseIf           either "if" or "elseif" to influence chaining
186       *
187       * @return string PHP code
188       */
189      private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if')
190      {
191          $code = '';
192          $prefix = $collection->getPrefix();
193   
194          if (!empty($prefix) && '/' !== $prefix) {
195              $code .= sprintf("    %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true));
196          }
197   
198          $ifOrElseIf = 'if';
199   
200          foreach ($collection->getItems() as $route) {
201              if ($route instanceof StaticPrefixCollection) {
202                  $code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf);
203                  $ifOrElseIf = 'elseif';
204              } else {
205                  $code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n";
206                  $ifOrElseIf = 'if';
207              }
208          }
209   
210          if (!empty($prefix) && '/' !== $prefix) {
211              $code .= "    }\n\n";
212              // apply extra indention at each line (except empty ones)
213              $code = preg_replace('/^.{2,}$/m', '    $0', $code);
214          }
215   
216          return $code;
217      }
218   
219      /**
220       * Compiles a single Route to PHP code used to match it against the path info.
221       *
222       * @param Route       $route                A Route instance
223       * @param string      $name                 The name of the Route
224       * @param bool        $supportsRedirections Whether redirections are supported by the base class
225       * @param string|null $parentPrefix         The prefix of the parent collection used to optimize the code
226       *
227       * @return string PHP code
228       *
229       * @throws \LogicException
230       */
231      private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
232      {
233          $code = '';
234          $compiledRoute = $route->compile();
235          $conditions = [];
236          $hasTrailingSlash = false;
237          $matches = false;
238          $hostMatches = false;
239          $methods = $route->getMethods();
240   
241          $supportsTrailingSlash = $supportsRedirections && (!$methods || \in_array('GET', $methods));
242          $regex = $compiledRoute->getRegex();
243   
244          if (!\count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#'.('u' === substr($regex, -1) ? 'u' : ''), $regex, $m)) {
245              if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) {
246                  $conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
247                  $hasTrailingSlash = true;
248              } else {
249                  $conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true));
250              }
251          } else {
252              if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
253                  $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true));
254              }
255   
256              if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
257                  $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
258                  $hasTrailingSlash = true;
259              }
260              $conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true));
261   
262              $matches = true;
263          }
264   
265          if ($compiledRoute->getHostVariables()) {
266              $hostMatches = true;
267          }
268   
269          if ($route->getCondition()) {
270              $conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), ['context', 'request']);
271          }
272   
273          $conditions = implode(' && ', $conditions);
274   
275          $code .= <<<EOF
276          // $name
277          if ($conditions) {
278   
279  EOF;
280   
281   
282          $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
283   
284          // the offset where the return value is appended below, with indentation
285          $retOffset = 12 + \strlen($code);
286   
287          // optimize parameters array
288          if ($matches || $hostMatches) {
289              $vars = [];
290              if ($hostMatches) {
291                  $vars[] = '$hostMatches';
292              }
293              if ($matches) {
294                  $vars[] = '$matches';
295              }
296              $vars[] = "['_route' => '$name']";
297   
298              $code .= sprintf(
299                  "            \$ret = \$this->mergeDefaults(array_replace(%s), %s);\n",
300                  implode(', ', $vars),
301                  str_replace("\n", '', var_export($route->getDefaults(), true))
302              );
303          } elseif ($route->getDefaults()) {
304              $code .= sprintf("            \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), ['_route' => $name]), true)));
305          } else {
306              $code .= sprintf("            \$ret = ['_route' => '%s'];\n", $name);
307          }
308   
309          if ($hasTrailingSlash) {
310              $code .= <<<EOF
311              if ('/' === substr(\$pathinfo, -1)) {
312                  // no-op
313              } elseif ('GET' !== \$canonicalMethod) {
314                  goto $gotoname;
315              } else {
316                  return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', '$name'));
317              }
318   
319   
320  EOF;
321   
322          }
323   
324          if ($methods) {
325              $methodVariable = \in_array('GET', $methods) ? '$canonicalMethod' : '$requestMethod';
326              $methods = implode("', '", $methods);
327          }
328   
329          if ($schemes = $route->getSchemes()) {
330              if (!$supportsRedirections) {
331                  throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
332              }
333              $schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
334              if ($methods) {
335                  $code .= <<<EOF
336              \$requiredSchemes = $schemes;
337              \$hasRequiredScheme = isset(\$requiredSchemes[\$context->getScheme()]);
338              if (!in_array($methodVariable, ['$methods'])) {
339                  if (\$hasRequiredScheme) {
340                      \$allow = array_merge(\$allow, ['$methods']);
341                  }
342                  goto $gotoname;
343              }
344              if (!\$hasRequiredScheme) {
345                  if ('GET' !== \$canonicalMethod) {
346                      goto $gotoname;
347                  }
348   
349                  return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)));
350              }
351   
352   
353  EOF;
354   
355              } else {
356                  $code .= <<<EOF
357              \$requiredSchemes = $schemes;
358              if (!isset(\$requiredSchemes[\$context->getScheme()])) {
359                  if ('GET' !== \$canonicalMethod) {
360                      goto $gotoname;
361                  }
362   
363                  return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes)));
364              }
365   
366   
367  EOF;
368   
369              }
370          } elseif ($methods) {
371              $code .= <<<EOF
372              if (!in_array($methodVariable, ['$methods'])) {
373                  \$allow = array_merge(\$allow, ['$methods']);
374                  goto $gotoname;
375              }
376   
377   
378  EOF;
379   
380          }
381   
382          if ($hasTrailingSlash || $schemes || $methods) {
383              $code .= "            return \$ret;\n";
384          } else {
385              $code = substr_replace($code, 'return', $retOffset, 6);
386          }
387          $code .= "        }\n";
388   
389          if ($hasTrailingSlash || $schemes || $methods) {
390              $code .= "        $gotoname:\n";
391          }
392   
393          return $code;
394      }
395   
396      /**
397       * Groups consecutive routes having the same host regex.
398       *
399       * The result is a collection of collections of routes having the same host regex.
400       *
401       * @param RouteCollection $routes A flat RouteCollection
402       *
403       * @return DumperCollection A collection with routes grouped by host regex in sub-collections
404       */
405      private function groupRoutesByHostRegex(RouteCollection $routes)
406      {
407          $groups = new DumperCollection();
408          $currentGroup = new DumperCollection();
409          $currentGroup->setAttribute('host_regex', null);
410          $groups->add($currentGroup);
411   
412          foreach ($routes as $name => $route) {
413              $hostRegex = $route->compile()->getHostRegex();
414              if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
415                  $currentGroup = new DumperCollection();
416                  $currentGroup->setAttribute('host_regex', $hostRegex);
417                  $groups->add($currentGroup);
418              }
419              $currentGroup->add(new DumperRoute($name, $route));
420          }
421   
422          return $groups;
423      }
424   
425      private function getExpressionLanguage()
426      {
427          if (null === $this->expressionLanguage) {
428              if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
429                  throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
430              }
431              $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
432          }
433   
434          return $this->expressionLanguage;
435      }
436  }
437