[ Index ] |
PHP Cross Reference of phpBB-3.1.12-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\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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |