PageRenderTime 42ms CodeModel.GetById 10ms RepoModel.GetById 1ms app.codeStats 0ms

/core/Associates/Symfony/Routes/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php

https://gitlab.com/fiesta-framework/Documentation
PHP | 281 lines | 221 code | 17 blank | 43 comment | 5 complexity | 699d0f5b724f28c6db769209fa5bd00e MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Routing\Matcher\Dumper;
  11. use Symfony\Component\Routing\Route;
  12. /**
  13. * Dumps a set of Apache mod_rewrite rules.
  14. *
  15. * @deprecated Deprecated since version 2.5, to be removed in 3.0.
  16. * The performance gains are minimal and it's very hard to replicate
  17. * the behavior of PHP implementation.
  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. $options['script_name'] = self::escape($options['script_name'], ' ', '\\');
  45. $rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
  46. $methodVars = array();
  47. $hostRegexUnique = 0;
  48. $prevHostRegex = '';
  49. foreach ($this->getRoutes()->all() as $name => $route) {
  50. if ($route->getCondition()) {
  51. throw new \LogicException(sprintf('Unable to dump the routes for Apache as route "%s" has a condition.', $name));
  52. }
  53. $compiledRoute = $route->compile();
  54. $hostRegex = $compiledRoute->getHostRegex();
  55. if (null !== $hostRegex && $prevHostRegex !== $hostRegex) {
  56. $prevHostRegex = $hostRegex;
  57. $hostRegexUnique++;
  58. $rule = array();
  59. $regex = $this->regexToApacheRegex($hostRegex);
  60. $regex = self::escape($regex, ' ', '\\');
  61. $rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
  62. $variables = array();
  63. $variables[] = sprintf('E=__ROUTING_host_%s:1', $hostRegexUnique);
  64. foreach ($compiledRoute->getHostVariables() as $i => $variable) {
  65. $variables[] = sprintf('E=__ROUTING_host_%s_%s:%%%d', $hostRegexUnique, $variable, $i+1);
  66. }
  67. $variables = implode(',', $variables);
  68. $rule[] = sprintf('RewriteRule .? - [%s]', $variables);
  69. $rules[] = implode("\n", $rule);
  70. }
  71. $rules[] = $this->dumpRoute($name, $route, $options, $hostRegexUnique);
  72. if ($req = $route->getRequirement('_method')) {
  73. $methods = explode('|', strtoupper($req));
  74. $methodVars = array_merge($methodVars, $methods);
  75. }
  76. }
  77. if (0 < count($methodVars)) {
  78. $rule = array('# 405 Method Not Allowed');
  79. $methodVars = array_values(array_unique($methodVars));
  80. if (in_array('GET', $methodVars) && !in_array('HEAD', $methodVars)) {
  81. $methodVars[] = 'HEAD';
  82. }
  83. foreach ($methodVars as $i => $methodVar) {
  84. $rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
  85. }
  86. $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
  87. $rules[] = implode("\n", $rule);
  88. }
  89. return implode("\n\n", $rules)."\n";
  90. }
  91. /**
  92. * Dumps a single route.
  93. *
  94. * @param string $name Route name
  95. * @param Route $route The route
  96. * @param array $options Options
  97. * @param bool $hostRegexUnique Unique identifier for the host regex
  98. *
  99. * @return string The compiled route
  100. */
  101. private function dumpRoute($name, $route, array $options, $hostRegexUnique)
  102. {
  103. $compiledRoute = $route->compile();
  104. // prepare the apache regex
  105. $regex = $this->regexToApacheRegex($compiledRoute->getRegex());
  106. $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\');
  107. $methods = $this->getRouteMethods($route);
  108. $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
  109. $variables = array('E=_ROUTING_route:'.$name);
  110. foreach ($compiledRoute->getHostVariables() as $variable) {
  111. $variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_host_%s_%s}', $variable, $hostRegexUnique, $variable);
  112. }
  113. foreach ($compiledRoute->getPathVariables() as $i => $variable) {
  114. $variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
  115. }
  116. foreach ($this->normalizeValues($route->getDefaults()) as $key => $value) {
  117. $variables[] = 'E=_ROUTING_default_'.$key.':'.strtr($value, array(
  118. ':' => '\\:',
  119. '=' => '\\=',
  120. '\\' => '\\\\',
  121. ' ' => '\\ ',
  122. ));
  123. }
  124. $variables = implode(',', $variables);
  125. $rule = array("# $name");
  126. // method mismatch
  127. if (0 < count($methods)) {
  128. $allow = array();
  129. foreach ($methods as $method) {
  130. $allow[] = 'E=_ROUTING_allow_'.$method.':1';
  131. }
  132. if ($compiledRoute->getHostRegex()) {
  133. $rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
  134. }
  135. $rule[] = "RewriteCond %{REQUEST_URI} $regex";
  136. $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
  137. $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
  138. }
  139. // redirect with trailing slash appended
  140. if ($hasTrailingSlash) {
  141. if ($compiledRoute->getHostRegex()) {
  142. $rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
  143. }
  144. $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
  145. $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
  146. }
  147. // the main rule
  148. if ($compiledRoute->getHostRegex()) {
  149. $rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_host_%s} =1", $hostRegexUnique);
  150. }
  151. $rule[] = "RewriteCond %{REQUEST_URI} $regex";
  152. $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
  153. return implode("\n", $rule);
  154. }
  155. /**
  156. * Returns methods allowed for a route.
  157. *
  158. * @param Route $route The route
  159. *
  160. * @return array The methods
  161. */
  162. private function getRouteMethods(Route $route)
  163. {
  164. $methods = array();
  165. if ($req = $route->getRequirement('_method')) {
  166. $methods = explode('|', strtoupper($req));
  167. // GET and HEAD are equivalent
  168. if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
  169. $methods[] = 'HEAD';
  170. }
  171. }
  172. return $methods;
  173. }
  174. /**
  175. * Converts a regex to make it suitable for mod_rewrite.
  176. *
  177. * @param string $regex The regex
  178. *
  179. * @return string The converted regex
  180. */
  181. private function regexToApacheRegex($regex)
  182. {
  183. $regexPatternEnd = strrpos($regex, $regex[0]);
  184. return preg_replace('/\?P<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
  185. }
  186. /**
  187. * Escapes a string.
  188. *
  189. * @param string $string The string to be escaped
  190. * @param string $char The character to be escaped
  191. * @param string $with The character to be used for escaping
  192. *
  193. * @return string The escaped string
  194. */
  195. private static function escape($string, $char, $with)
  196. {
  197. $escaped = false;
  198. $output = '';
  199. foreach (str_split($string) as $symbol) {
  200. if ($escaped) {
  201. $output .= $symbol;
  202. $escaped = false;
  203. continue;
  204. }
  205. if ($symbol === $char) {
  206. $output .= $with.$char;
  207. continue;
  208. }
  209. if ($symbol === $with) {
  210. $escaped = true;
  211. }
  212. $output .= $symbol;
  213. }
  214. return $output;
  215. }
  216. /**
  217. * Normalizes an array of values.
  218. *
  219. * @param array $values
  220. *
  221. * @return string[]
  222. */
  223. private function normalizeValues(array $values)
  224. {
  225. $normalizedValues = array();
  226. foreach ($values as $key => $value) {
  227. if (is_array($value)) {
  228. foreach ($value as $index => $bit) {
  229. $normalizedValues[sprintf('%s[%s]', $key, $index)] = $bit;
  230. }
  231. } else {
  232. $normalizedValues[$key] = (string) $value;
  233. }
  234. }
  235. return $normalizedValues;
  236. }
  237. }