[ Index ]

PHP Cross Reference of phpBB-3.1.12-deutsch

title

Body

[close]

/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ -> PhpMatcherDumper.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\Matcher\Dumper;
  13  
  14  use Symfony\Component\Routing\Route;
  15  use Symfony\Component\Routing\RouteCollection;
  16  
  17  /**
  18   * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
  19   *
  20   * @author Fabien Potencier <fabien@symfony.com>
  21   * @author Tobias Schultze <http://tobion.de>
  22   * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
  23   */
  24  class PhpMatcherDumper extends MatcherDumper
  25  {
  26      /**
  27       * Dumps a set of routes to a PHP class.
  28       *
  29       * Available options:
  30       *
  31       *  * class:      The class name
  32       *  * base_class: The base class name
  33       *
  34       * @param array $options An array of options
  35       *
  36       * @return string A PHP class representing the matcher class
  37       */
  38      public function dump(array $options = array())
  39      {
  40          $options = array_replace(array(
  41              'class' => 'ProjectUrlMatcher',
  42              'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
  43          ), $options);
  44  
  45          // trailing slash support is only enabled if we know how to redirect the user
  46          $interfaces = class_implements($options['base_class']);
  47          $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
  48  
  49          return <<<EOF
  50  <?php
  51  
  52  use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  53  use Symfony\Component\Routing\Exception\ResourceNotFoundException;
  54  use Symfony\Component\Routing\RequestContext;
  55  
  56  /**
  57   * {$options['class']}.
  58   *
  59   * This class has been auto-generated
  60   * by the Symfony Routing Component.
  61   */
  62  class {$options['class']} extends {$options['base_class']}
  63  {
  64      /**
  65       * Constructor.
  66       */
  67      public function __construct(RequestContext \$context)
  68      {
  69          \$this->context = \$context;
  70      }
  71  
  72  {$this->generateMatchMethod($supportsRedirections)}
  73  }
  74  
  75  EOF;
  76      }
  77  
  78      /**
  79       * Generates the code for the match method implementing UrlMatcherInterface.
  80       *
  81       * @param bool $supportsRedirections Whether redirections are supported by the base class
  82       *
  83       * @return string Match method as PHP code
  84       */
  85      private function generateMatchMethod($supportsRedirections)
  86      {
  87          $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
  88  
  89          return <<<EOF
  90      public function match(\$pathinfo)
  91      {
  92          \$allow = array();
  93          \$pathinfo = rawurldecode(\$pathinfo);
  94  
  95  $code
  96  
  97          throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
  98      }
  99  EOF;
 100      }
 101  
 102      /**
 103       * Generates PHP code to match a RouteCollection with all its routes.
 104       *
 105       * @param RouteCollection $routes               A RouteCollection instance
 106       * @param bool            $supportsRedirections Whether redirections are supported by the base class
 107       *
 108       * @return string PHP code
 109       */
 110      private function compileRoutes(RouteCollection $routes, $supportsRedirections)
 111      {
 112          $fetchedHost = false;
 113  
 114          $groups = $this->groupRoutesByHostRegex($routes);
 115          $code = '';
 116  
 117          foreach ($groups as $collection) {
 118              if (null !== $regex = $collection->getAttribute('host_regex')) {
 119                  if (!$fetchedHost) {
 120                      $code .= "        \$host = \$this->context->getHost();\n\n";
 121                      $fetchedHost = true;
 122                  }
 123  
 124                  $code .= sprintf("        if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
 125              }
 126  
 127              $tree = $this->buildPrefixTree($collection);
 128              $groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections);
 129  
 130              if (null !== $regex) {
 131                  // apply extra indention at each line (except empty ones)
 132                  $groupCode = preg_replace('/^.{2,}$/m', '    $0', $groupCode);
 133                  $code .= $groupCode;
 134                  $code .= "        }\n\n";
 135              } else {
 136                  $code .= $groupCode;
 137              }
 138          }
 139  
 140          return $code;
 141      }
 142  
 143      /**
 144       * Generates PHP code recursively to match a tree of routes.
 145       *
 146       * @param DumperPrefixCollection $collection           A DumperPrefixCollection instance
 147       * @param bool                   $supportsRedirections Whether redirections are supported by the base class
 148       * @param string                 $parentPrefix         Prefix of the parent collection
 149       *
 150       * @return string PHP code
 151       */
 152      private function compilePrefixRoutes(DumperPrefixCollection $collection, $supportsRedirections, $parentPrefix = '')
 153      {
 154          $code = '';
 155          $prefix = $collection->getPrefix();
 156          $optimizable = 1 < strlen($prefix) && 1 < count($collection->all());
 157          $optimizedPrefix = $parentPrefix;
 158  
 159          if ($optimizable) {
 160              $optimizedPrefix = $prefix;
 161  
 162              $code .= sprintf("    if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
 163          }
 164  
 165          foreach ($collection as $route) {
 166              if ($route instanceof DumperCollection) {
 167                  $code .= $this->compilePrefixRoutes($route, $supportsRedirections, $optimizedPrefix);
 168              } else {
 169                  $code .= $this->compileRoute($route->getRoute(), $route->getName(), $supportsRedirections, $optimizedPrefix)."\n";
 170              }
 171          }
 172  
 173          if ($optimizable) {
 174              $code .= "    }\n\n";
 175              // apply extra indention at each line (except empty ones)
 176              $code = preg_replace('/^.{2,}$/m', '    $0', $code);
 177          }
 178  
 179          return $code;
 180      }
 181  
 182      /**
 183       * Compiles a single Route to PHP code used to match it against the path info.
 184       *
 185       * @param Route       $route                A Route instance
 186       * @param string      $name                 The name of the Route
 187       * @param bool        $supportsRedirections Whether redirections are supported by the base class
 188       * @param string|null $parentPrefix         The prefix of the parent collection used to optimize the code
 189       *
 190       * @return string PHP code
 191       *
 192       * @throws \LogicException
 193       */
 194      private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
 195      {
 196          $code = '';
 197          $compiledRoute = $route->compile();
 198          $conditions = array();
 199          $hasTrailingSlash = false;
 200          $matches = false;
 201          $hostMatches = false;
 202          $methods = array();
 203  
 204          if ($req = $route->getRequirement('_method')) {
 205              $methods = explode('|', strtoupper($req));
 206              // GET and HEAD are equivalent
 207              if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
 208                  $methods[] = 'HEAD';
 209              }
 210          }
 211  
 212          $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
 213  
 214          if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
 215              if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
 216                  $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
 217                  $hasTrailingSlash = true;
 218              } else {
 219                  $conditions[] = sprintf('$pathinfo === %s', var_export(str_replace('\\', '', $m['url']), true));
 220              }
 221          } else {
 222              if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
 223                  $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true));
 224              }
 225  
 226              $regex = $compiledRoute->getRegex();
 227              if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
 228                  $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
 229                  $hasTrailingSlash = true;
 230              }
 231              $conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true));
 232  
 233              $matches = true;
 234          }
 235  
 236          if ($compiledRoute->getHostVariables()) {
 237              $hostMatches = true;
 238          }
 239  
 240          $conditions = implode(' && ', $conditions);
 241  
 242          $code .= <<<EOF
 243          // $name
 244          if ($conditions) {
 245  
 246  EOF;
 247  
 248          if ($methods) {
 249              $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
 250  
 251              if (1 === count($methods)) {
 252                  $code .= <<<EOF
 253              if (\$this->context->getMethod() != '$methods[0]') {
 254                  \$allow[] = '$methods[0]';
 255                  goto $gotoname;
 256              }
 257  
 258  
 259  EOF;
 260              } else {
 261                  $methods = implode("', '", $methods);
 262                  $code .= <<<EOF
 263              if (!in_array(\$this->context->getMethod(), array('$methods'))) {
 264                  \$allow = array_merge(\$allow, array('$methods'));
 265                  goto $gotoname;
 266              }
 267  
 268  
 269  EOF;
 270              }
 271          }
 272  
 273          if ($hasTrailingSlash) {
 274              $code .= <<<EOF
 275              if (substr(\$pathinfo, -1) !== '/') {
 276                  return \$this->redirect(\$pathinfo.'/', '$name');
 277              }
 278  
 279  
 280  EOF;
 281          }
 282  
 283          if ($scheme = $route->getRequirement('_scheme')) {
 284              if (!$supportsRedirections) {
 285                  throw new \LogicException('The "_scheme" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
 286              }
 287  
 288              $code .= <<<EOF
 289              if (\$this->context->getScheme() !== '$scheme') {
 290                  return \$this->redirect(\$pathinfo, '$name', '$scheme');
 291              }
 292  
 293  
 294  EOF;
 295          }
 296  
 297          // optimize parameters array
 298          if ($matches || $hostMatches) {
 299              $vars = array();
 300              if ($hostMatches) {
 301                  $vars[] = '$hostMatches';
 302              }
 303              if ($matches) {
 304                  $vars[] = '$matches';
 305              }
 306              $vars[] = "array('_route' => '$name')";
 307  
 308              $code .= sprintf("            return \$this->mergeDefaults(array_replace(%s), %s);\n", implode(', ', $vars), str_replace("\n", '', var_export($route->getDefaults(), true)));
 309          } elseif ($route->getDefaults()) {
 310              $code .= sprintf("            return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
 311          } else {
 312              $code .= sprintf("            return array('_route' => '%s');\n", $name);
 313          }
 314          $code .= "        }\n";
 315  
 316          if ($methods) {
 317              $code .= "        $gotoname:\n";
 318          }
 319  
 320          return $code;
 321      }
 322  
 323      /**
 324       * Groups consecutive routes having the same host regex.
 325       *
 326       * The result is a collection of collections of routes having the same host regex.
 327       *
 328       * @param RouteCollection $routes A flat RouteCollection
 329       *
 330       * @return DumperCollection A collection with routes grouped by host regex in sub-collections
 331       */
 332      private function groupRoutesByHostRegex(RouteCollection $routes)
 333      {
 334          $groups = new DumperCollection();
 335  
 336          $currentGroup = new DumperCollection();
 337          $currentGroup->setAttribute('host_regex', null);
 338          $groups->add($currentGroup);
 339  
 340          foreach ($routes as $name => $route) {
 341              $hostRegex = $route->compile()->getHostRegex();
 342              if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
 343                  $currentGroup = new DumperCollection();
 344                  $currentGroup->setAttribute('host_regex', $hostRegex);
 345                  $groups->add($currentGroup);
 346              }
 347              $currentGroup->add(new DumperRoute($name, $route));
 348          }
 349  
 350          return $groups;
 351      }
 352  
 353      /**
 354       * Organizes the routes into a prefix tree.
 355       *
 356       * Routes order is preserved such that traversing the tree will traverse the
 357       * routes in the origin order.
 358       *
 359       * @param DumperCollection $collection A collection of routes
 360       *
 361       * @return DumperPrefixCollection
 362       */
 363      private function buildPrefixTree(DumperCollection $collection)
 364      {
 365          $tree = new DumperPrefixCollection();
 366          $current = $tree;
 367  
 368          foreach ($collection as $route) {
 369              $current = $current->addPrefixRoute($route);
 370          }
 371  
 372          $tree->mergeSlashNodes();
 373  
 374          return $tree;
 375      }
 376  }


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