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