[ Index ] |
PHP Cross Reference of phpBB-3.3.14-deutsch |
[Summary view] [Print] [Text view]
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\ExpressionLanguage\ExpressionFunctionProviderInterface; 15 use Symfony\Component\ExpressionLanguage\ExpressionLanguage; 16 use Symfony\Component\Routing\Route; 17 use Symfony\Component\Routing\RouteCollection; 18 19 /** 20 * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes. 21 * 22 * @author Fabien Potencier <fabien@symfony.com> 23 * @author Tobias Schultze <http://tobion.de> 24 * @author Arnaud Le Blanc <arnaud.lb@gmail.com> 25 */ 26 class PhpMatcherDumper extends MatcherDumper 27 { 28 private $expressionLanguage; 29 30 /** 31 * @var ExpressionFunctionProviderInterface[] 32 */ 33 private $expressionLanguageProviders = []; 34 35 /** 36 * Dumps a set of routes to a PHP class. 37 * 38 * Available options: 39 * 40 * * class: The class name 41 * * base_class: The base class name 42 * 43 * @param array $options An array of options 44 * 45 * @return string A PHP class representing the matcher class 46 */ 47 public function dump(array $options = []) 48 { 49 $options = array_replace([ 50 'class' => 'ProjectUrlMatcher', 51 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', 52 ], $options); 53 54 // trailing slash support is only enabled if we know how to redirect the user 55 $interfaces = class_implements($options['base_class']); 56 $supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']); 57 58 return <<<EOF 59 <?php 60 61 use Symfony\Component\Routing\Exception\MethodNotAllowedException; 62 use Symfony\Component\Routing\Exception\ResourceNotFoundException; 63 use Symfony\Component\Routing\RequestContext; 64 65 /** 66 * This class has been auto-generated 67 * by the Symfony Routing Component. 68 */ 69 class {$options['class']} extends {$options['base_class']} 70 { 71 public function __construct(RequestContext \$context) 72 { 73 \$this->context = \$context; 74 } 75 76 {$this->generateMatchMethod($supportsRedirections)} 77 } 78 79 EOF; 80 } 81 82 public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) 83 { 84 $this->expressionLanguageProviders[] = $provider; 85 } 86 87 /** 88 * Generates the code for the match method implementing UrlMatcherInterface. 89 * 90 * @param bool $supportsRedirections Whether redirections are supported by the base class 91 * 92 * @return string Match method as PHP code 93 */ 94 private function generateMatchMethod($supportsRedirections) 95 { 96 $code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n"); 97 98 return <<<EOF 99 public function match(\$rawPathinfo) 100 { 101 \$allow = []; 102 \$pathinfo = rawurldecode(\$rawPathinfo); 103 \$trimmedPathinfo = rtrim(\$pathinfo, '/'); 104 \$context = \$this->context; 105 \$request = \$this->request ?: \$this->createRequest(\$pathinfo); 106 \$requestMethod = \$canonicalMethod = \$context->getMethod(); 107 108 if ('HEAD' === \$requestMethod) { 109 \$canonicalMethod = 'GET'; 110 } 111 112 $code 113 114 throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException(); 115 } 116 EOF; 117 } 118 119 /** 120 * Generates PHP code to match a RouteCollection with all its routes. 121 * 122 * @param RouteCollection $routes A RouteCollection instance 123 * @param bool $supportsRedirections Whether redirections are supported by the base class 124 * 125 * @return string PHP code 126 */ 127 private function compileRoutes(RouteCollection $routes, $supportsRedirections) 128 { 129 $fetchedHost = false; 130 $groups = $this->groupRoutesByHostRegex($routes); 131 $code = ''; 132 133 foreach ($groups as $collection) { 134 if (null !== $regex = $collection->getAttribute('host_regex')) { 135 if (!$fetchedHost) { 136 $code .= " \$host = \$context->getHost();\n\n"; 137 $fetchedHost = true; 138 } 139 140 $code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true)); 141 } 142 143 $tree = $this->buildStaticPrefixCollection($collection); 144 $groupCode = $this->compileStaticPrefixRoutes($tree, $supportsRedirections); 145 146 if (null !== $regex) { 147 // apply extra indention at each line (except empty ones) 148 $groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode); 149 $code .= $groupCode; 150 $code .= " }\n\n"; 151 } else { 152 $code .= $groupCode; 153 } 154 } 155 156 // used to display the Welcome Page in apps that don't define a homepage 157 $code .= " if ('/' === \$pathinfo && !\$allow) {\n"; 158 $code .= " throw new Symfony\Component\Routing\Exception\NoConfigurationException();\n"; 159 $code .= " }\n"; 160 161 return $code; 162 } 163 164 private function buildStaticPrefixCollection(DumperCollection $collection) 165 { 166 $prefixCollection = new StaticPrefixCollection(); 167 168 foreach ($collection as $dumperRoute) { 169 $prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix(); 170 $prefixCollection->addRoute($prefix, $dumperRoute); 171 } 172 173 $prefixCollection->optimizeGroups(); 174 175 return $prefixCollection; 176 } 177 178 /** 179 * Generates PHP code to match a tree of routes. 180 * 181 * @param StaticPrefixCollection $collection A StaticPrefixCollection instance 182 * @param bool $supportsRedirections Whether redirections are supported by the base class 183 * @param string $ifOrElseIf either "if" or "elseif" to influence chaining 184 * 185 * @return string PHP code 186 */ 187 private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if') 188 { 189 $code = ''; 190 $prefix = $collection->getPrefix(); 191 192 if (!empty($prefix) && '/' !== $prefix) { 193 $code .= sprintf(" %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true)); 194 } 195 196 $ifOrElseIf = 'if'; 197 198 foreach ($collection->getItems() as $route) { 199 if ($route instanceof StaticPrefixCollection) { 200 $code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf); 201 $ifOrElseIf = 'elseif'; 202 } else { 203 $code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n"; 204 $ifOrElseIf = 'if'; 205 } 206 } 207 208 if (!empty($prefix) && '/' !== $prefix) { 209 $code .= " }\n\n"; 210 // apply extra indention at each line (except empty ones) 211 $code = preg_replace('/^.{2,}$/m', ' $0', $code); 212 } 213 214 return $code; 215 } 216 217 /** 218 * Compiles a single Route to PHP code used to match it against the path info. 219 * 220 * @param Route $route A Route instance 221 * @param string $name The name of the Route 222 * @param bool $supportsRedirections Whether redirections are supported by the base class 223 * @param string|null $parentPrefix The prefix of the parent collection used to optimize the code 224 * 225 * @return string PHP code 226 * 227 * @throws \LogicException 228 */ 229 private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null) 230 { 231 $code = ''; 232 $compiledRoute = $route->compile(); 233 $conditions = []; 234 $hasTrailingSlash = false; 235 $matches = false; 236 $hostMatches = false; 237 $methods = $route->getMethods(); 238 239 $supportsTrailingSlash = $supportsRedirections && (!$methods || \in_array('GET', $methods)); 240 $regex = $compiledRoute->getRegex(); 241 242 if (!\count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#'.('u' === substr($regex, -1) ? 'u' : ''), $regex, $m)) { 243 if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) { 244 $conditions[] = sprintf('%s === $trimmedPathinfo', var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); 245 $hasTrailingSlash = true; 246 } else { 247 $conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true)); 248 } 249 } else { 250 if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) { 251 $conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true)); 252 } 253 254 if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) { 255 $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2); 256 $hasTrailingSlash = true; 257 } 258 $conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true)); 259 260 $matches = true; 261 } 262 263 if ($compiledRoute->getHostVariables()) { 264 $hostMatches = true; 265 } 266 267 if ($route->getCondition()) { 268 $conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), ['context', 'request']); 269 } 270 271 $conditions = implode(' && ', $conditions); 272 273 $code .= <<<EOF 274 // $name 275 if ($conditions) { 276 277 EOF; 278 279 $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); 280 281 // the offset where the return value is appended below, with indentation 282 $retOffset = 12 + \strlen($code); 283 284 // optimize parameters array 285 if ($matches || $hostMatches) { 286 $vars = []; 287 if ($hostMatches) { 288 $vars[] = '$hostMatches'; 289 } 290 if ($matches) { 291 $vars[] = '$matches'; 292 } 293 $vars[] = "['_route' => '$name']"; 294 295 $code .= sprintf( 296 " \$ret = \$this->mergeDefaults(array_replace(%s), %s);\n", 297 implode(', ', $vars), 298 str_replace("\n", '', var_export($route->getDefaults(), true)) 299 ); 300 } elseif ($route->getDefaults()) { 301 $code .= sprintf(" \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), ['_route' => $name]), true))); 302 } else { 303 $code .= sprintf(" \$ret = ['_route' => '%s'];\n", $name); 304 } 305 306 if ($hasTrailingSlash) { 307 $code .= <<<EOF 308 if ('/' === substr(\$pathinfo, -1)) { 309 // no-op 310 } elseif ('GET' !== \$canonicalMethod) { 311 goto $gotoname; 312 } else { 313 return array_replace(\$ret, \$this->redirect(\$rawPathinfo.'/', '$name')); 314 } 315 316 317 EOF; 318 } 319 320 if ($methods) { 321 $methodVariable = \in_array('GET', $methods) ? '$canonicalMethod' : '$requestMethod'; 322 $methods = implode("', '", $methods); 323 } 324 325 if ($schemes = $route->getSchemes()) { 326 if (!$supportsRedirections) { 327 throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); 328 } 329 $schemes = str_replace("\n", '', var_export(array_flip($schemes), true)); 330 if ($methods) { 331 $code .= <<<EOF 332 \$requiredSchemes = $schemes; 333 \$hasRequiredScheme = isset(\$requiredSchemes[\$context->getScheme()]); 334 if (!in_array($methodVariable, ['$methods'])) { 335 if (\$hasRequiredScheme) { 336 \$allow = array_merge(\$allow, ['$methods']); 337 } 338 goto $gotoname; 339 } 340 if (!\$hasRequiredScheme) { 341 if ('GET' !== \$canonicalMethod) { 342 goto $gotoname; 343 } 344 345 return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes))); 346 } 347 348 349 EOF; 350 } else { 351 $code .= <<<EOF 352 \$requiredSchemes = $schemes; 353 if (!isset(\$requiredSchemes[\$context->getScheme()])) { 354 if ('GET' !== \$canonicalMethod) { 355 goto $gotoname; 356 } 357 358 return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes))); 359 } 360 361 362 EOF; 363 } 364 } elseif ($methods) { 365 $code .= <<<EOF 366 if (!in_array($methodVariable, ['$methods'])) { 367 \$allow = array_merge(\$allow, ['$methods']); 368 goto $gotoname; 369 } 370 371 372 EOF; 373 } 374 375 if ($hasTrailingSlash || $schemes || $methods) { 376 $code .= " return \$ret;\n"; 377 } else { 378 $code = substr_replace($code, 'return', $retOffset, 6); 379 } 380 $code .= " }\n"; 381 382 if ($hasTrailingSlash || $schemes || $methods) { 383 $code .= " $gotoname:\n"; 384 } 385 386 return $code; 387 } 388 389 /** 390 * Groups consecutive routes having the same host regex. 391 * 392 * The result is a collection of collections of routes having the same host regex. 393 * 394 * @param RouteCollection $routes A flat RouteCollection 395 * 396 * @return DumperCollection A collection with routes grouped by host regex in sub-collections 397 */ 398 private function groupRoutesByHostRegex(RouteCollection $routes) 399 { 400 $groups = new DumperCollection(); 401 $currentGroup = new DumperCollection(); 402 $currentGroup->setAttribute('host_regex', null); 403 $groups->add($currentGroup); 404 405 foreach ($routes as $name => $route) { 406 $hostRegex = $route->compile()->getHostRegex(); 407 if ($currentGroup->getAttribute('host_regex') !== $hostRegex) { 408 $currentGroup = new DumperCollection(); 409 $currentGroup->setAttribute('host_regex', $hostRegex); 410 $groups->add($currentGroup); 411 } 412 $currentGroup->add(new DumperRoute($name, $route)); 413 } 414 415 return $groups; 416 } 417 418 private function getExpressionLanguage() 419 { 420 if (null === $this->expressionLanguage) { 421 if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) { 422 throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.'); 423 } 424 $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); 425 } 426 427 return $this->expressionLanguage; 428 } 429 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Mon Nov 25 19:05:08 2024 | Cross-referenced by PHPXref 0.7.1 |