Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

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: 09.10.2024, 12:57 - Dateigröße: 13.45 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\Routing\Route;
015  use Symfony\Component\Routing\RouteCollection;
016  use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
017  use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
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 = array();
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 = array())
048      {
049          $options = array_replace(array(
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   * {$options['class']}.
067   *
068   * This class has been auto-generated
069   * by the Symfony Routing Component.
070   */
071  class {$options['class']} extends {$options['base_class']}
072  {
073      /**
074       * Constructor.
075       */
076      public function __construct(RequestContext \$context)
077      {
078          \$this->context = \$context;
079      }
080   
081  {$this->generateMatchMethod($supportsRedirections)}
082  }
083   
084  EOF;
085   
086      }
087   
088      public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
089      {
090          $this->expressionLanguageProviders[] = $provider;
091      }
092   
093      /**
094       * Generates the code for the match method implementing UrlMatcherInterface.
095       *
096       * @param bool $supportsRedirections Whether redirections are supported by the base class
097       *
098       * @return string Match method as PHP code
099       */
100      private function generateMatchMethod($supportsRedirections)
101      {
102          $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
103   
104          return <<<EOF
105      public function match(\$pathinfo)
106      {
107          \$allow = array();
108          \$pathinfo = rawurldecode(\$pathinfo);
109          \$context = \$this->context;
110          \$request = \$this->request;
111   
112  $code
113   
114          throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
115      }
116  EOF;
117   
118      }
119   
120      /**
121       * Generates PHP code to match a RouteCollection with all its routes.
122       *
123       * @param RouteCollection $routes               A RouteCollection instance
124       * @param bool            $supportsRedirections Whether redirections are supported by the base class
125       *
126       * @return string PHP code
127       */
128      private function compileRoutes(RouteCollection $routes, $supportsRedirections)
129      {
130          $fetchedHost = false;
131   
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 = \$this->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->buildPrefixTree($collection);
146              $groupCode = $this->compilePrefixRoutes($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          return $code;
159      }
160   
161      /**
162       * Generates PHP code recursively to match a tree of routes.
163       *
164       * @param DumperPrefixCollection $collection           A DumperPrefixCollection instance
165       * @param bool                   $supportsRedirections Whether redirections are supported by the base class
166       * @param string                 $parentPrefix         Prefix of the parent collection
167       *
168       * @return string PHP code
169       */
170      private function compilePrefixRoutes(DumperPrefixCollection $collection, $supportsRedirections, $parentPrefix = '')
171      {
172          $code = '';
173          $prefix = $collection->getPrefix();
174          $optimizable = 1 < strlen($prefix) && 1 < count($collection->all());
175          $optimizedPrefix = $parentPrefix;
176   
177          if ($optimizable) {
178              $optimizedPrefix = $prefix;
179   
180              $code .= sprintf("    if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
181          }
182   
183          foreach ($collection as $route) {
184              if ($route instanceof DumperCollection) {
185                  $code .= $this->compilePrefixRoutes($route, $supportsRedirections, $optimizedPrefix);
186              } else {
187                  $code .= $this->compileRoute($route->getRoute(), $route->getName(), $supportsRedirections, $optimizedPrefix)."\n";
188              }
189          }
190   
191          if ($optimizable) {
192              $code .= "    }\n\n";
193              // apply extra indention at each line (except empty ones)
194              $code = preg_replace('/^.{2,}$/m', '    $0', $code);
195          }
196   
197          return $code;
198      }
199   
200      /**
201       * Compiles a single Route to PHP code used to match it against the path info.
202       *
203       * @param Route       $route                A Route instance
204       * @param string      $name                 The name of the Route
205       * @param bool        $supportsRedirections Whether redirections are supported by the base class
206       * @param string|null $parentPrefix         The prefix of the parent collection used to optimize the code
207       *
208       * @return string PHP code
209       *
210       * @throws \LogicException
211       */
212      private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
213      {
214          $code = '';
215          $compiledRoute = $route->compile();
216          $conditions = array();
217          $hasTrailingSlash = false;
218          $matches = false;
219          $hostMatches = false;
220          $methods = $route->getMethods();
221   
222          // GET and HEAD are equivalent
223          if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
224              $methods[] = 'HEAD';
225          }
226   
227          $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
228   
229          if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
230              if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
231                  $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
232                  $hasTrailingSlash = true;
233              } else {
234                  $conditions[] = sprintf('$pathinfo === %s', var_export(str_replace('\\', '', $m['url']), true));
235              }
236          } else {
237              if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
238                  $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true));
239              }
240   
241              $regex = $compiledRoute->getRegex();
242              if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
243                  $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
244                  $hasTrailingSlash = true;
245              }
246              $conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true));
247   
248              $matches = true;
249          }
250   
251          if ($compiledRoute->getHostVariables()) {
252              $hostMatches = true;
253          }
254   
255          if ($route->getCondition()) {
256              $conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request'));
257          }
258   
259          $conditions = implode(' && ', $conditions);
260   
261          $code .= <<<EOF
262          // $name
263          if ($conditions) {
264   
265  EOF;
266   
267   
268          $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
269          if ($methods) {
270              if (1 === count($methods)) {
271                  $code .= <<<EOF
272              if (\$this->context->getMethod() != '$methods[0]') {
273                  \$allow[] = '$methods[0]';
274                  goto $gotoname;
275              }
276   
277   
278  EOF;
279   
280              } else {
281                  $methods = implode("', '", $methods);
282                  $code .= <<<EOF
283              if (!in_array(\$this->context->getMethod(), array('$methods'))) {
284                  \$allow = array_merge(\$allow, array('$methods'));
285                  goto $gotoname;
286              }
287   
288   
289  EOF;
290   
291              }
292          }
293   
294          if ($hasTrailingSlash) {
295              $code .= <<<EOF
296              if (substr(\$pathinfo, -1) !== '/') {
297                  return \$this->redirect(\$pathinfo.'/', '$name');
298              }
299   
300   
301  EOF;
302   
303          }
304   
305          if ($schemes = $route->getSchemes()) {
306              if (!$supportsRedirections) {
307                  throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
308              }
309              $schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
310              $code .= <<<EOF
311              \$requiredSchemes = $schemes;
312              if (!isset(\$requiredSchemes[\$this->context->getScheme()])) {
313                  return \$this->redirect(\$pathinfo, '$name', key(\$requiredSchemes));
314              }
315   
316   
317  EOF;
318   
319          }
320   
321          // optimize parameters array
322          if ($matches || $hostMatches) {
323              $vars = array();
324              if ($hostMatches) {
325                  $vars[] = '$hostMatches';
326              }
327              if ($matches) {
328                  $vars[] = '$matches';
329              }
330              $vars[] = "array('_route' => '$name')";
331   
332              $code .= sprintf(
333                  "            return \$this->mergeDefaults(array_replace(%s), %s);\n",
334                  implode(', ', $vars),
335                  str_replace("\n", '', var_export($route->getDefaults(), true))
336              );
337          } elseif ($route->getDefaults()) {
338              $code .= sprintf("            return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
339          } else {
340              $code .= sprintf("            return array('_route' => '%s');\n", $name);
341          }
342          $code .= "        }\n";
343   
344          if ($methods) {
345              $code .= "        $gotoname:\n";
346          }
347   
348          return $code;
349      }
350   
351      /**
352       * Groups consecutive routes having the same host regex.
353       *
354       * The result is a collection of collections of routes having the same host regex.
355       *
356       * @param RouteCollection $routes A flat RouteCollection
357       *
358       * @return DumperCollection A collection with routes grouped by host regex in sub-collections
359       */
360      private function groupRoutesByHostRegex(RouteCollection $routes)
361      {
362          $groups = new DumperCollection();
363   
364          $currentGroup = new DumperCollection();
365          $currentGroup->setAttribute('host_regex', null);
366          $groups->add($currentGroup);
367   
368          foreach ($routes as $name => $route) {
369              $hostRegex = $route->compile()->getHostRegex();
370              if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
371                  $currentGroup = new DumperCollection();
372                  $currentGroup->setAttribute('host_regex', $hostRegex);
373                  $groups->add($currentGroup);
374              }
375              $currentGroup->add(new DumperRoute($name, $route));
376          }
377   
378          return $groups;
379      }
380   
381      /**
382       * Organizes the routes into a prefix tree.
383       *
384       * Routes order is preserved such that traversing the tree will traverse the
385       * routes in the origin order.
386       *
387       * @param DumperCollection $collection A collection of routes
388       *
389       * @return DumperPrefixCollection
390       */
391      private function buildPrefixTree(DumperCollection $collection)
392      {
393          $tree = new DumperPrefixCollection();
394          $current = $tree;
395   
396          foreach ($collection as $route) {
397              $current = $current->addPrefixRoute($route);
398          }
399   
400          $tree->mergeSlashNodes();
401   
402          return $tree;
403      }
404   
405      private function getExpressionLanguage()
406      {
407          if (null === $this->expressionLanguage) {
408              if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
409                  throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
410              }
411              $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
412          }
413   
414          return $this->expressionLanguage;
415      }
416  }
417