class ApacheMatcherDumper in Zircon Profile 8
Same name and namespace in other branches
- 8.0 vendor/symfony/routing/Matcher/Dumper/ApacheMatcherDumper.php \Symfony\Component\Routing\Matcher\Dumper\ApacheMatcherDumper
Dumps a set of Apache mod_rewrite rules.
@author Fabien Potencier <fabien@symfony.com> @author Kris Wallsmith <kris@symfony.com>
Hierarchy
- class \Symfony\Component\Routing\Matcher\Dumper\MatcherDumper implements MatcherDumperInterface
- class \Symfony\Component\Routing\Matcher\Dumper\ApacheMatcherDumper
Expanded class hierarchy of ApacheMatcherDumper
Deprecated
since version 2.5, to be removed in 3.0. The performance gains are minimal and it's very hard to replicate the behavior of PHP implementation.
1 file declares its use of ApacheMatcherDumper
- LegacyApacheMatcherDumperTest.php in vendor/
symfony/ routing/ Tests/ Matcher/ Dumper/ LegacyApacheMatcherDumperTest.php
File
- vendor/
symfony/ routing/ Matcher/ Dumper/ ApacheMatcherDumper.php, line 28
Namespace
Symfony\Component\Routing\Matcher\DumperView 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();
$hostRegexUnique = 0;
$prevHostRegex = '';
foreach ($this
->getRoutes()
->all() as $name => $route) {
if ($route
->getCondition()) {
throw new \LogicException(sprintf('Unable to dump the routes for Apache as route "%s" has a condition.', $name));
}
$compiledRoute = $route
->compile();
$hostRegex = $compiledRoute
->getHostRegex();
if (null !== $hostRegex && $prevHostRegex !== $hostRegex) {
$prevHostRegex = $hostRegex;
++$hostRegexUnique;
$rule = array();
$regex = $this
->regexToApacheRegex($hostRegex);
$regex = self::escape($regex, ' ', '\\');
$rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
$variables = array();
$variables[] = sprintf('E=__ROUTING_host_%s:1', $hostRegexUnique);
foreach ($compiledRoute
->getHostVariables() as $i => $variable) {
$variables[] = sprintf('E=__ROUTING_host_%s_%s:%%%d', $hostRegexUnique, $variable, $i + 1);
}
$variables = implode(',', $variables);
$rule[] = sprintf('RewriteRule .? - [%s]', $variables);
$rules[] = implode("\n", $rule);
}
$rules[] = $this
->dumpRoute($name, $route, $options, $hostRegexUnique);
$methodVars = array_merge($methodVars, $route
->getMethods());
}
if (0 < count($methodVars)) {
$rule = array(
'# 405 Method Not Allowed',
);
$methodVars = array_values(array_unique($methodVars));
if (in_array('GET', $methodVars) && !in_array('HEAD', $methodVars)) {
$methodVars[] = 'HEAD';
}
foreach ($methodVars as $i => $methodVar) {
$rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%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";
}
/**
* Dumps a single route.
*
* @param string $name Route name
* @param Route $route The route
* @param array $options Options
* @param bool $hostRegexUnique Unique identifier for the host regex
*
* @return string The compiled route
*/
private function dumpRoute($name, $route, array $options, $hostRegexUnique) {
$compiledRoute = $route
->compile();
// prepare the apache regex
$regex = $this
->regexToApacheRegex($compiledRoute
->getRegex());
$regex = '^' . self::escape(preg_quote($options['base_uri']) . substr($regex, 1), ' ', '\\');
$methods = $this
->getRouteMethods($route);
$hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
$variables = array(
'E=_ROUTING_route:' . $name,
);
foreach ($compiledRoute
->getHostVariables() as $variable) {
$variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_host_%s_%s}', $variable, $hostRegexUnique, $variable);
}
foreach ($compiledRoute
->getPathVariables() as $i => $variable) {
$variables[] = 'E=_ROUTING_param_' . $variable . ':%' . ($i + 1);
}
foreach ($this
->normalizeValues($route
->getDefaults()) as $key => $value) {
$variables[] = 'E=_ROUTING_default_' . $key . ':' . strtr($value, array(
':' => '\\:',
'=' => '\\=',
'\\' => '\\\\',
' ' => '\\ ',
));
}
$variables = implode(',', $variables);
$rule = array(
"# {$name}",
);
// method mismatch
if (0 < count($methods)) {
$allow = array();
foreach ($methods as $method) {
$allow[] = 'E=_ROUTING_allow_' . $method . ':1';
}
if ($compiledRoute
->getHostRegex()) {
$rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
}
$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) {
if ($compiledRoute
->getHostRegex()) {
$rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
}
$rule[] = 'RewriteCond %{REQUEST_URI} ' . substr($regex, 0, -2) . '$';
$rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
}
// the main rule
if ($compiledRoute
->getHostRegex()) {
$rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
}
$rule[] = "RewriteCond %{REQUEST_URI} {$regex}";
$rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,{$variables}]";
return implode("\n", $rule);
}
/**
* Returns methods allowed for a route.
*
* @param Route $route The route
*
* @return array The methods
*/
private function getRouteMethods(Route $route) {
$methods = $route
->getMethods();
// GET and HEAD are equivalent
if (in_array('GET', $methods) && !in_array('HEAD', $methods)) {
$methods[] = 'HEAD';
}
return $methods;
}
/**
* Converts a regex to make it suitable for mod_rewrite.
*
* @param string $regex The regex
*
* @return string The converted regex
*/
private function regexToApacheRegex($regex) {
$regexPatternEnd = strrpos($regex, $regex[0]);
return preg_replace('/\\?P<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
}
/**
* 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;
}
/**
* Normalizes an array of values.
*
* @param array $values
*
* @return string[]
*/
private function normalizeValues(array $values) {
$normalizedValues = array();
foreach ($values as $key => $value) {
if (is_array($value)) {
foreach ($value as $index => $bit) {
$normalizedValues[sprintf('%s[%s]', $key, $index)] = $bit;
}
}
else {
$normalizedValues[$key] = (string) $value;
}
}
return $normalizedValues;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ApacheMatcherDumper:: |
public | function |
Dumps a set of Apache mod_rewrite rules. Overrides MatcherDumperInterface:: |
|
ApacheMatcherDumper:: |
private | function | Dumps a single route. | |
ApacheMatcherDumper:: |
private static | function | Escapes a string. | |
ApacheMatcherDumper:: |
private | function | Returns methods allowed for a route. | |
ApacheMatcherDumper:: |
private | function | Normalizes an array of values. | |
ApacheMatcherDumper:: |
private | function | Converts a regex to make it suitable for mod_rewrite. | |
MatcherDumper:: |
private | property | ||
MatcherDumper:: |
public | function |
Gets the routes to dump. Overrides MatcherDumperInterface:: |
|
MatcherDumper:: |
public | function | Constructor. |