1eb51852a07d350a29ba30413bcd85fbd31f5472
[yaffs-website] / vendor / symfony / routing / Matcher / Dumper / ApacheMatcherDumper.php
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 @trigger_error('The '.__NAMESPACE__.'\ApacheMatcherDumper class is deprecated since version 2.5 and will be removed in 3.0. It\'s hard to replicate the behaviour of the PHP implementation and the performance gains are minimal.', E_USER_DEPRECATED);
15
16 use Symfony\Component\Routing\Route;
17
18 /**
19  * Dumps a set of Apache mod_rewrite rules.
20  *
21  * @deprecated since version 2.5, to be removed in 3.0.
22  *             The performance gains are minimal and it's very hard to replicate
23  *             the behavior of PHP implementation.
24  *
25  * @author Fabien Potencier <fabien@symfony.com>
26  * @author Kris Wallsmith <kris@symfony.com>
27  */
28 class ApacheMatcherDumper extends MatcherDumper
29 {
30     /**
31      * Dumps a set of Apache mod_rewrite rules.
32      *
33      * Available options:
34      *
35      *  * script_name: The script name (app.php by default)
36      *  * base_uri:    The base URI ("" by default)
37      *
38      * @param array $options An array of options
39      *
40      * @return string A string to be used as Apache rewrite rules
41      *
42      * @throws \LogicException When the route regex is invalid
43      */
44     public function dump(array $options = array())
45     {
46         $options = array_merge(array(
47             'script_name' => 'app.php',
48             'base_uri' => '',
49         ), $options);
50
51         $options['script_name'] = self::escape($options['script_name'], ' ', '\\');
52
53         $rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
54         $methodVars = array();
55         $hostRegexUnique = 0;
56         $prevHostRegex = '';
57
58         foreach ($this->getRoutes()->all() as $name => $route) {
59             if ($route->getCondition()) {
60                 throw new \LogicException(sprintf('Unable to dump the routes for Apache as route "%s" has a condition.', $name));
61             }
62
63             $compiledRoute = $route->compile();
64             $hostRegex = $compiledRoute->getHostRegex();
65
66             if (null !== $hostRegex && $prevHostRegex !== $hostRegex) {
67                 $prevHostRegex = $hostRegex;
68                 ++$hostRegexUnique;
69
70                 $rule = array();
71
72                 $regex = $this->regexToApacheRegex($hostRegex);
73                 $regex = self::escape($regex, ' ', '\\');
74
75                 $rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
76
77                 $variables = array();
78                 $variables[] = sprintf('E=__ROUTING_host_%s:1', $hostRegexUnique);
79
80                 foreach ($compiledRoute->getHostVariables() as $i => $variable) {
81                     $variables[] = sprintf('E=__ROUTING_host_%s_%s:%%%d', $hostRegexUnique, $variable, $i + 1);
82                 }
83
84                 $variables = implode(',', $variables);
85
86                 $rule[] = sprintf('RewriteRule .? - [%s]', $variables);
87
88                 $rules[] = implode("\n", $rule);
89             }
90
91             $rules[] = $this->dumpRoute($name, $route, $options, $hostRegexUnique);
92
93             $methodVars = array_merge($methodVars, $route->getMethods());
94         }
95         if (0 < count($methodVars)) {
96             $rule = array('# 405 Method Not Allowed');
97             $methodVars = array_values(array_unique($methodVars));
98             if (in_array('GET', $methodVars) && !in_array('HEAD', $methodVars)) {
99                 $methodVars[] = 'HEAD';
100             }
101             foreach ($methodVars as $i => $methodVar) {
102                 $rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
103             }
104             $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
105
106             $rules[] = implode("\n", $rule);
107         }
108
109         return implode("\n\n", $rules)."\n";
110     }
111
112     /**
113      * Dumps a single route.
114      *
115      * @param string $name            Route name
116      * @param Route  $route           The route
117      * @param array  $options         Options
118      * @param bool   $hostRegexUnique Unique identifier for the host regex
119      *
120      * @return string The compiled route
121      */
122     private function dumpRoute($name, $route, array $options, $hostRegexUnique)
123     {
124         $compiledRoute = $route->compile();
125
126         // prepare the apache regex
127         $regex = $this->regexToApacheRegex($compiledRoute->getRegex());
128         $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\');
129
130         $methods = $this->getRouteMethods($route);
131
132         $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
133
134         $variables = array('E=_ROUTING_route:'.$name);
135         foreach ($compiledRoute->getHostVariables() as $variable) {
136             $variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_host_%s_%s}', $variable, $hostRegexUnique, $variable);
137         }
138         foreach ($compiledRoute->getPathVariables() as $i => $variable) {
139             $variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
140         }
141         foreach ($this->normalizeValues($route->getDefaults()) as $key => $value) {
142             $variables[] = 'E=_ROUTING_default_'.$key.':'.strtr($value, array(
143                 ':' => '\\:',
144                 '=' => '\\=',
145                 '\\' => '\\\\',
146                 ' ' => '\\ ',
147             ));
148         }
149         $variables = implode(',', $variables);
150
151         $rule = array("# $name");
152
153         // method mismatch
154         if (0 < count($methods)) {
155             $allow = array();
156             foreach ($methods as $method) {
157                 $allow[] = 'E=_ROUTING_allow_'.$method.':1';
158             }
159
160             if ($compiledRoute->getHostRegex()) {
161                 $rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
162             }
163
164             $rule[] = "RewriteCond %{REQUEST_URI} $regex";
165             $rule[] = sprintf('RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]', implode('|', $methods));
166             $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
167         }
168
169         // redirect with trailing slash appended
170         if ($hasTrailingSlash) {
171             if ($compiledRoute->getHostRegex()) {
172                 $rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
173             }
174
175             $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
176             $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
177         }
178
179         // the main rule
180
181         if ($compiledRoute->getHostRegex()) {
182             $rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
183         }
184
185         $rule[] = "RewriteCond %{REQUEST_URI} $regex";
186         $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
187
188         return implode("\n", $rule);
189     }
190
191     /**
192      * Returns methods allowed for a route.
193      *
194      * @param Route $route The route
195      *
196      * @return array The methods
197      */
198     private function getRouteMethods(Route $route)
199     {
200         $methods = $route->getMethods();
201
202         // GET and HEAD are equivalent
203         if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
204             $methods[] = 'HEAD';
205         }
206
207         return $methods;
208     }
209
210     /**
211      * Converts a regex to make it suitable for mod_rewrite.
212      *
213      * @param string $regex The regex
214      *
215      * @return string The converted regex
216      */
217     private function regexToApacheRegex($regex)
218     {
219         $regexPatternEnd = strrpos($regex, $regex[0]);
220
221         return preg_replace('/\?P<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
222     }
223
224     /**
225      * Escapes a string.
226      *
227      * @param string $string The string to be escaped
228      * @param string $char   The character to be escaped
229      * @param string $with   The character to be used for escaping
230      *
231      * @return string The escaped string
232      */
233     private static function escape($string, $char, $with)
234     {
235         $escaped = false;
236         $output = '';
237         foreach (str_split($string) as $symbol) {
238             if ($escaped) {
239                 $output .= $symbol;
240                 $escaped = false;
241                 continue;
242             }
243             if ($symbol === $char) {
244                 $output .= $with.$char;
245                 continue;
246             }
247             if ($symbol === $with) {
248                 $escaped = true;
249             }
250             $output .= $symbol;
251         }
252
253         return $output;
254     }
255
256     /**
257      * Normalizes an array of values.
258      *
259      * @param array $values
260      *
261      * @return string[]
262      */
263     private function normalizeValues(array $values)
264     {
265         $normalizedValues = array();
266         foreach ($values as $key => $value) {
267             if (is_array($value)) {
268                 foreach ($value as $index => $bit) {
269                     $normalizedValues[sprintf('%s[%s]', $key, $index)] = $bit;
270                 }
271             } else {
272                 $normalizedValues[$key] = (string) $value;
273             }
274         }
275
276         return $normalizedValues;
277     }
278 }