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