]> git.immae.eu Git - github/wallabag/wallabag.git/blob - vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php
twig implementation
[github/wallabag/wallabag.git] / vendor / symfony / routing / Symfony / Component / 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 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 }