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. |
|
(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
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