Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

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:59 - Dateigröße: 12.21 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   
017  /**
018   * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
019   *
020   * @author Fabien Potencier <fabien@symfony.com>
021   * @author Tobias Schultze <http://tobion.de>
022   * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
023   */
024  class PhpMatcherDumper extends MatcherDumper
025  {
026      /**
027       * Dumps a set of routes to a PHP class.
028       *
029       * Available options:
030       *
031       *  * class:      The class name
032       *  * base_class: The base class name
033       *
034       * @param array $options An array of options
035       *
036       * @return string A PHP class representing the matcher class
037       */
038      public function dump(array $options = array())
039      {
040          $options = array_replace(array(
041              'class'      => 'ProjectUrlMatcher',
042              'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
043          ), $options);
044   
045          // trailing slash support is only enabled if we know how to redirect the user
046          $interfaces = class_implements($options['base_class']);
047          $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
048   
049          return <<<EOF
050  <?php
051   
052  use Symfony\Component\Routing\Exception\MethodNotAllowedException;
053  use Symfony\Component\Routing\Exception\ResourceNotFoundException;
054  use Symfony\Component\Routing\RequestContext;
055   
056  /**
057   * {$options['class']}
058   *
059   * This class has been auto-generated
060   * by the Symfony Routing Component.
061   */
062  class {$options['class']} extends {$options['base_class']}
063  {
064      /**
065       * Constructor.
066       */
067      public function __construct(RequestContext \$context)
068      {
069          \$this->context = \$context;
070      }
071   
072  {$this->generateMatchMethod($supportsRedirections)}
073  }
074   
075  EOF;
076   
077      }
078   
079      /**
080       * Generates the code for the match method implementing UrlMatcherInterface.
081       *
082       * @param bool    $supportsRedirections Whether redirections are supported by the base class
083       *
084       * @return string Match method as PHP code
085       */
086      private function generateMatchMethod($supportsRedirections)
087      {
088          $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
089   
090          return <<<EOF
091      public function match(\$pathinfo)
092      {
093          \$allow = array();
094          \$pathinfo = rawurldecode(\$pathinfo);
095   
096  $code
097   
098          throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
099      }
100  EOF;
101   
102      }
103   
104      /**
105       * Generates PHP code to match a RouteCollection with all its routes.
106       *
107       * @param RouteCollection $routes               A RouteCollection instance
108       * @param bool            $supportsRedirections Whether redirections are supported by the base class
109       *
110       * @return string PHP code
111       */
112      private function compileRoutes(RouteCollection $routes, $supportsRedirections)
113      {
114          $fetchedHost = false;
115   
116          $groups = $this->groupRoutesByHostRegex($routes);
117          $code = '';
118   
119          foreach ($groups as $collection) {
120              if (null !== $regex = $collection->getAttribute('host_regex')) {
121                  if (!$fetchedHost) {
122                      $code .= "        \$host = \$this->context->getHost();\n\n";
123                      $fetchedHost = true;
124                  }
125   
126                  $code .= sprintf("        if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
127              }
128   
129              $tree = $this->buildPrefixTree($collection);
130              $groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections);
131   
132              if (null !== $regex) {
133                  // apply extra indention at each line (except empty ones)
134                  $groupCode = preg_replace('/^.{2,}$/m', '    $0', $groupCode);
135                  $code .= $groupCode;
136                  $code .= "        }\n\n";
137              } else {
138                  $code .= $groupCode;
139              }
140          }
141   
142          return $code;
143      }
144   
145      /**
146       * Generates PHP code recursively to match a tree of routes
147       *
148       * @param DumperPrefixCollection $collection           A DumperPrefixCollection instance
149       * @param bool                   $supportsRedirections Whether redirections are supported by the base class
150       * @param string                 $parentPrefix         Prefix of the parent collection
151       *
152       * @return string PHP code
153       */
154      private function compilePrefixRoutes(DumperPrefixCollection $collection, $supportsRedirections, $parentPrefix = '')
155      {
156          $code = '';
157          $prefix = $collection->getPrefix();
158          $optimizable = 1 < strlen($prefix) && 1 < count($collection->all());
159          $optimizedPrefix = $parentPrefix;
160   
161          if ($optimizable) {
162              $optimizedPrefix = $prefix;
163   
164              $code .= sprintf("    if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
165          }
166   
167          foreach ($collection as $route) {
168              if ($route instanceof DumperCollection) {
169                  $code .= $this->compilePrefixRoutes($route, $supportsRedirections, $optimizedPrefix);
170              } else {
171                  $code .= $this->compileRoute($route->getRoute(), $route->getName(), $supportsRedirections, $optimizedPrefix)."\n";
172              }
173          }
174   
175          if ($optimizable) {
176              $code .= "    }\n\n";
177              // apply extra indention at each line (except empty ones)
178              $code = preg_replace('/^.{2,}$/m', '    $0', $code);
179          }
180   
181          return $code;
182      }
183   
184      /**
185       * Compiles a single Route to PHP code used to match it against the path info.
186       *
187       * @param Route       $route                A Route instance
188       * @param string      $name                 The name of the Route
189       * @param bool        $supportsRedirections Whether redirections are supported by the base class
190       * @param string|null $parentPrefix         The prefix of the parent collection used to optimize the code
191       *
192       * @return string PHP code
193       *
194       * @throws \LogicException
195       */
196      private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
197      {
198          $code = '';
199          $compiledRoute = $route->compile();
200          $conditions = array();
201          $hasTrailingSlash = false;
202          $matches = false;
203          $hostMatches = false;
204          $methods = array();
205   
206          if ($req = $route->getRequirement('_method')) {
207              $methods = explode('|', strtoupper($req));
208              // GET and HEAD are equivalent
209              if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
210                  $methods[] = 'HEAD';
211              }
212          }
213   
214          $supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
215   
216          if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
217              if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
218                  $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
219                  $hasTrailingSlash = true;
220              } else {
221                  $conditions[] = sprintf("\$pathinfo === %s", var_export(str_replace('\\', '', $m['url']), true));
222              }
223          } else {
224              if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
225                  $conditions[] = sprintf("0 === strpos(\$pathinfo, %s)", var_export($compiledRoute->getStaticPrefix(), true));
226              }
227   
228              $regex = $compiledRoute->getRegex();
229              if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
230                  $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
231                  $hasTrailingSlash = true;
232              }
233              $conditions[] = sprintf("preg_match(%s, \$pathinfo, \$matches)", var_export($regex, true));
234   
235              $matches = true;
236          }
237   
238          if ($compiledRoute->getHostVariables()) {
239              $hostMatches = true;
240          }
241   
242          $conditions = implode(' && ', $conditions);
243   
244          $code .= <<<EOF
245          // $name
246          if ($conditions) {
247   
248  EOF;
249   
250   
251          if ($methods) {
252              $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
253   
254              if (1 === count($methods)) {
255                  $code .= <<<EOF
256              if (\$this->context->getMethod() != '$methods[0]') {
257                  \$allow[] = '$methods[0]';
258                  goto $gotoname;
259              }
260   
261   
262  EOF;
263   
264              } else {
265                  $methods = implode("', '", $methods);
266                  $code .= <<<EOF
267              if (!in_array(\$this->context->getMethod(), array('$methods'))) {
268                  \$allow = array_merge(\$allow, array('$methods'));
269                  goto $gotoname;
270              }
271   
272   
273  EOF;
274   
275              }
276          }
277   
278          if ($hasTrailingSlash) {
279              $code .= <<<EOF
280              if (substr(\$pathinfo, -1) !== '/') {
281                  return \$this->redirect(\$pathinfo.'/', '$name');
282              }
283   
284   
285  EOF;
286   
287          }
288   
289          if ($scheme = $route->getRequirement('_scheme')) {
290              if (!$supportsRedirections) {
291                  throw new \LogicException('The "_scheme" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
292              }
293   
294              $code .= <<<EOF
295              if (\$this->context->getScheme() !== '$scheme') {
296                  return \$this->redirect(\$pathinfo, '$name', '$scheme');
297              }
298   
299   
300  EOF;
301   
302          }
303   
304          // optimize parameters array
305          if ($matches || $hostMatches) {
306              $vars = array();
307              if ($hostMatches) {
308                  $vars[] = '$hostMatches';
309              }
310              if ($matches) {
311                  $vars[] = '$matches';
312              }
313              $vars[] = "array('_route' => '$name')";
314   
315              $code .= sprintf("            return \$this->mergeDefaults(array_replace(%s), %s);\n"
316                  , implode(', ', $vars), str_replace("\n", '', var_export($route->getDefaults(), true)));
317          } elseif ($route->getDefaults()) {
318              $code .= sprintf("            return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
319          } else {
320              $code .= sprintf("            return array('_route' => '%s');\n", $name);
321          }
322          $code .= "        }\n";
323   
324          if ($methods) {
325              $code .= "        $gotoname:\n";
326          }
327   
328          return $code;
329      }
330   
331      /**
332       * Groups consecutive routes having the same host regex.
333       *
334       * The result is a collection of collections of routes having the same host regex.
335       *
336       * @param RouteCollection $routes A flat RouteCollection
337       *
338       * @return DumperCollection A collection with routes grouped by host regex in sub-collections
339       */
340      private function groupRoutesByHostRegex(RouteCollection $routes)
341      {
342          $groups = new DumperCollection();
343   
344          $currentGroup = new DumperCollection();
345          $currentGroup->setAttribute('host_regex', null);
346          $groups->add($currentGroup);
347   
348          foreach ($routes as $name => $route) {
349              $hostRegex = $route->compile()->getHostRegex();
350              if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
351                  $currentGroup = new DumperCollection();
352                  $currentGroup->setAttribute('host_regex', $hostRegex);
353                  $groups->add($currentGroup);
354              }
355              $currentGroup->add(new DumperRoute($name, $route));
356          }
357   
358          return $groups;
359      }
360   
361      /**
362       * Organizes the routes into a prefix tree.
363       *
364       * Routes order is preserved such that traversing the tree will traverse the
365       * routes in the origin order.
366       *
367       * @param DumperCollection $collection A collection of routes
368       *
369       * @return DumperPrefixCollection
370       */
371      private function buildPrefixTree(DumperCollection $collection)
372      {
373          $tree = new DumperPrefixCollection();
374          $current = $tree;
375   
376          foreach ($collection as $route) {
377              $current = $current->addPrefixRoute($route);
378          }
379   
380          $tree->mergeSlashNodes();
381   
382          return $tree;
383      }
384  }
385