class ApacheMatcherDumper

Dumps a set of Apache mod_rewrite rules.

@author Fabien Potencier <fabien@symfony.com> @author Kris Wallsmith <kris@symfony.com>

Hierarchy

Expanded class hierarchy of ApacheMatcherDumper

1 file declares its use of ApacheMatcherDumper
ApacheMatcherDumperTest.php in drupal/core/vendor/symfony/routing/Symfony/Component/Routing/Tests/Matcher/Dumper/ApacheMatcherDumperTest.php

File

drupal/core/vendor/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php, line 20

Namespace

Symfony\Component\Routing\Matcher\Dumper
View source
class ApacheMatcherDumper extends MatcherDumper {

  /**
   * Dumps a set of Apache mod_rewrite rules.
   *
   * Available options:
   *
   *  * script_name: The script name (app.php by default)
   *  * base_uri:    The base URI ("" by default)
   *
   * @param array $options An array of options
   *
   * @return string A string to be used as Apache rewrite rules
   *
   * @throws \LogicException When the route regex is invalid
   */
  public function dump(array $options = array()) {
    $options = array_merge(array(
      'script_name' => 'app.php',
      'base_uri' => '',
    ), $options);
    $options['script_name'] = self::escape($options['script_name'], ' ', '\\');
    $rules = array(
      "# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]",
    );
    $methodVars = array();
    foreach ($this
      ->getRoutes()
      ->all() as $name => $route) {
      $compiledRoute = $route
        ->compile();

      // prepare the apache regex
      $regex = $compiledRoute
        ->getRegex();
      $delimiter = $regex[0];
      $regexPatternEnd = strrpos($regex, $delimiter);
      if (strlen($regex) < 2 || 0 === $regexPatternEnd) {
        throw new \LogicException('The "%s" route regex "%s" is invalid', $name, $regex);
      }
      $regex = preg_replace('/\\?<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
      $regex = '^' . self::escape(preg_quote($options['base_uri']) . substr($regex, 1), ' ', '\\');
      $methods = array();
      if ($req = $route
        ->getRequirement('_method')) {
        $methods = explode('|', strtoupper($req));

        // GET and HEAD are equivalent
        if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
          $methods[] = 'HEAD';
        }
      }
      $hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
      $variables = array(
        'E=_ROUTING__route:' . $name,
      );
      foreach ($compiledRoute
        ->getVariables() as $i => $variable) {
        $variables[] = 'E=_ROUTING_' . $variable . ':%' . ($i + 1);
      }
      foreach ($route
        ->getDefaults() as $key => $value) {
        $variables[] = 'E=_ROUTING_DEFAULTS_' . $key . ':' . strtr($value, array(
          ':' => '\\:',
          '=' => '\\=',
          '\\' => '\\\\',
          ' ' => '\\ ',
        ));
      }
      $variables = implode(',', $variables);
      $rule = array(
        "# {$name}",
      );

      // method mismatch
      if ($req = $route
        ->getRequirement('_method')) {
        $methods = explode('|', strtoupper($req));

        // GET and HEAD are equivalent
        if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
          $methods[] = 'HEAD';
        }
        $allow = array();
        foreach ($methods as $method) {
          $methodVars[] = $method;
          $allow[] = 'E=_ROUTING__allow_' . $method . ':1';
        }
        $rule[] = "RewriteCond %{REQUEST_URI} {$regex}";
        $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)\$ [NC]", implode('|', $methods));
        $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
      }

      // redirect with trailing slash appended
      if ($hasTrailingSlash) {
        $rule[] = 'RewriteCond %{REQUEST_URI} ' . substr($regex, 0, -2) . '$';
        $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
      }

      // the main rule
      $rule[] = "RewriteCond %{REQUEST_URI} {$regex}";
      $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,{$variables}]";
      $rules[] = implode("\n", $rule);
    }
    if (0 < count($methodVars)) {
      $rule = array(
        '# 405 Method Not Allowed',
      );
      $methodVars = array_values(array_unique($methodVars));
      foreach ($methodVars as $i => $methodVar) {
        $rule[] = sprintf('RewriteCond %%{_ROUTING__allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
      }
      $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
      $rules[] = implode("\n", $rule);
    }
    return implode("\n\n", $rules) . "\n";
  }

  /**
   * Escapes a string.
   *
   * @param string $string The string to be escaped
   * @param string $char   The character to be escaped
   * @param string $with   The character to be used for escaping
   *
   * @return string The escaped string
   */
  private static function escape($string, $char, $with) {
    $escaped = false;
    $output = '';
    foreach (str_split($string) as $symbol) {
      if ($escaped) {
        $output .= $symbol;
        $escaped = false;
        continue;
      }
      if ($symbol === $char) {
        $output .= $with . $char;
        continue;
      }
      if ($symbol === $with) {
        $escaped = true;
      }
      $output .= $symbol;
    }
    return $output;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ApacheMatcherDumper::dump public function Dumps a set of Apache mod_rewrite rules. Overrides MatcherDumperInterface::dump
ApacheMatcherDumper::escape private static function Escapes a string.
MatcherDumper::$routes private property
MatcherDumper::getRoutes public function Gets the routes to dump. Overrides MatcherDumperInterface::getRoutes
MatcherDumper::__construct public function Constructor.