[Routing] simplified route compiler
This commit is contained in:
parent
59c6609aec
commit
7c95bda751
@ -135,24 +135,6 @@ EOF
|
|||||||
$output->writeln(sprintf('<comment>Options</comment> %s', $options));
|
$output->writeln(sprintf('<comment>Options</comment> %s', $options));
|
||||||
$output->write('<comment>Regex</comment> ');
|
$output->write('<comment>Regex</comment> ');
|
||||||
$output->writeln(preg_replace('/^ /', '', preg_replace('/^/m', ' ', $route->getRegex())), OutputInterface::OUTPUT_RAW);
|
$output->writeln(preg_replace('/^ /', '', preg_replace('/^/m', ' ', $route->getRegex())), OutputInterface::OUTPUT_RAW);
|
||||||
|
|
||||||
$tokens = '';
|
|
||||||
foreach ($route->getTokens() as $token) {
|
|
||||||
if (!$tokens) {
|
|
||||||
$tokens = $this->displayToken($token);
|
|
||||||
} else {
|
|
||||||
$tokens .= "\n".str_repeat(' ', 13).$this->displayToken($token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$output->writeln(sprintf('<comment>Tokens</comment> %s', $tokens));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function displayToken($token)
|
|
||||||
{
|
|
||||||
$type = array_shift($token);
|
|
||||||
array_shift($token);
|
|
||||||
|
|
||||||
return sprintf('%-10s %s', $type, $this->formatValue($token));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function formatValue($value)
|
protected function formatValue($value)
|
||||||
|
@ -89,6 +89,8 @@ class UrlGenerator implements UrlGeneratorInterface
|
|||||||
*/
|
*/
|
||||||
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
|
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
|
||||||
{
|
{
|
||||||
|
$variables = array_flip($variables);
|
||||||
|
|
||||||
$originParameters = $parameters;
|
$originParameters = $parameters;
|
||||||
$parameters = array_replace($this->context->getParameters(), $parameters);
|
$parameters = array_replace($this->context->getParameters(), $parameters);
|
||||||
$tparams = array_replace($defaults, $parameters);
|
$tparams = array_replace($defaults, $parameters);
|
||||||
@ -104,8 +106,8 @@ class UrlGenerator implements UrlGeneratorInterface
|
|||||||
if ('variable' === $token[0]) {
|
if ('variable' === $token[0]) {
|
||||||
if (false === $optional || !isset($defaults[$token[3]]) || (isset($parameters[$token[3]]) && $parameters[$token[3]] != $defaults[$token[3]])) {
|
if (false === $optional || !isset($defaults[$token[3]]) || (isset($parameters[$token[3]]) && $parameters[$token[3]] != $defaults[$token[3]])) {
|
||||||
// check requirement
|
// check requirement
|
||||||
if (isset($requirements[$token[3]]) && !preg_match('#^'.$requirements[$token[3]].'$#', $tparams[$token[3]])) {
|
if (!preg_match('#^'.$token[2].'$#', $tparams[$token[3]])) {
|
||||||
throw new \InvalidArgumentException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $requirements[$token[3]], $tparams[$token[3]]));
|
throw new \InvalidArgumentException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]]));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($tparams[$token[3]] || !$optional) {
|
if ($tparams[$token[3]] || !$optional) {
|
||||||
@ -116,14 +118,8 @@ class UrlGenerator implements UrlGeneratorInterface
|
|||||||
$optional = false;
|
$optional = false;
|
||||||
}
|
}
|
||||||
} elseif ('text' === $token[0]) {
|
} elseif ('text' === $token[0]) {
|
||||||
$url = $token[1].$token[2].$url;
|
$url = $token[1].$url;
|
||||||
$optional = false;
|
$optional = false;
|
||||||
} else {
|
|
||||||
// handle custom tokens
|
|
||||||
if ($segment = call_user_func_array(array($this, 'generateFor'.ucfirst(array_shift($token))), array_merge(array($optional, $tparams), $token))) {
|
|
||||||
$url = $segment.$url;
|
|
||||||
$optional = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,13 +47,13 @@ class ApacheMatcherDumper extends MatcherDumper
|
|||||||
$compiledRoute = $route->compile();
|
$compiledRoute = $route->compile();
|
||||||
|
|
||||||
// prepare the apache regex
|
// prepare the apache regex
|
||||||
$regex = preg_replace('/\?P<.+?>/', '', substr($compiledRoute->getRegex(), 1, -2));
|
$regex = preg_replace('/\?P<.+?>/', '', substr(str_replace(array("\n", ' '), '', $compiledRoute->getRegex()), 1, -2));
|
||||||
$regex = '^'.preg_quote($options['base_uri']).substr($regex, 1);
|
$regex = '^'.preg_quote($options['base_uri']).substr($regex, 1);
|
||||||
|
|
||||||
$hasTrailingSlash = '/$' == substr($regex, -2) && '^/$' != $regex;
|
$hasTrailingSlash = '/$' == substr($regex, -2) && '^/$' != $regex;
|
||||||
|
|
||||||
$variables = array('E=_ROUTING__route:'.$name);
|
$variables = array('E=_ROUTING__route:'.$name);
|
||||||
foreach (array_keys($compiledRoute->getVariables()) as $i => $variable) {
|
foreach ($compiledRoute->getVariables() as $i => $variable) {
|
||||||
$variables[] = 'E=_ROUTING_'.$variable.':%'.($i + 1);
|
$variables[] = 'E=_ROUTING_'.$variable.':%'.($i + 1);
|
||||||
}
|
}
|
||||||
foreach ($route->getDefaults() as $key => $value) {
|
foreach ($route->getDefaults() as $key => $value) {
|
||||||
|
@ -60,7 +60,7 @@ class PhpMatcherDumper extends MatcherDumper
|
|||||||
$conditions = array();
|
$conditions = array();
|
||||||
$hasTrailingSlash = false;
|
$hasTrailingSlash = false;
|
||||||
$matches = false;
|
$matches = false;
|
||||||
if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
|
if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', str_replace(array("\n", ' '), '', $compiledRoute->getRegex()), $m)) {
|
||||||
if ($supportsRedirections && substr($m['url'], -1) === '/') {
|
if ($supportsRedirections && substr($m['url'], -1) === '/') {
|
||||||
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === '%s'", rtrim(str_replace('\\', '', $m['url']), '/'));
|
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === '%s'", rtrim(str_replace('\\', '', $m['url']), '/'));
|
||||||
$hasTrailingSlash = true;
|
$hasTrailingSlash = true;
|
||||||
@ -72,7 +72,7 @@ class PhpMatcherDumper extends MatcherDumper
|
|||||||
$conditions[] = sprintf("0 === strpos(\$pathinfo, '%s')", $compiledRoute->getStaticPrefix());
|
$conditions[] = sprintf("0 === strpos(\$pathinfo, '%s')", $compiledRoute->getStaticPrefix());
|
||||||
}
|
}
|
||||||
|
|
||||||
$regex = $compiledRoute->getRegex();
|
$regex = str_replace(array("\n", ' '), '', $compiledRoute->getRegex());
|
||||||
if ($supportsRedirections && $pos = strpos($regex, '/$')) {
|
if ($supportsRedirections && $pos = strpos($regex, '/$')) {
|
||||||
$regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
|
$regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
|
||||||
$hasTrailingSlash = true;
|
$hasTrailingSlash = true;
|
||||||
|
@ -31,9 +31,7 @@ class Route
|
|||||||
*
|
*
|
||||||
* Available options:
|
* Available options:
|
||||||
*
|
*
|
||||||
* * segment_separators: An array of allowed characters for segment separators (/ by default)
|
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
|
||||||
* * text_regex: A regex that match a valid text name (.+? by default)
|
|
||||||
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
|
|
||||||
*
|
*
|
||||||
* @param string $pattern The pattern to match
|
* @param string $pattern The pattern to match
|
||||||
* @param array $defaults An array of default parameter values
|
* @param array $defaults An array of default parameter values
|
||||||
@ -101,8 +99,6 @@ class Route
|
|||||||
public function setOptions(array $options)
|
public function setOptions(array $options)
|
||||||
{
|
{
|
||||||
$this->options = array_merge(array(
|
$this->options = array_merge(array(
|
||||||
'segment_separators' => array('/', '.'),
|
|
||||||
'text_regex' => '.+?',
|
|
||||||
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
||||||
), $options);
|
), $options);
|
||||||
|
|
||||||
|
@ -18,15 +18,6 @@ namespace Symfony\Component\Routing;
|
|||||||
*/
|
*/
|
||||||
class RouteCompiler implements RouteCompilerInterface
|
class RouteCompiler implements RouteCompilerInterface
|
||||||
{
|
{
|
||||||
protected $options;
|
|
||||||
protected $route;
|
|
||||||
protected $variables;
|
|
||||||
protected $firstOptional;
|
|
||||||
protected $segments;
|
|
||||||
protected $tokens;
|
|
||||||
protected $staticPrefix;
|
|
||||||
protected $regex;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compiles the current route instance.
|
* Compiles the current route instance.
|
||||||
*
|
*
|
||||||
@ -36,200 +27,67 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
*/
|
*/
|
||||||
public function compile(Route $route)
|
public function compile(Route $route)
|
||||||
{
|
{
|
||||||
$this->route = $route;
|
$pattern = $route->getPattern();
|
||||||
$this->firstOptional = 0;
|
$len = strlen($pattern);
|
||||||
$this->segments = array();
|
|
||||||
$this->variables = array();
|
|
||||||
$this->tokens = array();
|
|
||||||
$this->staticPrefix = '';
|
|
||||||
$this->regex = '';
|
|
||||||
$this->options = $this->getOptions();
|
|
||||||
|
|
||||||
$this->preCompile();
|
|
||||||
|
|
||||||
$this->tokenize();
|
|
||||||
|
|
||||||
foreach ($this->tokens as $token) {
|
|
||||||
call_user_func_array(array($this, 'compileFor'.ucfirst(array_shift($token))), $token);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->postCompile();
|
|
||||||
|
|
||||||
$separator = '';
|
|
||||||
if (count($this->tokens)) {
|
|
||||||
$lastToken = $this->tokens[count($this->tokens) - 1];
|
|
||||||
$separator = 'separator' == $lastToken[0] ? $lastToken[2] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->regex = "#^".implode("", $this->segments)."".preg_quote($separator, '#')."$#x";
|
|
||||||
|
|
||||||
// optimize tokens for generation
|
|
||||||
$tokens = array();
|
$tokens = array();
|
||||||
foreach ($this->tokens as $i => $token) {
|
$variables = array();
|
||||||
if ($i + 1 === count($this->tokens) && 'separator' === $token[0]) {
|
$pos = 0;
|
||||||
// trailing /
|
preg_match_all('#.\{([\w\d_]+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER);
|
||||||
$tokens[] = array('text', $token[2], '', null);
|
foreach ($matches as $match) {
|
||||||
} elseif ('separator' !== $token[0]) {
|
if ($text = substr($pattern, $pos, $match[0][1] - $pos)) {
|
||||||
$tokens[] = $token;
|
$tokens[] = array('text', $text);
|
||||||
}
|
}
|
||||||
}
|
$pos = $match[0][1] + strlen($match[0][0]);
|
||||||
|
$var = $match[1][0];
|
||||||
|
|
||||||
$tokens = array_reverse($tokens);
|
if ($req = $route->getRequirement($var)) {
|
||||||
|
$regexp = $req;
|
||||||
return new CompiledRoute($this->route, $this->staticPrefix, $this->regex, $tokens, $this->variables);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pre-compiles a route.
|
|
||||||
*/
|
|
||||||
protected function preCompile()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post-compiles a route.
|
|
||||||
*/
|
|
||||||
protected function postCompile()
|
|
||||||
{
|
|
||||||
// all segments after the last static segment are optional
|
|
||||||
// be careful, the n-1 is optional only if n is empty
|
|
||||||
for ($i = $this->firstOptional, $max = count($this->segments); $i < $max; $i++) {
|
|
||||||
$this->segments[$i] = (0 == $i ? '/?' : '').str_repeat(' ', $i - $this->firstOptional).'(?:'.$this->segments[$i];
|
|
||||||
$this->segments[] = str_repeat(' ', $max - $i - 1).')?';
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->staticPrefix = '';
|
|
||||||
foreach ($this->tokens as $token) {
|
|
||||||
switch ($token[0]) {
|
|
||||||
case 'separator':
|
|
||||||
break;
|
|
||||||
case 'text':
|
|
||||||
// text is static
|
|
||||||
$this->staticPrefix .= $token[1].$token[2];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// everything else indicates variable parts. break switch and for loop
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tokenizes the route.
|
|
||||||
*
|
|
||||||
* @throws \InvalidArgumentException When route can't be parsed
|
|
||||||
*/
|
|
||||||
private function tokenize()
|
|
||||||
{
|
|
||||||
$this->tokens = array();
|
|
||||||
$buffer = $this->route->getPattern();
|
|
||||||
$afterASeparator = false;
|
|
||||||
$currentSeparator = '';
|
|
||||||
|
|
||||||
// a route is an array of (separator + variable) or (separator + text) segments
|
|
||||||
while (strlen($buffer)) {
|
|
||||||
if (false !== $this->tokenizeBufferBefore($buffer, $tokens, $afterASeparator, $currentSeparator)) {
|
|
||||||
// a custom token
|
|
||||||
$this->customToken = true;
|
|
||||||
} else if ($afterASeparator && preg_match('#^\{([\w\d_]+)\}#', $buffer, $match)) {
|
|
||||||
// a variable
|
|
||||||
$this->tokens[] = array('variable', $currentSeparator, $match[0], $match[1]);
|
|
||||||
|
|
||||||
$currentSeparator = '';
|
|
||||||
$buffer = substr($buffer, strlen($match[0]));
|
|
||||||
$afterASeparator = false;
|
|
||||||
} else if ($afterASeparator && preg_match('#^('.$this->options['text_regex'].')(?:'.$this->options['segment_separators_regex'].'|$)#', $buffer, $match)) {
|
|
||||||
// a text
|
|
||||||
$this->tokens[] = array('text', $currentSeparator, $match[1], null);
|
|
||||||
|
|
||||||
$currentSeparator = '';
|
|
||||||
$buffer = substr($buffer, strlen($match[1]));
|
|
||||||
$afterASeparator = false;
|
|
||||||
} else if (!$afterASeparator && preg_match('#^'.$this->options['segment_separators_regex'].'#', $buffer, $match)) {
|
|
||||||
// a separator
|
|
||||||
$this->tokens[] = array('separator', $currentSeparator, $match[0], null);
|
|
||||||
|
|
||||||
$currentSeparator = $match[0];
|
|
||||||
$buffer = substr($buffer, strlen($match[0]));
|
|
||||||
$afterASeparator = true;
|
|
||||||
} else if (false !== $this->tokenizeBufferAfter($buffer, $tokens, $afterASeparator, $currentSeparator)) {
|
|
||||||
// a custom token
|
|
||||||
$this->customToken = true;
|
|
||||||
} else {
|
} else {
|
||||||
// parsing problem
|
$regexp = $pos !== $len ? sprintf('[^%s]*?', $pattern[$pos]) : '.*?';
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse "%s" route near "%s".', $this->route->getPattern(), $buffer));
|
}
|
||||||
|
|
||||||
|
$tokens[] = array('variable', $match[0][0][0], $regexp, $var);
|
||||||
|
$variables[] = $var;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pos < $len) {
|
||||||
|
$tokens[] = array('text', substr($pattern, $pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the first optional token
|
||||||
|
$firstOptional = INF;
|
||||||
|
for ($i = count($tokens) - 1; $i >= 0; $i--) {
|
||||||
|
if ('variable' === $tokens[$i][0] && $route->hasDefault($tokens[$i][3])) {
|
||||||
|
$firstOptional = $i;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// compute the matching regexp
|
||||||
* Tokenizes the buffer before default logic is applied.
|
$regex = '';
|
||||||
*
|
$indent = 1;
|
||||||
* This method must return false if the buffer has not been parsed.
|
foreach ($tokens as $i => $token) {
|
||||||
*
|
if ('text' === $token[0]) {
|
||||||
* @param string $buffer The current route buffer
|
$regex .= str_repeat(' ', $indent * 4).preg_quote($token[1], '#')."\n";
|
||||||
* @param array $tokens An array of current tokens
|
} else {
|
||||||
* @param Boolean $afterASeparator Whether the buffer is just after a separator
|
if ($i >= $firstOptional) {
|
||||||
* @param string $currentSeparator The last matched separator
|
$regex .= str_repeat(' ', $indent * 4)."(?:\n";
|
||||||
*
|
++$indent;
|
||||||
* @return Boolean true if a token has been generated, false otherwise
|
}
|
||||||
*/
|
$regex .= str_repeat(' ', $indent * 4).sprintf("%s(?P<%s>%s)\n", preg_quote($token[1], '#'), $token[3], $token[2]);
|
||||||
protected function tokenizeBufferBefore(&$buffer, &$tokens, &$afterASeparator, &$currentSeparator)
|
}
|
||||||
{
|
}
|
||||||
return false;
|
while (--$indent) {
|
||||||
}
|
$regex .= str_repeat(' ', $indent * 4).")?\n";
|
||||||
|
|
||||||
/**
|
|
||||||
* Tokenizes the buffer after default logic is applied.
|
|
||||||
*
|
|
||||||
* This method must return false if the buffer has not been parsed.
|
|
||||||
*
|
|
||||||
* @param string $buffer The current route buffer
|
|
||||||
* @param array $tokens An array of current tokens
|
|
||||||
* @param Boolean $afterASeparator Whether the buffer is just after a separator
|
|
||||||
* @param string $currentSeparator The last matched separator
|
|
||||||
*
|
|
||||||
* @return Boolean true if a token has been generated, false otherwise
|
|
||||||
*/
|
|
||||||
protected function tokenizeBufferAfter(&$buffer, &$tokens, &$afterASeparator, &$currentSeparator)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function compileForText($separator, $text)
|
|
||||||
{
|
|
||||||
$this->firstOptional = count($this->segments) + 1;
|
|
||||||
|
|
||||||
$this->segments[] = preg_quote($separator, '#').preg_quote($text, '#');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function compileForVariable($separator, $name, $variable)
|
|
||||||
{
|
|
||||||
if (null === $requirement = $this->route->getRequirement($variable)) {
|
|
||||||
$requirement = $this->options['variable_content_regex'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->segments[] = preg_quote($separator, '#').'(?P<'.$variable.'>'.$requirement.')';
|
return new CompiledRoute(
|
||||||
$this->variables[$variable] = $name;
|
$route,
|
||||||
|
'text' === $tokens[0][0] ? $tokens[0][1] : '',
|
||||||
if (!$this->route->hasDefault($variable)) {
|
sprintf("#^\n%s$#x", $regex),
|
||||||
$this->firstOptional = count($this->segments);
|
array_reverse($tokens),
|
||||||
}
|
$variables
|
||||||
}
|
);
|
||||||
|
|
||||||
protected function compileForSeparator($separator, $regexSeparator)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getOptions()
|
|
||||||
{
|
|
||||||
$options = $this->route->getOptions();
|
|
||||||
|
|
||||||
// compute some regexes
|
|
||||||
$quoter = function ($a) { return preg_quote($a, '#'); };
|
|
||||||
$options['segment_separators_regex'] = '(?:'.implode('|', array_map($quoter, $options['segment_separators'])).')';
|
|
||||||
$options['variable_content_regex'] = '[^'.implode('', array_map($quoter, $options['segment_separators'])).']+?';
|
|
||||||
|
|
||||||
return $options;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,6 @@ class CompiledRouteTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(array('foo' => 'bar'), $compiled->getDefaults(), '->getDefaults() returns the route defaults');
|
$this->assertEquals(array('foo' => 'bar'), $compiled->getDefaults(), '->getDefaults() returns the route defaults');
|
||||||
$this->assertEquals(array('foo' => '\d+'), $compiled->getRequirements(), '->getRequirements() returns the route requirements');
|
$this->assertEquals(array('foo' => '\d+'), $compiled->getRequirements(), '->getRequirements() returns the route requirements');
|
||||||
$this->assertEquals(array_merge(array(
|
$this->assertEquals(array_merge(array(
|
||||||
'segment_separators' => array('/', '.'),
|
|
||||||
'text_regex' => '.+?',
|
|
||||||
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
||||||
), array('foo' => 'bar')), $compiled->getOptions(), '->getOptions() returns the route options');
|
), array('foo' => 'bar')), $compiled->getOptions(), '->getOptions() returns the route options');
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,10 @@ RewriteCond %{REQUEST_URI} ^/foo/(baz|symfony)$
|
|||||||
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:foo,E=_ROUTING_bar:%1,E=_ROUTING_def:test]
|
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:foo,E=_ROUTING_bar:%1,E=_ROUTING_def:test]
|
||||||
|
|
||||||
# bar
|
# bar
|
||||||
RewriteCond %{REQUEST_URI} ^/bar/([^/\.]+?)$
|
RewriteCond %{REQUEST_URI} ^/bar/(.*?)$
|
||||||
RewriteCond %{REQUEST_METHOD} !^(get|head)$ [NC]
|
RewriteCond %{REQUEST_METHOD} !^(get|head)$ [NC]
|
||||||
RewriteRule .* - [S=1,E=_ROUTING__allow_get:1,E=_ROUTING__allow_head:1]
|
RewriteRule .* - [S=1,E=_ROUTING__allow_get:1,E=_ROUTING__allow_head:1]
|
||||||
RewriteCond %{REQUEST_URI} ^/bar/([^/\.]+?)$
|
RewriteCond %{REQUEST_URI} ^/bar/(.*?)$
|
||||||
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:bar,E=_ROUTING_foo:%1]
|
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:bar,E=_ROUTING_foo:%1]
|
||||||
|
|
||||||
# baz
|
# baz
|
||||||
@ -28,18 +28,18 @@ RewriteCond %{REQUEST_URI} ^/test/baz3/$
|
|||||||
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz3]
|
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz3]
|
||||||
|
|
||||||
# baz4
|
# baz4
|
||||||
RewriteCond %{REQUEST_URI} ^/test/([^/\.]+?)$
|
RewriteCond %{REQUEST_URI} ^/test/([^/]*?)$
|
||||||
RewriteRule .* $0/ [QSA,L,R=301]
|
RewriteRule .* $0/ [QSA,L,R=301]
|
||||||
RewriteCond %{REQUEST_URI} ^/test/([^/\.]+?)/$
|
RewriteCond %{REQUEST_URI} ^/test/([^/]*?)/$
|
||||||
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz4,E=_ROUTING_foo:%1]
|
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz4,E=_ROUTING_foo:%1]
|
||||||
|
|
||||||
# baz5
|
# baz5
|
||||||
RewriteCond %{REQUEST_URI} ^/test/([^/\.]+?)/$
|
RewriteCond %{REQUEST_URI} ^/test/([^/]*?)/$
|
||||||
RewriteCond %{REQUEST_METHOD} !^(post)$ [NC]
|
RewriteCond %{REQUEST_METHOD} !^(post)$ [NC]
|
||||||
RewriteRule .* - [S=2,E=_ROUTING__allow_post:1]
|
RewriteRule .* - [S=2,E=_ROUTING__allow_post:1]
|
||||||
RewriteCond %{REQUEST_URI} ^/test/([^/\.]+?)$
|
RewriteCond %{REQUEST_URI} ^/test/([^/]*?)$
|
||||||
RewriteRule .* $0/ [QSA,L,R=301]
|
RewriteRule .* $0/ [QSA,L,R=301]
|
||||||
RewriteCond %{REQUEST_URI} ^/test/([^/\.]+?)/$
|
RewriteCond %{REQUEST_URI} ^/test/([^/]*?)/$
|
||||||
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz5,E=_ROUTING_foo:%1]
|
RewriteRule .* app.php [QSA,L,E=_ROUTING__route:baz5,E=_ROUTING_foo:%1]
|
||||||
|
|
||||||
# 405 Method Not Allowed
|
# 405 Method Not Allowed
|
||||||
|
@ -30,7 +30,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bar
|
// bar
|
||||||
if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?P<foo>[^/\.]+?)$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?P<foo>.*?)$#x', $pathinfo, $matches)) {
|
||||||
if (!in_array($this->context->getMethod(), array('get', 'head'))) {
|
if (!in_array($this->context->getMethod(), array('get', 'head'))) {
|
||||||
$allow = array_merge($allow, array('get', 'head'));
|
$allow = array_merge($allow, array('get', 'head'));
|
||||||
goto not_bar;
|
goto not_bar;
|
||||||
@ -56,13 +56,13 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
// baz4
|
// baz4
|
||||||
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]*?)/$#x', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'baz4';
|
$matches['_route'] = 'baz4';
|
||||||
return $matches;
|
return $matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
// baz5
|
// baz5
|
||||||
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]*?)/$#x', $pathinfo, $matches)) {
|
||||||
if ($this->context->getMethod() != 'post') {
|
if ($this->context->getMethod() != 'post') {
|
||||||
$allow[] = 'post';
|
$allow[] = 'post';
|
||||||
goto not_baz5;
|
goto not_baz5;
|
||||||
@ -73,7 +73,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
not_baz5:
|
not_baz5:
|
||||||
|
|
||||||
// baz.baz6
|
// baz.baz6
|
||||||
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]*?)/$#x', $pathinfo, $matches)) {
|
||||||
if ($this->context->getMethod() != 'put') {
|
if ($this->context->getMethod() != 'put') {
|
||||||
$allow[] = 'put';
|
$allow[] = 'put';
|
||||||
goto not_bazbaz6;
|
goto not_bazbaz6;
|
||||||
|
@ -30,7 +30,7 @@ class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\Redirec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// bar
|
// bar
|
||||||
if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?P<foo>[^/\.]+?)$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/bar') && preg_match('#^/bar/(?P<foo>.*?)$#x', $pathinfo, $matches)) {
|
||||||
if (!in_array($this->context->getMethod(), array('get', 'head'))) {
|
if (!in_array($this->context->getMethod(), array('get', 'head'))) {
|
||||||
$allow = array_merge($allow, array('get', 'head'));
|
$allow = array_merge($allow, array('get', 'head'));
|
||||||
goto not_bar;
|
goto not_bar;
|
||||||
@ -59,7 +59,7 @@ class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\Redirec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// baz4
|
// baz4
|
||||||
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]*?)/?$#x', $pathinfo, $matches)) {
|
||||||
if (substr($pathinfo, -1) !== '/') {
|
if (substr($pathinfo, -1) !== '/') {
|
||||||
return $this->redirect($pathinfo.'/', 'baz4');
|
return $this->redirect($pathinfo.'/', 'baz4');
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\Redirec
|
|||||||
}
|
}
|
||||||
|
|
||||||
// baz5
|
// baz5
|
||||||
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]*?)/?$#x', $pathinfo, $matches)) {
|
||||||
if ($this->context->getMethod() != 'post') {
|
if ($this->context->getMethod() != 'post') {
|
||||||
$allow[] = 'post';
|
$allow[] = 'post';
|
||||||
goto not_baz5;
|
goto not_baz5;
|
||||||
@ -82,7 +82,7 @@ class ProjectUrlMatcher extends Symfony\Tests\Component\Routing\Fixtures\Redirec
|
|||||||
not_baz5:
|
not_baz5:
|
||||||
|
|
||||||
// baz.baz6
|
// baz.baz6
|
||||||
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/\.]+?)/?$#x', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/test') && preg_match('#^/test/(?P<foo>[^/]*?)/?$#x', $pathinfo, $matches)) {
|
||||||
if ($this->context->getMethod() != 'put') {
|
if ($this->context->getMethod() != 'put') {
|
||||||
$allow[] = 'put';
|
$allow[] = 'put';
|
||||||
goto not_bazbaz6;
|
goto not_bazbaz6;
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of the Symfony package.
|
|
||||||
*
|
|
||||||
* (c) Fabien Potencier <fabien@symfony.com>
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Symfony\Tests\Component\Routing;
|
|
||||||
|
|
||||||
use Symfony\Component\Routing\RouteCompiler as BaseRouteCompiler;
|
|
||||||
use Symfony\Component\Routing\Route;
|
|
||||||
|
|
||||||
class RouteCompiler extends BaseRouteCompiler
|
|
||||||
{
|
|
||||||
protected function tokenizeBufferBefore(&$buffer, &$tokens, &$afterASeparator, &$currentSeparator)
|
|
||||||
{
|
|
||||||
if ($afterASeparator && preg_match('#^=([\w\d_]+)#', $buffer, $match)) {
|
|
||||||
// a labelled variable
|
|
||||||
$this->tokens[] = array('label', $currentSeparator, $match[0], $match[1]);
|
|
||||||
|
|
||||||
$currentSeparator = '';
|
|
||||||
$buffer = substr($buffer, strlen($match[0]));
|
|
||||||
$afterASeparator = false;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function compileForLabel($separator, $name, $variable)
|
|
||||||
{
|
|
||||||
if (null === $requirement = $this->route->getRequirement($variable)) {
|
|
||||||
$requirement = $this->options['variable_content_regex'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->segments[] = preg_quote($separator, '#').$variable.$separator.'(?P<'.$variable.'>'.$requirement.')';
|
|
||||||
$this->variables[$variable] = $name;
|
|
||||||
|
|
||||||
if (!$this->route->getDefault($variable)) {
|
|
||||||
$this->firstOptional = count($this->segments);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function generateForLabel($optional, $tparams, $separator, $name, $variable)
|
|
||||||
{
|
|
||||||
if (!empty($tparams[$variable]) && (!$optional || !isset($this->defaults[$variable]) || $tparams[$variable] != $this->defaults[$variable])) {
|
|
||||||
return $variable.'/'.urlencode($tparams[$variable]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,8 +13,6 @@ namespace Symfony\Tests\Component\Routing;
|
|||||||
|
|
||||||
use Symfony\Component\Routing\Route;
|
use Symfony\Component\Routing\Route;
|
||||||
|
|
||||||
require __DIR__.'/RouteCompiler.php';
|
|
||||||
|
|
||||||
class RouteCompilerTest extends \PHPUnit_Framework_TestCase
|
class RouteCompilerTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -27,7 +25,7 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$compiled = $route->compile();
|
$compiled = $route->compile();
|
||||||
$this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
|
$this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
|
||||||
$this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)');
|
$this->assertEquals($regex, str_replace(array("\n", ' '), '', $compiled->getRegex()), $name.' (regex)');
|
||||||
$this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
|
$this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
|
||||||
$this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
|
$this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
|
||||||
}
|
}
|
||||||
@ -39,57 +37,50 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
|
|||||||
'Static route',
|
'Static route',
|
||||||
array('/foo'),
|
array('/foo'),
|
||||||
'/foo', '#^/foo$#x', array(), array(
|
'/foo', '#^/foo$#x', array(), array(
|
||||||
array('text', '/', 'foo', null),
|
array('text', '/foo'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
array(
|
array(
|
||||||
'Route with a variable',
|
'Route with a variable',
|
||||||
array('/foo/{bar}'),
|
array('/foo/{bar}'),
|
||||||
'/foo', '#^/foo/(?P<bar>[^/\.]+?)$#x', array('bar' => '{bar}'), array(
|
'/foo', '#^/foo/(?P<bar>.*?)$#x', array('bar'), array(
|
||||||
array('variable', '/', '{bar}', 'bar'),
|
array('variable', '/', '.*?', 'bar'),
|
||||||
array('text', '/', 'foo', null),
|
array('text', '/foo'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
array(
|
array(
|
||||||
'Route with a variable that has a default value',
|
'Route with a variable that has a default value',
|
||||||
array('/foo/{bar}', array('bar' => 'bar')),
|
array('/foo/{bar}', array('bar' => 'bar')),
|
||||||
'/foo', '#^/foo(?:/(?P<bar>[^/\.]+?))?$#x', array('bar' => '{bar}'), array(
|
'/foo', '#^/foo(?:/(?P<bar>.*?))?$#x', array('bar'), array(
|
||||||
array('variable', '/', '{bar}', 'bar'),
|
array('variable', '/', '.*?', 'bar'),
|
||||||
array('text', '/', 'foo', null),
|
array('text', '/foo'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
array(
|
array(
|
||||||
'Route with several variables',
|
'Route with several variables',
|
||||||
array('/foo/{bar}/{foobar}'),
|
array('/foo/{bar}/{foobar}'),
|
||||||
'/foo', '#^/foo/(?P<bar>[^/\.]+?)/(?P<foobar>[^/\.]+?)$#x', array('bar' => '{bar}', 'foobar' => '{foobar}'), array(
|
'/foo', '#^/foo/(?P<bar>[^/]*?)/(?P<foobar>.*?)$#x', array('bar', 'foobar'), array(
|
||||||
array('variable', '/', '{foobar}', 'foobar'),
|
array('variable', '/', '.*?', 'foobar'),
|
||||||
array('variable', '/', '{bar}', 'bar'),
|
array('variable', '/', '[^/]*?', 'bar'),
|
||||||
array('text', '/', 'foo', null),
|
array('text', '/foo'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
array(
|
array(
|
||||||
'Route with several variables that have default values',
|
'Route with several variables that have default values',
|
||||||
array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')),
|
array('/foo/{bar}/{foobar}', array('bar' => 'bar', 'foobar' => '')),
|
||||||
'/foo', '#^/foo(?:/(?P<bar>[^/\.]+?) (?:/(?P<foobar>[^/\.]+?) )?)?$#x', array('bar' => '{bar}', 'foobar' => '{foobar}'), array(
|
'/foo', '#^/foo(?:/(?P<bar>[^/]*?)(?:/(?P<foobar>.*?))?)?$#x', array('bar', 'foobar'), array(
|
||||||
array('variable', '/', '{foobar}', 'foobar'),
|
array('variable', '/', '.*?', 'foobar'),
|
||||||
array('variable', '/', '{bar}', 'bar'),
|
array('variable', '/', '[^/]*?', 'bar'),
|
||||||
array('text', '/', 'foo', null),
|
array('text', '/foo'),
|
||||||
)),
|
)),
|
||||||
|
|
||||||
array(
|
array(
|
||||||
'Route with several variables but some of them have no default values',
|
'Route with several variables but some of them have no default values',
|
||||||
array('/foo/{bar}/{foobar}', array('bar' => 'bar')),
|
array('/foo/{bar}/{foobar}', array('bar' => 'bar')),
|
||||||
'/foo', '#^/foo/(?P<bar>[^/\.]+?)/(?P<foobar>[^/\.]+?)$#x', array('bar' => '{bar}', 'foobar' => '{foobar}'), array(
|
'/foo', '#^/foo/(?P<bar>[^/]*?)/(?P<foobar>.*?)$#x', array('bar', 'foobar'), array(
|
||||||
array('variable', '/', '{foobar}', 'foobar'),
|
array('variable', '/', '.*?', 'foobar'),
|
||||||
array('variable', '/', '{bar}', 'bar'),
|
array('variable', '/', '[^/]*?', 'bar'),
|
||||||
array('text', '/', 'foo', null),
|
array('text', '/foo'),
|
||||||
)),
|
|
||||||
|
|
||||||
array(
|
|
||||||
'Route with a custom token',
|
|
||||||
array('/=foo', array(), array(), array('compiler_class' => 'Symfony\\Tests\\Component\\Routing\\RouteCompiler')),
|
|
||||||
'', '#^/foo/(?P<foo>[^/\.]+?)$#x', array('foo' => '=foo'), array(
|
|
||||||
array('label', '/', '=foo', 'foo'),
|
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -41,8 +41,6 @@ class RouteTest extends \PHPUnit_Framework_TestCase
|
|||||||
$route = new Route('/{foo}');
|
$route = new Route('/{foo}');
|
||||||
$route->setOptions(array('foo' => 'bar'));
|
$route->setOptions(array('foo' => 'bar'));
|
||||||
$this->assertEquals(array_merge(array(
|
$this->assertEquals(array_merge(array(
|
||||||
'segment_separators' => array('/', '.'),
|
|
||||||
'text_regex' => '.+?',
|
|
||||||
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler',
|
||||||
), array('foo' => 'bar')), $route->getOptions(), '->setOptions() sets the options');
|
), array('foo' => 'bar')), $route->getOptions(), '->setOptions() sets the options');
|
||||||
$this->assertEquals($route, $route->setOptions(array()), '->setOptions() implements a fluent interface');
|
$this->assertEquals($route, $route->setOptions(array()), '->setOptions() implements a fluent interface');
|
||||||
|
Reference in New Issue
Block a user