|
11 | 11 |
|
12 | 12 | namespace Symfony\Component\Routing\Matcher\Dumper;
|
13 | 13 |
|
| 14 | +use Symfony\Component\Routing\Route; |
| 15 | + |
14 | 16 | /**
|
15 | 17 | * Dumps a set of Apache mod_rewrite rules.
|
16 | 18 | *
|
@@ -46,88 +48,116 @@ public function dump(array $options = array())
|
46 | 48 | $methodVars = array();
|
47 | 49 |
|
48 | 50 | foreach ($this->getRoutes()->all() as $name => $route) {
|
49 |
| - $compiledRoute = $route->compile(); |
50 |
| - |
51 |
| - // prepare the apache regex |
52 |
| - $regex = $compiledRoute->getRegex(); |
53 |
| - $delimiter = $regex[0]; |
54 |
| - $regexPatternEnd = strrpos($regex, $delimiter); |
55 |
| - if (strlen($regex) < 2 || 0 === $regexPatternEnd) { |
56 |
| - throw new \LogicException('The "%s" route regex "%s" is invalid', $name, $regex); |
57 |
| - } |
58 |
| - $regex = preg_replace('/\?<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1)); |
59 |
| - $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\'); |
60 |
| - |
61 |
| - $methods = array(); |
62 |
| - if ($req = $route->getRequirement('_method')) { |
63 |
| - $methods = explode('|', strtoupper($req)); |
64 |
| - // GET and HEAD are equivalent |
65 |
| - if (in_array('GET', $methods) && !in_array('HEAD', $methods)) { |
66 |
| - $methods[] = 'HEAD'; |
67 |
| - } |
| 51 | + $rules[] = $this->dumpRoute($name, $route, $options); |
| 52 | + $methodVars = array_merge($methodVars, $this->getRouteMethods($route)); |
| 53 | + } |
| 54 | + |
| 55 | + if (0 < count($methodVars)) { |
| 56 | + $rule = array('# 405 Method Not Allowed'); |
| 57 | + $methodVars = array_values(array_unique($methodVars)); |
| 58 | + foreach ($methodVars as $i => $methodVar) { |
| 59 | + $rule[] = sprintf('RewriteCond %%{_ROUTING_allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : ''); |
68 | 60 | }
|
| 61 | + $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']); |
69 | 62 |
|
70 |
| - $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex; |
| 63 | + $rules[] = implode("\n", $rule); |
| 64 | + } |
71 | 65 |
|
72 |
| - $variables = array('E=_ROUTING__route:'.$name); |
73 |
| - foreach ($compiledRoute->getVariables() as $i => $variable) { |
74 |
| - $variables[] = 'E=_ROUTING_'.$variable.':%'.($i + 1); |
75 |
| - } |
76 |
| - foreach ($route->getDefaults() as $key => $value) { |
77 |
| - $variables[] = 'E=_ROUTING_DEFAULTS_'.$key.':'.strtr($value, array( |
78 |
| - ':' => '\\:', |
79 |
| - '=' => '\\=', |
80 |
| - '\\' => '\\\\', |
81 |
| - ' ' => '\\ ', |
82 |
| - )); |
83 |
| - } |
84 |
| - $variables = implode(',', $variables); |
85 |
| - |
86 |
| - $rule = array("# $name"); |
87 |
| - |
88 |
| - // method mismatch |
89 |
| - if ($req = $route->getRequirement('_method')) { |
90 |
| - $methods = explode('|', strtoupper($req)); |
91 |
| - // GET and HEAD are equivalent |
92 |
| - if (in_array('GET', $methods) && !in_array('HEAD', $methods)) { |
93 |
| - $methods[] = 'HEAD'; |
94 |
| - } |
95 |
| - $allow = array(); |
96 |
| - foreach ($methods as $method) { |
97 |
| - $methodVars[] = $method; |
98 |
| - $allow[] = 'E=_ROUTING__allow_'.$method.':1'; |
99 |
| - } |
100 |
| - |
101 |
| - $rule[] = "RewriteCond %{REQUEST_URI} $regex"; |
102 |
| - $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods)); |
103 |
| - $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow)); |
104 |
| - } |
| 66 | + return implode("\n\n", $rules)."\n"; |
| 67 | + } |
| 68 | + |
| 69 | + private function dumpRoute($name, $route, array $options) |
| 70 | + { |
| 71 | + $compiledRoute = $route->compile(); |
| 72 | + |
| 73 | + // prepare the apache regex |
| 74 | + $regex = $this->regexToApacheRegex($compiledRoute->getRegex()); |
| 75 | + $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\'); |
| 76 | + |
| 77 | + $methods = $this->getRouteMethods($route); |
| 78 | + |
| 79 | + $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex; |
| 80 | + |
| 81 | + $variables = array('E=_ROUTING_route:'.$name); |
| 82 | + foreach ($compiledRoute->getVariables() as $i => $variable) { |
| 83 | + $variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1); |
| 84 | + } |
| 85 | + foreach ($route->getDefaults() as $key => $value) { |
| 86 | + $variables[] = 'E=_ROUTING_default_'.$key.':'.strtr($value, array( |
| 87 | + ':' => '\\:', |
| 88 | + '=' => '\\=', |
| 89 | + '\\' => '\\\\', |
| 90 | + ' ' => '\\ ', |
| 91 | + )); |
| 92 | + } |
| 93 | + $variables = implode(',', $variables); |
| 94 | + |
| 95 | + $rule = array("# $name"); |
105 | 96 |
|
106 |
| - // redirect with trailing slash appended |
107 |
| - if ($hasTrailingSlash) { |
108 |
| - $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$'; |
109 |
| - $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]'; |
| 97 | + // method mismatch |
| 98 | + if (0 < count($methods)) { |
| 99 | + $allow = array(); |
| 100 | + foreach ($methods as $method) { |
| 101 | + $methodVars[] = $method; |
| 102 | + $allow[] = 'E=_ROUTING_allow_'.$method.':1'; |
110 | 103 | }
|
111 | 104 |
|
112 |
| - // the main rule |
113 | 105 | $rule[] = "RewriteCond %{REQUEST_URI} $regex";
|
114 |
| - $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]"; |
| 106 | + $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods)); |
| 107 | + $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow)); |
| 108 | + } |
115 | 109 |
|
116 |
| - $rules[] = implode("\n", $rule); |
| 110 | + // redirect with trailing slash appended |
| 111 | + if ($hasTrailingSlash) { |
| 112 | + $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$'; |
| 113 | + $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]'; |
117 | 114 | }
|
118 | 115 |
|
119 |
| - if (0 < count($methodVars)) { |
120 |
| - $rule = array('# 405 Method Not Allowed'); |
121 |
| - $methodVars = array_values(array_unique($methodVars)); |
122 |
| - foreach ($methodVars as $i => $methodVar) { |
123 |
| - $rule[] = sprintf('RewriteCond %%{_ROUTING__allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : ''); |
| 116 | + // the main rule |
| 117 | + $rule[] = "RewriteCond %{REQUEST_URI} $regex"; |
| 118 | + $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]"; |
| 119 | + |
| 120 | + return implode("\n", $rule); |
| 121 | + } |
| 122 | + |
| 123 | + /** |
| 124 | + * Returns methods allowed for a route |
| 125 | + * |
| 126 | + * @param Route $route The route |
| 127 | + * |
| 128 | + * @return array The methods |
| 129 | + */ |
| 130 | + private function getRouteMethods(Route $route) |
| 131 | + { |
| 132 | + $methods = array(); |
| 133 | + if ($req = $route->getRequirement('_method')) { |
| 134 | + $methods = explode('|', strtoupper($req)); |
| 135 | + // GET and HEAD are equivalent |
| 136 | + if (in_array('GET', $methods) && !in_array('HEAD', $methods)) { |
| 137 | + $methods[] = 'HEAD'; |
124 | 138 | }
|
125 |
| - $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']); |
| 139 | + } |
126 | 140 |
|
127 |
| - $rules[] = implode("\n", $rule); |
| 141 | + return $methods; |
| 142 | + } |
| 143 | + |
| 144 | + /** |
| 145 | + * Converts a regex to make it suitable for mod_rewrite |
| 146 | + * |
| 147 | + * @param string $regex The regex |
| 148 | + * |
| 149 | + * @return string The converted regex |
| 150 | + */ |
| 151 | + private function regexToApacheRegex($regex) |
| 152 | + { |
| 153 | + $delimiter = $regex[0]; |
| 154 | + $regexPatternEnd = strrpos($regex, $delimiter); |
| 155 | + if (strlen($regex) < 2 || 0 === $regexPatternEnd) { |
| 156 | + throw new \LogicException('The "%s" route regex "%s" is invalid', $name, $regex); |
128 | 157 | }
|
| 158 | + $regex = preg_replace('/\?<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1)); |
129 | 159 |
|
130 |
| - return implode("\n\n", $rules)."\n"; |
| 160 | + return $regex; |
131 | 161 | }
|
132 | 162 |
|
133 | 163 | /**
|
|
0 commit comments