ApacheMatcherDumper.php 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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. * @author Fabien Potencier <fabien@symfony.com>
  16. * @author Kris Wallsmith <kris@symfony.com>
  17. */
  18. class ApacheMatcherDumper extends MatcherDumper
  19. {
  20. /**
  21. * Dumps a set of Apache mod_rewrite rules.
  22. *
  23. * Available options:
  24. *
  25. * * script_name: The script name (app.php by default)
  26. * * base_uri: The base URI ("" by default)
  27. *
  28. * @param array $options An array of options
  29. *
  30. * @return string A string to be used as Apache rewrite rules
  31. */
  32. public function dump(array $options = array())
  33. {
  34. $options = array_merge(array(
  35. 'script_name' => 'app.php',
  36. 'base_uri' => '',
  37. ), $options);
  38. $rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
  39. $methodVars = array();
  40. foreach ($this->getRoutes()->all() as $name => $route) {
  41. $compiledRoute = $route->compile();
  42. // prepare the apache regex
  43. $regex = preg_replace('/\?P<.+?>/', '', substr(str_replace(array("\n", ' '), '', $compiledRoute->getRegex()), 1, -2));
  44. $regex = '^'.preg_quote($options['base_uri']).substr($regex, 1);
  45. $hasTrailingSlash = '/$' == substr($regex, -2) && '^/$' != $regex;
  46. $variables = array('E=_ROUTING__route:'.$name);
  47. foreach ($compiledRoute->getVariables() as $i => $variable) {
  48. $variables[] = 'E=_ROUTING_'.$variable.':%'.($i + 1);
  49. }
  50. foreach ($route->getDefaults() as $key => $value) {
  51. // todo: a more legit way to escape the value?
  52. $variables[] = 'E=_ROUTING_'.$key.':'.strtr($value, array(
  53. ':' => '\\:',
  54. '=' => '\\=',
  55. '\\' => '\\\\',
  56. ));
  57. }
  58. $variables = implode(',', $variables);
  59. $rule = array("# $name");
  60. // method mismatch
  61. if ($req = $route->getRequirement('_method')) {
  62. $methods = explode('|', strtoupper($req));
  63. // GET and HEAD are equivalent
  64. if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
  65. $methods[] = 'HEAD';
  66. }
  67. $allow = array();
  68. foreach ($methods as $method) {
  69. $methodVars[] = $method;
  70. $allow[] = 'E=_ROUTING__allow_'.$method.':1';
  71. }
  72. $rule[] = "RewriteCond %{REQUEST_URI} $regex";
  73. $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
  74. $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
  75. }
  76. // redirect with trailing slash appended
  77. if ($hasTrailingSlash) {
  78. $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
  79. $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
  80. }
  81. // the main rule
  82. $rule[] = "RewriteCond %{REQUEST_URI} $regex";
  83. $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
  84. $rules[] = implode("\n", $rule);
  85. }
  86. if (0 < count($methodVars)) {
  87. $rule = array('# 405 Method Not Allowed');
  88. $methodVars = array_values(array_unique($methodVars));
  89. foreach ($methodVars as $i => $methodVar) {
  90. $rule[] = sprintf('RewriteCond %%{_ROUTING__allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
  91. }
  92. $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
  93. $rules[] = implode("\n", $rule);
  94. }
  95. return implode("\n\n", $rules)."\n";
  96. }
  97. }