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. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
PhpMatcherDumper.php
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