merged branch arnaud-lb/hostname-routes (PR #3378)
This PR was merged into the master branch. Commits -------17f51a1
Merge pull request #6 from Tobion/hostname-routese120a7a
fix API of RouteCollection26e5684
some type fixes514e27a
[Routing] fix PhpMatcherDumper that returned numeric-indexed params that are returned besides named placeholders by preg_match7ed3013
switch to array_replace instead of array_merge94ec653
removed irrelevant string case in XmlFileLoader9ffe3de
synchronize the fixtures in different formats and fix default for numeric requirement6cd3457
fixed CS8366b8a
[Routing] fixed validity check for hostname params in UrlGeneratora8ce621
[Routing] added support for hostname in the apache matcher dumper562174a
[Routing] fixed indentation of dumped collections1489021
fixed CSa270458
[Routing] added some more unit tests153fcf2
[Routing] added some unit tests for the PHP loader68da6ad
[Routing] added support for hostname in the XML loader3dfca47
[Routing] added some unit tests for the YAML loader92f9c15
[Routing] changed CompiledRoute signature to be more consistentd91e5a2
[Routing] fixed Route annotation for hostname (should be hostname_pattern instead of hostnamePattern)62de881
[Routing] clarified a variable content11b4378
[Routing] added hostname support in UrlMatcherfc015d5
[Routing] fixed route generation with a hostname pattern when the hostname is the same as the current one (no need to force the generated URL to be absolute)462999d
[Routing] display hostname pattern in router:debug output805806a
[Routing] added hostname matching support to UrlGenerator7a15e00
[Routing] added hostname matching support to AnnotationClassLoadercab450c
[Routing] added hostname matching support to YamlFileLoader85d11af
[Routing] added hostname matching support to PhpMatcherDumper402359b
[Routing] added hostname matching support to RouteCompileradd3658
[Routing] added hostname matching support to Route and RouteCollection23feb37
[Routing] added hostname matching support to CompiledRoute Discussion ---------- [2.2][Routing] hostname pattern for routes Bug fix: no Feature addition: yes Fixes the following tickets: #1762, #3276 Backwards compatibility break: no Symfony2 tests pass: yes This adds a hostname_pattern property to routes. It works like the pattern property (hostname_pattern can have variables, requirements, etc). The hostname_pattern property can be set on both routes and route collections. Yaml example: ``` yaml # Setting the hostname_pattern for a whole collection of routes AcmeBundle: resource: "@AcmeBundle/Controller/" type: annotation prefix: / hostname_pattern: {locale}.example.com requirements: locale: en|fr # Setting the hostname_pattern for single route some_route: pattern: /hello/{name} hostname_pattern: {locale}.example.com requirements: locale: en|fr name: \w+ defaults: _controller: Foo:bar:baz ``` Annotations example: ``` php <?php /** * Inherits requirements and hostname pattern from the collection * @Route("/foo") */ public function fooAction(); /** * Set a specific hostnamePattern for this route only * @Route("/foo", hostnamePattern="{_locale}.example.com", requirements={"_locale="fr|en"}) */ public function fooAction(); ``` Performance: Consecutive routes with the same hostname pattern are grouped, and a single test is made against the hostname for this group, so the overhead is very low: ``` @Route("/foo", hostnamePattern="a.example.com") @Route("/bar", hostnamePattern="a.example.com") @Route("/baz", hostnamePattern="b.example.com") ``` is compiled like this: ``` if (hostname matches a.example.com) { // test route "/foo" // test route "/bar" } if (hostname matches b.example.com) { // test route "/baz" } ``` The PR also tries harder to optimize routes sharing the same prefix: ``` @Route("/cafe") @Route("/cacao") @Route("/coca") ``` is compiled like this: ``` if (url starts with /c) { if (url starts with /ca) { // test route "/cafe" // test route "/cacao" } // test route "/coca" } ``` --------------------------------------------------------------------------- by Koc at 2012-02-16T14:14:19Z Interesting. Have you looked at #3057, #3002? Killer feature of #3057 : multiple hostnames per route. --------------------------------------------------------------------------- by arnaud-lb at 2012-02-16T14:21:28Z @Koc yes, the main difference is that this PR allows variables in the hostname pattern, with requirements, etc just like the path pattern. The other PRs use a `_host` requirement, which works like the `_method` requirement (takes a list of allowed hostnames separated by `|`). > Killer feature of #3057 : multiple hostnames per route. If you have multiple tlds you can easily do it like this: ``` yaml hostbased_route: pattern: / hostname_pattern: symfony.{tld} requirements: tld: org|com ``` Or with completely different domain names: ``` yaml hostbased_route: pattern: / hostname_pattern: {domain} requirements: domain: example\.com|symfony\.com ``` Requirements allow DIC %parameters%, so you can also put you domains in your config.yml. --------------------------------------------------------------------------- by Koc at 2012-02-16T15:52:16Z wow, nice! So looks like this PR closes my #3276 ticket? --------------------------------------------------------------------------- by arnaud-lb at 2012-02-16T15:53:55Z Yes, apparently :) --------------------------------------------------------------------------- by Koc at 2012-02-16T15:56:53Z I cann't find method `ParameterBag::resolveValue` calling in this PR, like here https://github.com/symfony/symfony/pull/3316/files --------------------------------------------------------------------------- by arnaud-lb at 2012-02-16T16:03:48Z I think it's in core already --------------------------------------------------------------------------- by Koc at 2012-02-16T16:11:38Z looks like yes https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Routing/Router.php#L81 --------------------------------------------------------------------------- by dlsniper at 2012-02-16T19:37:57Z This PR looks great, it's something like this I've been waiting for. I know @fabpot said he's working on something similar but I think if he agrees with this it could be a great addition to the core. @fabpot , @stof any objections about this PR if gets fully done? --------------------------------------------------------------------------- by stof at 2012-02-16T20:00:21Z Well, we already have 2 other implementations for this stuff in the PRs. @fabpot please take time to look at them --------------------------------------------------------------------------- by stof at 2012-02-16T20:03:17Z This one is absolutely not tested and seems to break the existing tests according to the description. So it cannot be reviewed as is. --------------------------------------------------------------------------- by dlsniper at 2012-02-16T22:00:24Z @stof I understand it's a WIP but the other PRs where ignored as well and like you've said, there's a bunch of PRs already on this issue all doing a thing or another. So an early feedback on this, or any other, could lead it to the right path in order to finally solve this issue. --------------------------------------------------------------------------- by arnaud-lb at 2012-02-17T23:57:28Z Added tests; others are passing now --------------------------------------------------------------------------- by arnaud-lb at 2012-02-22T21:10:20Z I'm going to add support for the Apache dumper and the XML loader; does this PR have a chance to be merged ? cc @fabpot @stof --------------------------------------------------------------------------- by stof at 2012-02-22T22:05:23Z @arnaud-lb We need to wait @fabpot's mind about the way he prefers to implement it to know which one can be merged. --------------------------------------------------------------------------- by IjinPL at 2012-02-27T02:01:57Z Forked @arnaud-lb *hostname_pattern* to add XML parasing support. --------------------------------------------------------------------------- by stof at 2012-04-03T23:59:12Z @arnaud-lb Please rebase your branch. It conflicts with master because of the move of the tests @fabpot @vicb ping --------------------------------------------------------------------------- by dlsniper at 2012-04-13T19:52:23Z Hi, If @arnaud-lb won't be able to rebase this I could help with some work on this but there's still the problem of actually choosing the right PR(s) for this issue. @blogsh says in his last commit that this PR is a bit better in his opinion but @fabpot needs to decide in the end. --------------------------------------------------------------------------- by arnaud-lb at 2012-04-14T17:26:55Z @stof rebased --------------------------------------------------------------------------- by nomack84 at 2012-04-20T13:01:00Z @fabpot Any final word about this pull request? It would be nice to have this feature ready for 2.1. --------------------------------------------------------------------------- by asm89 at 2012-04-24T21:27:50Z Using the `{_locale}` placeholder in the host would set the locale for the request just like it does now? Another thing I'm wondering is how/if it should be possible to set the hostname pattern for all your routes, or at least when importing routes? Otherwise you'll end up repeating the same host pattern over and over again. I think this is also important when importing routes from third party bundles. --------------------------------------------------------------------------- by fabpot at 2012-04-25T01:17:51Z I'm reviewing this PR and I'm going to make some modifications. I will send a PR to @arnaud-lb soon. --------------------------------------------------------------------------- by fabpot at 2012-04-25T03:10:18Z I've sent a PR to @arnaud-lb arnaud-lb/symfony#3 that fixes some minor bugs and add support in more classes. --------------------------------------------------------------------------- by fabpot at 2012-04-25T03:12:52Z @asm89: Placeholders in the hostname are managed in the same way as the ones from the URL pattern. You can set a hostname pattern for a collection (like the prefix for URL patterns). --------------------------------------------------------------------------- by Tobion at 2012-04-25T09:31:19Z I think we need to change the contents of $variables, $tokens, and $hostnameTokens in the CompiledRoute. They contain redundant information and the content structure of these variables ist not documentation in any way. If we remove duplicated content and put it in a (single) well defined variable, it would also reduce the information that need to be saved in the generated class by the UrlGeneratorDumper. --------------------------------------------------------------------------- by arnaud-lb at 2012-04-26T08:54:21Z @fabpot thanks :) I've merged it --------------------------------------------------------------------------- by stof at 2012-04-26T12:08:40Z A rebase is needed --------------------------------------------------------------------------- by fabpot at 2012-04-26T13:28:08Z no need to rebase, I will resolve the conflicts when merging. I've still have some minor changes to do before merging though. Anyone willing to have a look at implementing the Apache dumper part? --------------------------------------------------------------------------- by Tobion at 2012-04-26T14:59:00Z @fabpot you want to merge this for 2.1 although it introduces big changes that need extensive review and testing? But #3958 is not considered for 2.1? I thought we are in some sort of feature freeze for the components in order to not postpone the release. --------------------------------------------------------------------------- by fabpot at 2012-04-26T17:21:09Z @Tobion: I never said it will be in 2.1. The plan is to create a 2.1 branch soon so that we can continue working on 2.2. --------------------------------------------------------------------------- by Koc at 2012-04-26T19:46:43Z https://twitter.com/#!/fabpot/status/178502663690915840
This commit is contained in:
commit
c94bdf6bdf
@ -15,7 +15,6 @@ use Symfony\Component\Console\Input\InputArgument;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Routing\RouterInterface;
|
use Symfony\Component\Routing\RouterInterface;
|
||||||
use Symfony\Component\Console\Output\Output;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A console command for retrieving information about routes
|
* A console command for retrieving information about routes
|
||||||
@ -82,8 +81,10 @@ EOF
|
|||||||
|
|
||||||
$output->writeln($this->getHelper('formatter')->formatSection('router', 'Current routes'));
|
$output->writeln($this->getHelper('formatter')->formatSection('router', 'Current routes'));
|
||||||
|
|
||||||
$maxName = 4;
|
$maxName = strlen('name');
|
||||||
$maxMethod = 6;
|
$maxMethod = strlen('method');
|
||||||
|
$maxHostname = strlen('hostname');
|
||||||
|
|
||||||
foreach ($routes as $name => $route) {
|
foreach ($routes as $name => $route) {
|
||||||
$requirements = $route->getRequirements();
|
$requirements = $route->getRequirements();
|
||||||
$method = isset($requirements['_method'])
|
$method = isset($requirements['_method'])
|
||||||
@ -91,20 +92,18 @@ EOF
|
|||||||
? implode(', ', $requirements['_method']) : $requirements['_method']
|
? implode(', ', $requirements['_method']) : $requirements['_method']
|
||||||
)
|
)
|
||||||
: 'ANY';
|
: 'ANY';
|
||||||
|
$hostname = '' !== $route->getHostnamePattern()
|
||||||
|
? $route->getHostnamePattern() : 'ANY';
|
||||||
|
|
||||||
if (strlen($name) > $maxName) {
|
$maxName = max($maxName, strlen($name));
|
||||||
$maxName = strlen($name);
|
$maxMethod = max($maxMethod, strlen($method));
|
||||||
|
$maxHostname = max($maxHostname, strlen($hostname));
|
||||||
}
|
}
|
||||||
|
$format = '%-'.$maxName.'s %-'.$maxMethod.'s %-'.$maxHostname.'s %s';
|
||||||
if (strlen($method) > $maxMethod) {
|
|
||||||
$maxMethod = strlen($method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$format = '%-'.$maxName.'s %-'.$maxMethod.'s %s';
|
|
||||||
|
|
||||||
// displays the generated routes
|
// displays the generated routes
|
||||||
$format1 = '%-'.($maxName + 19).'s %-'.($maxMethod + 19).'s %s';
|
$format1 = '%-'.($maxName + 19).'s %-'.($maxMethod + 19).'s %-'.($maxHostname + 19).'s %s';
|
||||||
$output->writeln(sprintf($format1, '<comment>Name</comment>', '<comment>Method</comment>', '<comment>Pattern</comment>'));
|
$output->writeln(sprintf($format1, '<comment>Name</comment>', '<comment>Method</comment>', '<comment>Hostname</comment>', '<comment>Pattern</comment>'));
|
||||||
foreach ($routes as $name => $route) {
|
foreach ($routes as $name => $route) {
|
||||||
$requirements = $route->getRequirements();
|
$requirements = $route->getRequirements();
|
||||||
$method = isset($requirements['_method'])
|
$method = isset($requirements['_method'])
|
||||||
@ -112,7 +111,9 @@ EOF
|
|||||||
? implode(', ', $requirements['_method']) : $requirements['_method']
|
? implode(', ', $requirements['_method']) : $requirements['_method']
|
||||||
)
|
)
|
||||||
: 'ANY';
|
: 'ANY';
|
||||||
$output->writeln(sprintf($format, $name, $method, $route->getPattern()));
|
$hostname = '' !== $route->getHostnamePattern()
|
||||||
|
? $route->getHostnamePattern() : 'ANY';
|
||||||
|
$output->writeln(sprintf($format, $name, $method, $hostname, $route->getPattern()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ class Route
|
|||||||
private $requirements;
|
private $requirements;
|
||||||
private $options;
|
private $options;
|
||||||
private $defaults;
|
private $defaults;
|
||||||
|
private $hostnamePattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -43,7 +44,7 @@ class Route
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach ($data as $key => $value) {
|
foreach ($data as $key => $value) {
|
||||||
$method = 'set'.$key;
|
$method = 'set'.str_replace('_', '', $key);
|
||||||
if (!method_exists($this, $method)) {
|
if (!method_exists($this, $method)) {
|
||||||
throw new \BadMethodCallException(sprintf("Unknown property '%s' on annotation '%s'.", $key, get_class($this)));
|
throw new \BadMethodCallException(sprintf("Unknown property '%s' on annotation '%s'.", $key, get_class($this)));
|
||||||
}
|
}
|
||||||
@ -61,6 +62,16 @@ class Route
|
|||||||
return $this->pattern;
|
return $this->pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setHostnamePattern($pattern)
|
||||||
|
{
|
||||||
|
$this->hostnamePattern = $pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHostnamePattern()
|
||||||
|
{
|
||||||
|
return $this->hostnamePattern;
|
||||||
|
}
|
||||||
|
|
||||||
public function setName($name)
|
public function setName($name)
|
||||||
{
|
{
|
||||||
$this->name = $name;
|
$this->name = $name;
|
||||||
|
@ -22,6 +22,10 @@ class CompiledRoute
|
|||||||
private $tokens;
|
private $tokens;
|
||||||
private $staticPrefix;
|
private $staticPrefix;
|
||||||
private $regex;
|
private $regex;
|
||||||
|
private $pathVariables;
|
||||||
|
private $hostnameVariables;
|
||||||
|
private $hostnameRegex;
|
||||||
|
private $hostnameTokens;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -29,13 +33,21 @@ class CompiledRoute
|
|||||||
* @param string $staticPrefix The static prefix of the compiled route
|
* @param string $staticPrefix The static prefix of the compiled route
|
||||||
* @param string $regex The regular expression to use to match this route
|
* @param string $regex The regular expression to use to match this route
|
||||||
* @param array $tokens An array of tokens to use to generate URL for this route
|
* @param array $tokens An array of tokens to use to generate URL for this route
|
||||||
* @param array $variables An array of variables
|
* @param array $pathVariables An array of path variables
|
||||||
|
* @param string|null $hostnameRegex Hostname regex
|
||||||
|
* @param array $hostnameTokens Hostname tokens
|
||||||
|
* @param array $hostnameVariables An array of hostname variables
|
||||||
|
* @param array $variables An array of variables (variables defined in the path and in the hostname patterns)
|
||||||
*/
|
*/
|
||||||
public function __construct($staticPrefix, $regex, array $tokens, array $variables)
|
public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostnameRegex = null, array $hostnameTokens = array(), array $hostnameVariables = array(), array $variables = array())
|
||||||
{
|
{
|
||||||
$this->staticPrefix = $staticPrefix;
|
$this->staticPrefix = (string) $staticPrefix;
|
||||||
$this->regex = $regex;
|
$this->regex = $regex;
|
||||||
$this->tokens = $tokens;
|
$this->tokens = $tokens;
|
||||||
|
$this->pathVariables = $pathVariables;
|
||||||
|
$this->hostnameRegex = $hostnameRegex;
|
||||||
|
$this->hostnameTokens = $hostnameTokens;
|
||||||
|
$this->hostnameVariables = $hostnameVariables;
|
||||||
$this->variables = $variables;
|
$this->variables = $variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +71,16 @@ class CompiledRoute
|
|||||||
return $this->regex;
|
return $this->regex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname regex
|
||||||
|
*
|
||||||
|
* @return string|null The hostname regex or null
|
||||||
|
*/
|
||||||
|
public function getHostnameRegex()
|
||||||
|
{
|
||||||
|
return $this->hostnameRegex;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the tokens.
|
* Returns the tokens.
|
||||||
*
|
*
|
||||||
@ -69,6 +91,16 @@ class CompiledRoute
|
|||||||
return $this->tokens;
|
return $this->tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname tokens.
|
||||||
|
*
|
||||||
|
* @return array The tokens
|
||||||
|
*/
|
||||||
|
public function getHostnameTokens()
|
||||||
|
{
|
||||||
|
return $this->hostnameTokens;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the variables.
|
* Returns the variables.
|
||||||
*
|
*
|
||||||
@ -78,4 +110,25 @@ class CompiledRoute
|
|||||||
{
|
{
|
||||||
return $this->variables;
|
return $this->variables;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the path variables.
|
||||||
|
*
|
||||||
|
* @return array The variables
|
||||||
|
*/
|
||||||
|
public function getPathVariables()
|
||||||
|
{
|
||||||
|
return $this->pathVariables;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname variables.
|
||||||
|
*
|
||||||
|
* @return array The variables
|
||||||
|
*/
|
||||||
|
public function getHostnameVariables()
|
||||||
|
{
|
||||||
|
return $this->hostnameVariables;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -91,6 +91,7 @@ EOF;
|
|||||||
$properties[] = $route->getDefaults();
|
$properties[] = $route->getDefaults();
|
||||||
$properties[] = $route->getRequirements();
|
$properties[] = $route->getRequirements();
|
||||||
$properties[] = $compiledRoute->getTokens();
|
$properties[] = $compiledRoute->getTokens();
|
||||||
|
$properties[] = $compiledRoute->getHostnameTokens();
|
||||||
|
|
||||||
$routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
|
$routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true)));
|
||||||
}
|
}
|
||||||
@ -113,9 +114,9 @@ EOF;
|
|||||||
throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', \$name));
|
throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', \$name));
|
||||||
}
|
}
|
||||||
|
|
||||||
list(\$variables, \$defaults, \$requirements, \$tokens) = self::\$declaredRoutes[\$name];
|
list(\$variables, \$defaults, \$requirements, \$tokens, \$hostnameTokens) = self::\$declaredRoutes[\$name];
|
||||||
|
|
||||||
return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute);
|
return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute, \$hostnameTokens);
|
||||||
}
|
}
|
||||||
EOF;
|
EOF;
|
||||||
}
|
}
|
||||||
|
@ -122,14 +122,14 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
|||||||
// the Route has a cache of its own and is not recompiled as long as it does not get modified
|
// the Route has a cache of its own and is not recompiled as long as it does not get modified
|
||||||
$compiledRoute = $route->compile();
|
$compiledRoute = $route->compile();
|
||||||
|
|
||||||
return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute);
|
return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $absolute, $compiledRoute->getHostnameTokens());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws MissingMandatoryParametersException When route has some missing mandatory parameters
|
* @throws MissingMandatoryParametersException When route has some missing mandatory parameters
|
||||||
* @throws InvalidParameterException When a parameter value is not correct
|
* @throws InvalidParameterException When a parameter value is not correct
|
||||||
*/
|
*/
|
||||||
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute)
|
protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute, $hostnameTokens)
|
||||||
{
|
{
|
||||||
$variables = array_flip($variables);
|
$variables = array_flip($variables);
|
||||||
$mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
|
$mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
|
||||||
@ -191,13 +191,43 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
|||||||
$url .= '?'.$query;
|
$url .= '?'.$query;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->context->getHost()) {
|
if ($host = $this->context->getHost()) {
|
||||||
$scheme = $this->context->getScheme();
|
$scheme = $this->context->getScheme();
|
||||||
if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) {
|
if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) {
|
||||||
$absolute = true;
|
$absolute = true;
|
||||||
$scheme = $req;
|
$scheme = $req;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($hostnameTokens) {
|
||||||
|
$routeHost = '';
|
||||||
|
foreach ($hostnameTokens as $token) {
|
||||||
|
if ('variable' === $token[0]) {
|
||||||
|
if (null !== $this->strictRequirements && !preg_match('#^'.$token[2].'$#', $mergedParams[$token[3]])) {
|
||||||
|
$message = sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $mergedParams[$token[3]]);
|
||||||
|
|
||||||
|
if ($this->strictRequirements) {
|
||||||
|
throw new InvalidParameterException($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->logger) {
|
||||||
|
$this->logger->err($message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$routeHost = $token[1].$mergedParams[$token[3]].$routeHost;
|
||||||
|
} elseif ('text' === $token[0]) {
|
||||||
|
$routeHost = $token[1].$routeHost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($routeHost != $host) {
|
||||||
|
$host = $routeHost;
|
||||||
|
$absolute = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($absolute) {
|
if ($absolute) {
|
||||||
$port = '';
|
$port = '';
|
||||||
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
|
if ('http' === $scheme && 80 != $this->context->getHttpPort()) {
|
||||||
@ -206,7 +236,7 @@ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInt
|
|||||||
$port = ':'.$this->context->getHttpsPort();
|
$port = ':'.$this->context->getHttpsPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
$url = $scheme.'://'.$this->context->getHost().$port.$url;
|
$url = $scheme.'://'.$host.$port.$url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
|||||||
'requirements' => array(),
|
'requirements' => array(),
|
||||||
'options' => array(),
|
'options' => array(),
|
||||||
'defaults' => array(),
|
'defaults' => array(),
|
||||||
|
'hostname_pattern' => '',
|
||||||
);
|
);
|
||||||
|
|
||||||
$class = new \ReflectionClass($class);
|
$class = new \ReflectionClass($class);
|
||||||
@ -124,6 +125,10 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
|||||||
if (null !== $annot->getDefaults()) {
|
if (null !== $annot->getDefaults()) {
|
||||||
$globals['defaults'] = $annot->getDefaults();
|
$globals['defaults'] = $annot->getDefaults();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null !== $annot->getHostnamePattern()) {
|
||||||
|
$globals['hostname_pattern'] = $annot->getHostnamePattern();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$collection = new RouteCollection();
|
$collection = new RouteCollection();
|
||||||
@ -148,16 +153,21 @@ abstract class AnnotationClassLoader implements LoaderInterface
|
|||||||
$name = $this->getDefaultRouteName($class, $method);
|
$name = $this->getDefaultRouteName($class, $method);
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaults = array_merge($globals['defaults'], $annot->getDefaults());
|
$defaults = array_replace($globals['defaults'], $annot->getDefaults());
|
||||||
foreach ($method->getParameters() as $param) {
|
foreach ($method->getParameters() as $param) {
|
||||||
if ($param->isOptional()) {
|
if ($param->isOptional()) {
|
||||||
$defaults[$param->getName()] = $param->getDefaultValue();
|
$defaults[$param->getName()] = $param->getDefaultValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$requirements = array_merge($globals['requirements'], $annot->getRequirements());
|
$requirements = array_replace($globals['requirements'], $annot->getRequirements());
|
||||||
$options = array_merge($globals['options'], $annot->getOptions());
|
$options = array_replace($globals['options'], $annot->getOptions());
|
||||||
|
|
||||||
$route = new Route($globals['pattern'].$annot->getPattern(), $defaults, $requirements, $options);
|
$hostnamePattern = $annot->getHostnamePattern();
|
||||||
|
if (null === $hostnamePattern) {
|
||||||
|
$hostnamePattern = $globals['hostname_pattern'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$route = new Route($globals['pattern'].$annot->getPattern(), $defaults, $requirements, $options, $hostnamePattern);
|
||||||
|
|
||||||
$this->configureRoute($route, $class, $method, $annot);
|
$this->configureRoute($route, $class, $method, $annot);
|
||||||
|
|
||||||
|
@ -73,9 +73,10 @@ class XmlFileLoader extends FileLoader
|
|||||||
$this->parseRoute($collection, $node, $path);
|
$this->parseRoute($collection, $node, $path);
|
||||||
break;
|
break;
|
||||||
case 'import':
|
case 'import':
|
||||||
$resource = (string) $node->getAttribute('resource');
|
$resource = $node->getAttribute('resource');
|
||||||
$type = (string) $node->getAttribute('type');
|
$type = $node->getAttribute('type');
|
||||||
$prefix = (string) $node->getAttribute('prefix');
|
$prefix = $node->getAttribute('prefix');
|
||||||
|
$hostnamePattern = $node->getAttribute('hostname-pattern');
|
||||||
|
|
||||||
$defaults = array();
|
$defaults = array();
|
||||||
$requirements = array();
|
$requirements = array();
|
||||||
@ -88,13 +89,13 @@ class XmlFileLoader extends FileLoader
|
|||||||
|
|
||||||
switch ($n->tagName) {
|
switch ($n->tagName) {
|
||||||
case 'default':
|
case 'default':
|
||||||
$defaults[(string) $n->getAttribute('key')] = trim((string) $n->nodeValue);
|
$defaults[$n->getAttribute('key')] = trim($n->nodeValue);
|
||||||
break;
|
break;
|
||||||
case 'requirement':
|
case 'requirement':
|
||||||
$requirements[(string) $n->getAttribute('key')] = trim((string) $n->nodeValue);
|
$requirements[$n->getAttribute('key')] = trim($n->nodeValue);
|
||||||
break;
|
break;
|
||||||
case 'option':
|
case 'option':
|
||||||
$options[(string) $n->getAttribute('key')] = trim((string) $n->nodeValue);
|
$options[$n->getAttribute('key')] = trim($n->nodeValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $n->tagName));
|
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $n->tagName));
|
||||||
@ -102,7 +103,7 @@ class XmlFileLoader extends FileLoader
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->setCurrentDir(dirname($path));
|
$this->setCurrentDir(dirname($path));
|
||||||
$collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix, $defaults, $requirements, $options);
|
$collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix, $defaults, $requirements, $options, $hostnamePattern);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
|
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
|
||||||
@ -141,22 +142,22 @@ class XmlFileLoader extends FileLoader
|
|||||||
|
|
||||||
switch ($node->tagName) {
|
switch ($node->tagName) {
|
||||||
case 'default':
|
case 'default':
|
||||||
$defaults[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue);
|
$defaults[$node->getAttribute('key')] = trim((string) $node->nodeValue);
|
||||||
break;
|
break;
|
||||||
case 'option':
|
case 'option':
|
||||||
$options[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue);
|
$options[$node->getAttribute('key')] = trim((string) $node->nodeValue);
|
||||||
break;
|
break;
|
||||||
case 'requirement':
|
case 'requirement':
|
||||||
$requirements[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue);
|
$requirements[$node->getAttribute('key')] = trim((string) $node->nodeValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
|
throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = new Route((string) $definition->getAttribute('pattern'), $defaults, $requirements, $options);
|
$route = new Route($definition->getAttribute('pattern'), $defaults, $requirements, $options, $definition->getAttribute('hostname-pattern'));
|
||||||
|
|
||||||
$collection->add((string) $definition->getAttribute('id'), $route);
|
$collection->add($definition->getAttribute('id'), $route);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,7 +27,7 @@ use Symfony\Component\Config\Loader\FileLoader;
|
|||||||
class YamlFileLoader extends FileLoader
|
class YamlFileLoader extends FileLoader
|
||||||
{
|
{
|
||||||
private static $availableKeys = array(
|
private static $availableKeys = array(
|
||||||
'type', 'resource', 'prefix', 'pattern', 'options', 'defaults', 'requirements'
|
'type', 'resource', 'prefix', 'pattern', 'options', 'defaults', 'requirements', 'hostname_pattern',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -70,9 +70,10 @@ class YamlFileLoader extends FileLoader
|
|||||||
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
||||||
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
||||||
$options = isset($config['options']) ? $config['options'] : array();
|
$options = isset($config['options']) ? $config['options'] : array();
|
||||||
|
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : '';
|
||||||
|
|
||||||
$this->setCurrentDir(dirname($path));
|
$this->setCurrentDir(dirname($path));
|
||||||
$collection->addCollection($this->import($config['resource'], $type, false, $file), $prefix, $defaults, $requirements, $options);
|
$collection->addCollection($this->import($config['resource'], $type, false, $file), $prefix, $defaults, $requirements, $options, $hostnamePattern);
|
||||||
} else {
|
} else {
|
||||||
$this->parseRoute($collection, $name, $config, $path);
|
$this->parseRoute($collection, $name, $config, $path);
|
||||||
}
|
}
|
||||||
@ -106,12 +107,13 @@ class YamlFileLoader extends FileLoader
|
|||||||
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
$defaults = isset($config['defaults']) ? $config['defaults'] : array();
|
||||||
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
$requirements = isset($config['requirements']) ? $config['requirements'] : array();
|
||||||
$options = isset($config['options']) ? $config['options'] : array();
|
$options = isset($config['options']) ? $config['options'] : array();
|
||||||
|
$hostnamePattern = isset($config['hostname_pattern']) ? $config['hostname_pattern'] : null;
|
||||||
|
|
||||||
if (!isset($config['pattern'])) {
|
if (!isset($config['pattern'])) {
|
||||||
throw new \InvalidArgumentException(sprintf('You must define a "pattern" for the "%s" route.', $name));
|
throw new \InvalidArgumentException(sprintf('You must define a "pattern" for the "%s" route.', $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
$route = new Route($config['pattern'], $defaults, $requirements, $options);
|
$route = new Route($config['pattern'], $defaults, $requirements, $options, $hostnamePattern);
|
||||||
|
|
||||||
$collection->add($name, $route);
|
$collection->add($name, $route);
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
<xsd:attribute name="id" type="xsd:string" />
|
<xsd:attribute name="id" type="xsd:string" />
|
||||||
<xsd:attribute name="pattern" type="xsd:string" />
|
<xsd:attribute name="pattern" type="xsd:string" />
|
||||||
|
<xsd:attribute name="hostname-pattern" type="xsd:string" />
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
<xsd:complexType name="import">
|
<xsd:complexType name="import">
|
||||||
@ -36,6 +37,7 @@
|
|||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
<xsd:attribute name="prefix" type="xsd:string" />
|
<xsd:attribute name="prefix" type="xsd:string" />
|
||||||
<xsd:attribute name="class" type="xsd:string" />
|
<xsd:attribute name="class" type="xsd:string" />
|
||||||
|
<xsd:attribute name="hostname-pattern" type="xsd:string" />
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
|
|
||||||
<xsd:complexType name="element" mixed="true">
|
<xsd:complexType name="element" mixed="true">
|
||||||
|
@ -46,17 +46,55 @@ class ApacheMatcherDumper extends MatcherDumper
|
|||||||
|
|
||||||
$rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
|
$rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
|
||||||
$methodVars = array();
|
$methodVars = array();
|
||||||
|
$hostnameRegexUnique = 0;
|
||||||
|
$prevHosnameRegex = '';
|
||||||
|
|
||||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||||
$rules[] = $this->dumpRoute($name, $route, $options);
|
|
||||||
$methodVars = array_merge($methodVars, $this->getRouteMethods($route));
|
$compiledRoute = $route->compile();
|
||||||
|
$hostnameRegex = $compiledRoute->getHostnameRegex();
|
||||||
|
|
||||||
|
if (null !== $hostnameRegex && $prevHosnameRegex !== $hostnameRegex) {
|
||||||
|
|
||||||
|
$prevHosnameRegex = $hostnameRegex;
|
||||||
|
$hostnameRegexUnique++;
|
||||||
|
|
||||||
|
$rule = array();
|
||||||
|
|
||||||
|
$regex = $this->regexToApacheRegex($hostnameRegex);
|
||||||
|
$regex = self::escape($regex, ' ', '\\');
|
||||||
|
|
||||||
|
$rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
|
||||||
|
|
||||||
|
$variables = array();
|
||||||
|
$variables[] = sprintf('E=__ROUTING_hostname_%s:1', $hostnameRegexUnique);
|
||||||
|
|
||||||
|
foreach ($compiledRoute->getHostnameVariables() as $i => $variable) {
|
||||||
|
$variables[] = sprintf('E=__ROUTING_hostname_%s_%s:%%%d', $hostnameRegexUnique, $variable, $i+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$variables = implode(',', $variables);
|
||||||
|
|
||||||
|
$rule[] = sprintf('RewriteRule .? - [%s]', $variables);
|
||||||
|
|
||||||
|
$rules[] = implode("\n", $rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules[] = $this->dumpRoute($name, $route, $options, $hostnameRegexUnique);
|
||||||
|
|
||||||
|
if ($req = $route->getRequirement('_method')) {
|
||||||
|
$methods = explode('|', strtoupper($req));
|
||||||
|
$methodVars = array_merge($methodVars, $methods);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (0 < count($methodVars)) {
|
if (0 < count($methodVars)) {
|
||||||
$rule = array('# 405 Method Not Allowed');
|
$rule = array('# 405 Method Not Allowed');
|
||||||
$methodVars = array_values(array_unique($methodVars));
|
$methodVars = array_values(array_unique($methodVars));
|
||||||
|
if (in_array('GET', $methodVars) && !in_array('HEAD', $methodVars)) {
|
||||||
|
$methodVars[] = 'HEAD';
|
||||||
|
}
|
||||||
foreach ($methodVars as $i => $methodVar) {
|
foreach ($methodVars as $i => $methodVar) {
|
||||||
$rule[] = sprintf('RewriteCond %%{_ROUTING_allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
|
$rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
|
||||||
}
|
}
|
||||||
$rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
|
$rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
|
||||||
|
|
||||||
@ -66,7 +104,17 @@ class ApacheMatcherDumper extends MatcherDumper
|
|||||||
return implode("\n\n", $rules)."\n";
|
return implode("\n\n", $rules)."\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
private function dumpRoute($name, $route, array $options)
|
/**
|
||||||
|
* Dumps a single route
|
||||||
|
*
|
||||||
|
* @param string $name Route name
|
||||||
|
* @param Route $route The route
|
||||||
|
* @param array $options Options
|
||||||
|
* @param bool $hostnameRegexUnique Unique identifier for the hostname regex
|
||||||
|
*
|
||||||
|
* @return string The compiled route
|
||||||
|
*/
|
||||||
|
private function dumpRoute($name, $route, array $options, $hostnameRegexUnique)
|
||||||
{
|
{
|
||||||
$compiledRoute = $route->compile();
|
$compiledRoute = $route->compile();
|
||||||
|
|
||||||
@ -79,7 +127,10 @@ class ApacheMatcherDumper extends MatcherDumper
|
|||||||
$hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
|
$hasTrailingSlash = (!$methods || in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
|
||||||
|
|
||||||
$variables = array('E=_ROUTING_route:'.$name);
|
$variables = array('E=_ROUTING_route:'.$name);
|
||||||
foreach ($compiledRoute->getVariables() as $i => $variable) {
|
foreach ($compiledRoute->getHostnameVariables() as $variable) {
|
||||||
|
$variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_hostname_%s_%s}', $variable, $hostnameRegexUnique, $variable);
|
||||||
|
}
|
||||||
|
foreach ($compiledRoute->getPathVariables() as $i => $variable) {
|
||||||
$variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
|
$variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
|
||||||
}
|
}
|
||||||
foreach ($route->getDefaults() as $key => $value) {
|
foreach ($route->getDefaults() as $key => $value) {
|
||||||
@ -98,10 +149,13 @@ class ApacheMatcherDumper extends MatcherDumper
|
|||||||
if (0 < count($methods)) {
|
if (0 < count($methods)) {
|
||||||
$allow = array();
|
$allow = array();
|
||||||
foreach ($methods as $method) {
|
foreach ($methods as $method) {
|
||||||
$methodVars[] = $method;
|
|
||||||
$allow[] = 'E=_ROUTING_allow_'.$method.':1';
|
$allow[] = 'E=_ROUTING_allow_'.$method.':1';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($hostnameRegex = $compiledRoute->getHostnameRegex()) {
|
||||||
|
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_hostname_%s} =1", $hostnameRegexUnique);
|
||||||
|
}
|
||||||
|
|
||||||
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
|
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
|
||||||
$rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
|
$rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods));
|
||||||
$rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
|
$rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
|
||||||
@ -109,11 +163,21 @@ class ApacheMatcherDumper extends MatcherDumper
|
|||||||
|
|
||||||
// redirect with trailing slash appended
|
// redirect with trailing slash appended
|
||||||
if ($hasTrailingSlash) {
|
if ($hasTrailingSlash) {
|
||||||
|
|
||||||
|
if ($hostnameRegex = $compiledRoute->getHostnameRegex()) {
|
||||||
|
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_hostname_%s} =1", $hostnameRegexUnique);
|
||||||
|
}
|
||||||
|
|
||||||
$rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
|
$rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
|
||||||
$rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
|
$rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
|
||||||
}
|
}
|
||||||
|
|
||||||
// the main rule
|
// the main rule
|
||||||
|
|
||||||
|
if ($hostnameRegex = $compiledRoute->getHostnameRegex()) {
|
||||||
|
$rule[] = sprintf("RewriteCond %%{ENV:__ROUTING_hostname_%s} =1", $hostnameRegexUnique);
|
||||||
|
}
|
||||||
|
|
||||||
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
|
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
|
||||||
$rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
|
$rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ class DumperCollection implements \IteratorAggregate
|
|||||||
{
|
{
|
||||||
private $parent;
|
private $parent;
|
||||||
private $children = array();
|
private $children = array();
|
||||||
|
private $attributes = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the children routes and collections.
|
* Returns the children routes and collections.
|
||||||
@ -45,7 +46,7 @@ class DumperCollection implements \IteratorAggregate
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets children
|
* Sets children.
|
||||||
*
|
*
|
||||||
* @param array $children The children
|
* @param array $children The children
|
||||||
*/
|
*/
|
||||||
@ -98,4 +99,50 @@ class DumperCollection implements \IteratorAggregate
|
|||||||
{
|
{
|
||||||
$this->parent = $parent;
|
$this->parent = $parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the attribute is defined.
|
||||||
|
*
|
||||||
|
* @param string $name The attribute name
|
||||||
|
*
|
||||||
|
* @return Boolean true if the attribute is defined, false otherwise
|
||||||
|
*/
|
||||||
|
public function hasAttribute($name)
|
||||||
|
{
|
||||||
|
return array_key_exists($name, $this->attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an attribute by name.
|
||||||
|
*
|
||||||
|
* @param string $name The attribute name
|
||||||
|
* @param mixed $default Default value is the attribute doesn't exist
|
||||||
|
*
|
||||||
|
* @return mixed The attribute value
|
||||||
|
*/
|
||||||
|
public function getAttribute($name, $default = null)
|
||||||
|
{
|
||||||
|
return $this->hasAttribute($name) ? $this->attributes[$name] : $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an attribute by name.
|
||||||
|
*
|
||||||
|
* @param string $name The attribute name
|
||||||
|
* @param mixed $value The attribute value
|
||||||
|
*/
|
||||||
|
public function setAttribute($name, $value)
|
||||||
|
{
|
||||||
|
$this->attributes[$name] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets multiple attributes.
|
||||||
|
*
|
||||||
|
* @param array $attributes The attributes
|
||||||
|
*/
|
||||||
|
public function setAttributes($attributes)
|
||||||
|
{
|
||||||
|
$this->attributes = $attributes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class PhpMatcherDumper extends MatcherDumper
|
|||||||
*/
|
*/
|
||||||
public function dump(array $options = array())
|
public function dump(array $options = array())
|
||||||
{
|
{
|
||||||
$options = array_merge(array(
|
$options = array_replace(array(
|
||||||
'class' => 'ProjectUrlMatcher',
|
'class' => 'ProjectUrlMatcher',
|
||||||
'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||||
), $options);
|
), $options);
|
||||||
@ -109,10 +109,36 @@ EOF;
|
|||||||
*/
|
*/
|
||||||
private function compileRoutes(RouteCollection $routes, $supportsRedirections)
|
private function compileRoutes(RouteCollection $routes, $supportsRedirections)
|
||||||
{
|
{
|
||||||
$collection = $this->flattenRouteCollection($routes);
|
$fetchedHostname = false;
|
||||||
$tree = $this->buildPrefixTree($collection);
|
|
||||||
|
|
||||||
return $this->compilePrefixRoutes($tree, $supportsRedirections);
|
$routes = $this->flattenRouteCollection($routes);
|
||||||
|
$groups = $this->groupRoutesByHostnameRegex($routes);
|
||||||
|
$code = '';
|
||||||
|
|
||||||
|
foreach ($groups as $collection) {
|
||||||
|
if (null !== $regex = $collection->getAttribute('hostname_regex')) {
|
||||||
|
if (!$fetchedHostname) {
|
||||||
|
$code .= " \$hostname = \$this->context->getHost();\n\n";
|
||||||
|
$fetchedHostname = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$code .= sprintf(" if (preg_match(%s, \$hostname, \$hostnameMatches)) {\n", var_export($regex, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
$tree = $this->buildPrefixTree($collection);
|
||||||
|
$groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections);
|
||||||
|
|
||||||
|
if (null !== $regex) {
|
||||||
|
// apply extra indention at each line (except empty ones)
|
||||||
|
$groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode);
|
||||||
|
$code .= $groupCode;
|
||||||
|
$code .= " }\n\n";
|
||||||
|
} else {
|
||||||
|
$code .= $groupCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,6 +197,7 @@ EOF;
|
|||||||
$conditions = array();
|
$conditions = array();
|
||||||
$hasTrailingSlash = false;
|
$hasTrailingSlash = false;
|
||||||
$matches = false;
|
$matches = false;
|
||||||
|
$hostnameMatches = false;
|
||||||
$methods = array();
|
$methods = array();
|
||||||
|
|
||||||
if ($req = $route->getRequirement('_method')) {
|
if ($req = $route->getRequirement('_method')) {
|
||||||
@ -183,7 +210,7 @@ EOF;
|
|||||||
|
|
||||||
$supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
|
$supportsTrailingSlash = $supportsRedirections && (!$methods || in_array('HEAD', $methods));
|
||||||
|
|
||||||
if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
|
if (!count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
|
||||||
if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
|
if ($supportsTrailingSlash && substr($m['url'], -1) === '/') {
|
||||||
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
|
$conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
|
||||||
$hasTrailingSlash = true;
|
$hasTrailingSlash = true;
|
||||||
@ -205,6 +232,10 @@ EOF;
|
|||||||
$matches = true;
|
$matches = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($compiledRoute->getHostnameVariables()) {
|
||||||
|
$hostnameMatches = true;
|
||||||
|
}
|
||||||
|
|
||||||
$conditions = implode(' && ', $conditions);
|
$conditions = implode(' && ', $conditions);
|
||||||
|
|
||||||
$code .= <<<EOF
|
$code .= <<<EOF
|
||||||
@ -263,14 +294,21 @@ EOF;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// optimize parameters array
|
// optimize parameters array
|
||||||
if (true === $matches && $route->getDefaults()) {
|
if ($matches || $hostnameMatches) {
|
||||||
$code .= sprintf(" return array_merge(\$this->mergeDefaults(\$matches, %s), array('_route' => '%s'));\n"
|
$vars = array();
|
||||||
, str_replace("\n", '', var_export($route->getDefaults(), true)), $name);
|
if ($hostnameMatches) {
|
||||||
} elseif (true === $matches) {
|
$vars[] = '$hostnameMatches';
|
||||||
$code .= sprintf(" \$matches['_route'] = '%s';\n\n", $name);
|
}
|
||||||
$code .= " return \$matches;\n";
|
if ($matches) {
|
||||||
|
$vars[] = '$matches';
|
||||||
|
}
|
||||||
|
$vars[] = "array('_route' => '$name')";
|
||||||
|
|
||||||
|
$code .= sprintf(" return \$this->mergeDefaults(array_replace(%s), %s);\n"
|
||||||
|
, implode(', ', $vars), str_replace("\n", '', var_export($route->getDefaults(), true)));
|
||||||
|
|
||||||
} elseif ($route->getDefaults()) {
|
} elseif ($route->getDefaults()) {
|
||||||
$code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_merge($route->getDefaults(), array('_route' => $name)), true)));
|
$code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
|
||||||
} else {
|
} else {
|
||||||
$code .= sprintf(" return array('_route' => '%s');\n", $name);
|
$code .= sprintf(" return array('_route' => '%s');\n", $name);
|
||||||
}
|
}
|
||||||
@ -284,7 +322,7 @@ EOF;
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens a tree of routes to a single collection
|
* Flattens a tree of routes to a single collection.
|
||||||
*
|
*
|
||||||
* @param RouteCollection $routes Collection of routes
|
* @param RouteCollection $routes Collection of routes
|
||||||
* @param DumperCollection $to A DumperCollection to add routes to
|
* @param DumperCollection $to A DumperCollection to add routes to
|
||||||
@ -308,6 +346,36 @@ EOF;
|
|||||||
return $to;
|
return $to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Groups consecutive routes having the same hostname regex.
|
||||||
|
*
|
||||||
|
* The results is a collection of collections of routes having the same hostname regex.
|
||||||
|
*
|
||||||
|
* @param DumperCollection $routes Flat collection of DumperRoutes
|
||||||
|
*
|
||||||
|
* @return DumperCollection A collection with routes grouped by hostname regex in sub-collections
|
||||||
|
*/
|
||||||
|
private function groupRoutesByHostnameRegex(DumperCollection $routes)
|
||||||
|
{
|
||||||
|
$groups = new DumperCollection();
|
||||||
|
|
||||||
|
$currentGroup = new DumperCollection();
|
||||||
|
$currentGroup->setAttribute('hostname_regex', null);
|
||||||
|
$groups->add($currentGroup);
|
||||||
|
|
||||||
|
foreach ($routes as $route) {
|
||||||
|
$hostnameRegex = $route->getRoute()->compile()->getHostnameRegex();
|
||||||
|
if ($currentGroup->getAttribute('hostname_regex') !== $hostnameRegex) {
|
||||||
|
$currentGroup = new DumperCollection();
|
||||||
|
$currentGroup->setAttribute('hostname_regex', $hostnameRegex);
|
||||||
|
$groups->add($currentGroup);
|
||||||
|
}
|
||||||
|
$currentGroup->add($route);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Organizes the routes into a prefix tree.
|
* Organizes the routes into a prefix tree.
|
||||||
*
|
*
|
||||||
@ -331,5 +399,4 @@ EOF;
|
|||||||
|
|
||||||
return $tree;
|
return $tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,11 @@ class UrlMatcher implements UrlMatcherInterface
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hostnameMatches = array();
|
||||||
|
if ($compiledRoute->getHostnameRegex() && !preg_match($compiledRoute->getHostnameRegex(), $this->context->getHost(), $hostnameMatches)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// check HTTP method requirement
|
// check HTTP method requirement
|
||||||
if ($req = $route->getRequirement('_method')) {
|
if ($req = $route->getRequirement('_method')) {
|
||||||
// HEAD and GET are equivalent as per RFC
|
// HEAD and GET are equivalent as per RFC
|
||||||
@ -142,7 +147,7 @@ class UrlMatcher implements UrlMatcherInterface
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name));
|
return $this->mergeDefaults(array_replace($matches, $hostnameMatches, array('_route' => $name)), $route->getDefaults());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ class Route implements \Serializable
|
|||||||
private $requirements;
|
private $requirements;
|
||||||
private $options;
|
private $options;
|
||||||
private $compiled;
|
private $compiled;
|
||||||
|
private $hostnamePattern;
|
||||||
|
|
||||||
private static $compilers = array();
|
private static $compilers = array();
|
||||||
|
|
||||||
@ -39,15 +40,17 @@ class Route implements \Serializable
|
|||||||
* @param array $defaults An array of default parameter values
|
* @param array $defaults An array of default parameter values
|
||||||
* @param array $requirements An array of requirements for parameters (regexes)
|
* @param array $requirements An array of requirements for parameters (regexes)
|
||||||
* @param array $options An array of options
|
* @param array $options An array of options
|
||||||
|
* @param string $hostname The hostname pattern to match
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function __construct($pattern, array $defaults = array(), array $requirements = array(), array $options = array())
|
public function __construct($pattern, array $defaults = array(), array $requirements = array(), array $options = array(), $hostnamePattern = '')
|
||||||
{
|
{
|
||||||
$this->setPattern($pattern);
|
$this->setPattern($pattern);
|
||||||
$this->setDefaults($defaults);
|
$this->setDefaults($defaults);
|
||||||
$this->setRequirements($requirements);
|
$this->setRequirements($requirements);
|
||||||
$this->setOptions($options);
|
$this->setOptions($options);
|
||||||
|
$this->setHostnamePattern($hostnamePattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __clone()
|
public function __clone()
|
||||||
@ -103,6 +106,28 @@ class Route implements \Serializable
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname pattern.
|
||||||
|
*
|
||||||
|
* @return string The pattern
|
||||||
|
*/
|
||||||
|
public function getHostnamePattern()
|
||||||
|
{
|
||||||
|
return $this->hostnamePattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hostname pattern.
|
||||||
|
*
|
||||||
|
* @param string $pattern The pattern
|
||||||
|
*/
|
||||||
|
public function setHostnamePattern($pattern)
|
||||||
|
{
|
||||||
|
$this->hostnamePattern = (string) $pattern;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the options.
|
* Returns the options.
|
||||||
*
|
*
|
||||||
|
@ -30,6 +30,7 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||||||
private $resources;
|
private $resources;
|
||||||
private $prefix;
|
private $prefix;
|
||||||
private $parent;
|
private $parent;
|
||||||
|
private $hostnamePattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -41,6 +42,7 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||||||
$this->routes = array();
|
$this->routes = array();
|
||||||
$this->resources = array();
|
$this->resources = array();
|
||||||
$this->prefix = '';
|
$this->prefix = '';
|
||||||
|
$this->hostnamePattern = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __clone()
|
public function __clone()
|
||||||
@ -188,12 +190,13 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||||||
* @param array $defaults An array of default values
|
* @param array $defaults An array of default values
|
||||||
* @param array $requirements An array of requirements
|
* @param array $requirements An array of requirements
|
||||||
* @param array $options An array of options
|
* @param array $options An array of options
|
||||||
|
* @param string $hostnamePattern Hostname pattern
|
||||||
*
|
*
|
||||||
* @throws \InvalidArgumentException When the RouteCollection already exists in the tree
|
* @throws \InvalidArgumentException When the RouteCollection already exists in the tree
|
||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
public function addCollection(RouteCollection $collection, $prefix = '', $defaults = array(), $requirements = array(), $options = array())
|
public function addCollection(RouteCollection $collection, $prefix = '', $defaults = array(), $requirements = array(), $options = array(), $hostnamePattern = '')
|
||||||
{
|
{
|
||||||
// prevent infinite loops by recursive referencing
|
// prevent infinite loops by recursive referencing
|
||||||
$root = $this->getRoot();
|
$root = $this->getRoot();
|
||||||
@ -208,6 +211,11 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||||||
// the sub-collection must have the prefix of the parent (current instance) prepended because it does not
|
// the sub-collection must have the prefix of the parent (current instance) prepended because it does not
|
||||||
// necessarily already have it applied (depending on the order RouteCollections are added to each other)
|
// necessarily already have it applied (depending on the order RouteCollections are added to each other)
|
||||||
$collection->addPrefix($this->getPrefix() . $prefix, $defaults, $requirements, $options);
|
$collection->addPrefix($this->getPrefix() . $prefix, $defaults, $requirements, $options);
|
||||||
|
|
||||||
|
if ('' !== $hostnamePattern) {
|
||||||
|
$collection->setHostnamePattern($hostnamePattern);
|
||||||
|
}
|
||||||
|
|
||||||
$this->routes[] = $collection;
|
$this->routes[] = $collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +267,30 @@ class RouteCollection implements \IteratorAggregate, \Countable
|
|||||||
return $this->prefix;
|
return $this->prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the hostname pattern.
|
||||||
|
*
|
||||||
|
* @return string The pattern
|
||||||
|
*/
|
||||||
|
public function getHostnamePattern()
|
||||||
|
{
|
||||||
|
return $this->hostnamePattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the hostname pattern on this collection and all children.
|
||||||
|
*
|
||||||
|
* @param string $pattern The pattern
|
||||||
|
*/
|
||||||
|
public function setHostnamePattern($pattern)
|
||||||
|
{
|
||||||
|
$this->hostnamePattern = (string) $pattern;
|
||||||
|
|
||||||
|
foreach ($this->routes as $name => $route) {
|
||||||
|
$route->setHostnamePattern($pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of resources loaded to build this collection.
|
* Returns an array of resources loaded to build this collection.
|
||||||
*
|
*
|
||||||
|
@ -37,11 +37,57 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
*/
|
*/
|
||||||
public function compile(Route $route)
|
public function compile(Route $route)
|
||||||
{
|
{
|
||||||
|
$staticPrefix = null;
|
||||||
|
$hostnameVariables = array();
|
||||||
|
$pathVariables = array();
|
||||||
|
$variables = array();
|
||||||
|
$tokens = array();
|
||||||
|
$regex = null;
|
||||||
|
$hostnameRegex = null;
|
||||||
|
$hostnameTokens = array();
|
||||||
|
|
||||||
|
if ('' !== $hostnamePattern = $route->getHostnamePattern()) {
|
||||||
|
$result = $this->compilePattern($route, $hostnamePattern, true);
|
||||||
|
|
||||||
|
$hostnameVariables = $result['variables'];
|
||||||
|
$variables = array_merge($variables, $hostnameVariables);
|
||||||
|
|
||||||
|
$hostnameTokens = $result['tokens'];
|
||||||
|
$hostnameRegex = $result['regex'];
|
||||||
|
}
|
||||||
|
|
||||||
$pattern = $route->getPattern();
|
$pattern = $route->getPattern();
|
||||||
|
|
||||||
|
$result = $this->compilePattern($route, $pattern, false);
|
||||||
|
|
||||||
|
$staticPrefix = $result['staticPrefix'];
|
||||||
|
|
||||||
|
$pathVariables = $result['variables'];
|
||||||
|
$variables = array_merge($variables, $pathVariables);
|
||||||
|
|
||||||
|
$tokens = $result['tokens'];
|
||||||
|
$regex = $result['regex'];
|
||||||
|
|
||||||
|
return new CompiledRoute(
|
||||||
|
$staticPrefix,
|
||||||
|
$regex,
|
||||||
|
$tokens,
|
||||||
|
$pathVariables,
|
||||||
|
$hostnameRegex,
|
||||||
|
$hostnameTokens,
|
||||||
|
$hostnameVariables,
|
||||||
|
array_unique($variables)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function compilePattern(Route $route, $pattern, $isHostname)
|
||||||
|
{
|
||||||
|
$len = strlen($pattern);
|
||||||
$tokens = array();
|
$tokens = array();
|
||||||
$variables = array();
|
$variables = array();
|
||||||
$matches = array();
|
$matches = array();
|
||||||
$pos = 0;
|
$pos = 0;
|
||||||
|
$defaultSeparator = $isHostname ? '.' : '/';
|
||||||
|
|
||||||
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
|
// Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable
|
||||||
// in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
|
// in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself.
|
||||||
@ -78,7 +124,11 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
// Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally
|
// Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally
|
||||||
// part of {_format} when generating the URL, e.g. _format = 'mobile.html'.
|
// part of {_format} when generating the URL, e.g. _format = 'mobile.html'.
|
||||||
$nextSeparator = $this->findNextSeparator($followingPattern);
|
$nextSeparator = $this->findNextSeparator($followingPattern);
|
||||||
$regexp = sprintf('[^/%s]+', '/' !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : '');
|
$regexp = sprintf(
|
||||||
|
'[^%s%s]+',
|
||||||
|
preg_quote($defaultSeparator, self::REGEX_DELIMITER),
|
||||||
|
$defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : ''
|
||||||
|
);
|
||||||
if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) {
|
if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) {
|
||||||
// When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
|
// When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive
|
||||||
// quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns.
|
// quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns.
|
||||||
@ -99,6 +149,7 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
|
|
||||||
// find the first optional token
|
// find the first optional token
|
||||||
$firstOptional = INF;
|
$firstOptional = INF;
|
||||||
|
if (!$isHostname) {
|
||||||
for ($i = count($tokens) - 1; $i >= 0; $i--) {
|
for ($i = count($tokens) - 1; $i >= 0; $i--) {
|
||||||
$token = $tokens[$i];
|
$token = $tokens[$i];
|
||||||
if ('variable' === $token[0] && $route->hasDefault($token[3])) {
|
if ('variable' === $token[0] && $route->hasDefault($token[3])) {
|
||||||
@ -107,6 +158,7 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// compute the matching regexp
|
// compute the matching regexp
|
||||||
$regexp = '';
|
$regexp = '';
|
||||||
@ -114,11 +166,11 @@ class RouteCompiler implements RouteCompilerInterface
|
|||||||
$regexp .= $this->computeRegexp($tokens, $i, $firstOptional);
|
$regexp .= $this->computeRegexp($tokens, $i, $firstOptional);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompiledRoute(
|
return array(
|
||||||
'text' === $tokens[0][0] ? $tokens[0][1] : '',
|
'staticPrefix' => 'text' === $tokens[0][0] ? $tokens[0][1] : '',
|
||||||
self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
|
'regex' => self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'s',
|
||||||
array_reverse($tokens),
|
'tokens' => array_reverse($tokens),
|
||||||
$variables
|
'variables' => $variables,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,8 @@ class RouteTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('requirements', array('_method' => 'GET'), 'getRequirements'),
|
array('requirements', array('_method' => 'GET'), 'getRequirements'),
|
||||||
array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'),
|
array('options', array('compiler_class' => 'RouteCompiler'), 'getOptions'),
|
||||||
array('name', 'blog_index', 'getName'),
|
array('name', 'blog_index', 'getName'),
|
||||||
array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults')
|
array('defaults', array('_controller' => 'MyBlogBundle:Blog:index'), 'getDefaults'),
|
||||||
|
array('hostname_pattern', array('{locale}.example.com'), 'getHostnamePattern')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,10 @@ class CompiledRouteTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
public function testAccessors()
|
public function testAccessors()
|
||||||
{
|
{
|
||||||
$compiled = new CompiledRoute('prefix', 'regex', array('tokens'), array('variables'));
|
$compiled = new CompiledRoute('prefix', 'regex', array('tokens'), array(), array(), array(), array(), array('variables'));
|
||||||
$this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its first argument');
|
$this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its second argument');
|
||||||
$this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its second argument');
|
$this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its third argument');
|
||||||
$this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its third argument');
|
$this->assertEquals(array('tokens'), $compiled->getTokens(), '__construct() takes an array of tokens as its fourth argument');
|
||||||
$this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its forth argument');
|
$this->assertEquals(array('variables'), $compiled->getVariables(), '__construct() takes an array of variables as its ninth argument');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,8 +68,96 @@ RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz6,E=_ROUTING_default_foo:bar\
|
|||||||
RewriteCond %{REQUEST_URI} ^/te\ st/baz$
|
RewriteCond %{REQUEST_URI} ^/te\ st/baz$
|
||||||
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz7]
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz7]
|
||||||
|
|
||||||
|
# baz8
|
||||||
|
RewriteCond %{REQUEST_URI} ^/te\\\ st/baz$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz8]
|
||||||
|
|
||||||
|
# baz9
|
||||||
|
RewriteCond %{REQUEST_URI} ^/test/(te\\\ st)$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:baz9,E=_ROUTING_param_baz:%1]
|
||||||
|
|
||||||
|
RewriteCond %{HTTP:Host} ^a\.example\.com$
|
||||||
|
RewriteRule .? - [E=__ROUTING_hostname_1:1]
|
||||||
|
|
||||||
|
# route1
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_1} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route1$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route1]
|
||||||
|
|
||||||
|
# route2
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_1} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/c2/route2$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route2]
|
||||||
|
|
||||||
|
RewriteCond %{HTTP:Host} ^b\.example\.com$
|
||||||
|
RewriteRule .? - [E=__ROUTING_hostname_2:1]
|
||||||
|
|
||||||
|
# route3
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_2} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/c2/route3$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route3]
|
||||||
|
|
||||||
|
RewriteCond %{HTTP:Host} ^a\.example\.com$
|
||||||
|
RewriteRule .? - [E=__ROUTING_hostname_3:1]
|
||||||
|
|
||||||
|
# route4
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_3} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route4$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route4]
|
||||||
|
|
||||||
|
RewriteCond %{HTTP:Host} ^c\.example\.com$
|
||||||
|
RewriteRule .? - [E=__ROUTING_hostname_4:1]
|
||||||
|
|
||||||
|
# route5
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_4} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route5$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route5]
|
||||||
|
|
||||||
|
# route6
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route6$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route6]
|
||||||
|
|
||||||
|
RewriteCond %{HTTP:Host} ^([^\.]++)\.example\.com$
|
||||||
|
RewriteRule .? - [E=__ROUTING_hostname_5:1,E=__ROUTING_hostname_5_var1:%1]
|
||||||
|
|
||||||
|
# route11
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_5} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route11$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route11,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1}]
|
||||||
|
|
||||||
|
# route12
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_5} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route12$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route12,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1},E=_ROUTING_default_var1:val]
|
||||||
|
|
||||||
|
# route13
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_5} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route13/([^/]++)$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route13,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1},E=_ROUTING_param_name:%1]
|
||||||
|
|
||||||
|
# route14
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_5} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route14/([^/]++)$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route14,E=_ROUTING_param_var1:%{ENV:__ROUTING_hostname_5_var1},E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
|
||||||
|
|
||||||
|
RewriteCond %{HTTP:Host} ^c\.example\.com$
|
||||||
|
RewriteRule .? - [E=__ROUTING_hostname_6:1]
|
||||||
|
|
||||||
|
# route15
|
||||||
|
RewriteCond %{ENV:__ROUTING_hostname_6} =1
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route15/([^/]++)$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route15,E=_ROUTING_param_name:%1]
|
||||||
|
|
||||||
|
# route16
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route16/([^/]++)$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route16,E=_ROUTING_param_name:%1,E=_ROUTING_default_var1:val]
|
||||||
|
|
||||||
|
# route17
|
||||||
|
RewriteCond %{REQUEST_URI} ^/route17$
|
||||||
|
RewriteRule .* app.php [QSA,L,E=_ROUTING_route:route17]
|
||||||
|
|
||||||
# 405 Method Not Allowed
|
# 405 Method Not Allowed
|
||||||
RewriteCond %{_ROUTING_allow_GET} !-z [OR]
|
RewriteCond %{ENV:_ROUTING__allow_GET} =1 [OR]
|
||||||
RewriteCond %{_ROUTING_allow_HEAD} !-z [OR]
|
RewriteCond %{ENV:_ROUTING__allow_HEAD} =1 [OR]
|
||||||
RewriteCond %{_ROUTING_allow_POST} !-z
|
RewriteCond %{ENV:_ROUTING__allow_POST} =1
|
||||||
RewriteRule .* app.php [QSA,L]
|
RewriteRule .* app.php [QSA,L]
|
||||||
|
@ -27,7 +27,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
|
|
||||||
// foo
|
// foo
|
||||||
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?<bar>baz|symfony)$#s', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?<bar>baz|symfony)$#s', $pathinfo, $matches)) {
|
||||||
return array_merge($this->mergeDefaults($matches, array ( 'def' => 'test',)), array('_route' => 'foo'));
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($pathinfo, '/bar')) {
|
if (0 === strpos($pathinfo, '/bar')) {
|
||||||
@ -38,9 +38,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
goto not_bar;
|
goto not_bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'bar';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_bar:
|
not_bar:
|
||||||
|
|
||||||
@ -51,9 +49,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
goto not_barhead;
|
goto not_barhead;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'barhead';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_barhead:
|
not_barhead:
|
||||||
|
|
||||||
@ -80,9 +76,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
|
|
||||||
// baz4
|
// baz4
|
||||||
if (preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/test/(?<foo>[^/]++)/$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'baz4';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// baz5
|
// baz5
|
||||||
@ -92,9 +86,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
goto not_baz5;
|
goto not_baz5;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'baz5';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_baz5:
|
not_baz5:
|
||||||
|
|
||||||
@ -105,9 +97,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
goto not_bazbaz6;
|
goto not_bazbaz6;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'baz.baz6';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_bazbaz6:
|
not_bazbaz6:
|
||||||
|
|
||||||
@ -120,9 +110,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
|
|
||||||
// quoter
|
// quoter
|
||||||
if (preg_match('#^/(?<quoter>[\']+)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/(?<quoter>[\']+)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'quoter';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// space
|
// space
|
||||||
@ -134,40 +122,30 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||||
// foo1
|
// foo1
|
||||||
if (preg_match('#^/a/b\'b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo1';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bar1
|
// bar1
|
||||||
if (preg_match('#^/a/b\'b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'bar1';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// overridden
|
// overridden
|
||||||
if (preg_match('#^/a/(?<var>.*)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/(?<var>.*)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'overridden';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||||
// foo2
|
// foo2
|
||||||
if (preg_match('#^/a/b\'b/(?<foo1>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<foo1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo2';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bar2
|
// bar2
|
||||||
if (preg_match('#^/a/b\'b/(?<bar1>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<bar1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'bar2';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -177,7 +155,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
if (0 === strpos($pathinfo, '/multi')) {
|
if (0 === strpos($pathinfo, '/multi')) {
|
||||||
// helloWorld
|
// helloWorld
|
||||||
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?<who>[^/]++))?$#s', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?<who>[^/]++))?$#s', $pathinfo, $matches)) {
|
||||||
return array_merge($this->mergeDefaults($matches, array ( 'who' => 'World!',)), array('_route' => 'helloWorld'));
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',));
|
||||||
}
|
}
|
||||||
|
|
||||||
// overridden2
|
// overridden2
|
||||||
@ -194,19 +172,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
|
|
||||||
// foo3
|
// foo3
|
||||||
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo3';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bar3
|
// bar3
|
||||||
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'bar3';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($pathinfo, '/a')) {
|
|
||||||
if (0 === strpos($pathinfo, '/aba')) {
|
if (0 === strpos($pathinfo, '/aba')) {
|
||||||
// ababa
|
// ababa
|
||||||
if ($pathinfo === '/ababa') {
|
if ($pathinfo === '/ababa') {
|
||||||
@ -215,13 +188,103 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
|
|
||||||
// foo4
|
// foo4
|
||||||
if (preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo4';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hostname = $this->context->getHost();
|
||||||
|
|
||||||
|
if (preg_match('#^a\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route1
|
||||||
|
if ($pathinfo === '/route1') {
|
||||||
|
return array('_route' => 'route1');
|
||||||
|
}
|
||||||
|
|
||||||
|
// route2
|
||||||
|
if ($pathinfo === '/c2/route2') {
|
||||||
|
return array('_route' => 'route2');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^b\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route3
|
||||||
|
if ($pathinfo === '/c2/route3') {
|
||||||
|
return array('_route' => 'route3');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^a\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route4
|
||||||
|
if ($pathinfo === '/route4') {
|
||||||
|
return array('_route' => 'route4');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^c\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route5
|
||||||
|
if ($pathinfo === '/route5') {
|
||||||
|
return array('_route' => 'route5');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// route6
|
||||||
|
if ($pathinfo === '/route6') {
|
||||||
|
return array('_route' => 'route6');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^(?<var1>[^\\.]++)\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
if (0 === strpos($pathinfo, '/route1')) {
|
||||||
|
// route11
|
||||||
|
if ($pathinfo === '/route11') {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, array('_route' => 'route11')), array ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// route12
|
||||||
|
if ($pathinfo === '/route12') {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, array('_route' => 'route12')), array ( 'var1' => 'val',));
|
||||||
|
}
|
||||||
|
|
||||||
|
// route13
|
||||||
|
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, $matches, array('_route' => 'route13')), array ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// route14
|
||||||
|
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^c\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route15
|
||||||
|
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === strpos($pathinfo, '/route1')) {
|
||||||
|
// route16
|
||||||
|
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',));
|
||||||
|
}
|
||||||
|
|
||||||
|
// route17
|
||||||
|
if ($pathinfo === '/route17') {
|
||||||
|
return array('_route' => 'route17');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === strpos($pathinfo, '/a')) {
|
||||||
// a
|
// a
|
||||||
if ($pathinfo === '/a/a...') {
|
if ($pathinfo === '/a/a...') {
|
||||||
return array('_route' => 'a');
|
return array('_route' => 'a');
|
||||||
@ -230,16 +293,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
if (0 === strpos($pathinfo, '/a/b')) {
|
if (0 === strpos($pathinfo, '/a/b')) {
|
||||||
// b
|
// b
|
||||||
if (preg_match('#^/a/b/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'b';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// c
|
// c
|
||||||
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'c';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
|
|
||||||
// foo
|
// foo
|
||||||
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?<bar>baz|symfony)$#s', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/foo') && preg_match('#^/foo/(?<bar>baz|symfony)$#s', $pathinfo, $matches)) {
|
||||||
return array_merge($this->mergeDefaults($matches, array ( 'def' => 'test',)), array('_route' => 'foo'));
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo')), array ( 'def' => 'test',));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($pathinfo, '/bar')) {
|
if (0 === strpos($pathinfo, '/bar')) {
|
||||||
@ -38,9 +38,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
goto not_bar;
|
goto not_bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'bar';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_bar:
|
not_bar:
|
||||||
|
|
||||||
@ -51,9 +49,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
goto not_barhead;
|
goto not_barhead;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'barhead';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'barhead')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_barhead:
|
not_barhead:
|
||||||
|
|
||||||
@ -88,9 +84,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
return $this->redirect($pathinfo.'/', 'baz4');
|
return $this->redirect($pathinfo.'/', 'baz4');
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'baz4';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz4')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// baz5
|
// baz5
|
||||||
@ -100,9 +94,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
goto not_baz5;
|
goto not_baz5;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'baz5';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz5')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_baz5:
|
not_baz5:
|
||||||
|
|
||||||
@ -113,9 +105,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
goto not_bazbaz6;
|
goto not_bazbaz6;
|
||||||
}
|
}
|
||||||
|
|
||||||
$matches['_route'] = 'baz.baz6';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'baz.baz6')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
not_bazbaz6:
|
not_bazbaz6:
|
||||||
|
|
||||||
@ -128,9 +118,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
|
|
||||||
// quoter
|
// quoter
|
||||||
if (preg_match('#^/(?<quoter>[\']+)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/(?<quoter>[\']+)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'quoter';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'quoter')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// space
|
// space
|
||||||
@ -142,40 +130,30 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||||
// foo1
|
// foo1
|
||||||
if (preg_match('#^/a/b\'b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo1';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo1')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bar1
|
// bar1
|
||||||
if (preg_match('#^/a/b\'b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'bar1';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar1')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// overridden
|
// overridden
|
||||||
if (preg_match('#^/a/(?<var>.*)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/(?<var>.*)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'overridden';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'overridden')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
if (0 === strpos($pathinfo, '/a/b\'b')) {
|
||||||
// foo2
|
// foo2
|
||||||
if (preg_match('#^/a/b\'b/(?<foo1>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<foo1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo2';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo2')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bar2
|
// bar2
|
||||||
if (preg_match('#^/a/b\'b/(?<bar1>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b\'b/(?<bar1>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'bar2';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar2')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -185,7 +163,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
if (0 === strpos($pathinfo, '/multi')) {
|
if (0 === strpos($pathinfo, '/multi')) {
|
||||||
// helloWorld
|
// helloWorld
|
||||||
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?<who>[^/]++))?$#s', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?<who>[^/]++))?$#s', $pathinfo, $matches)) {
|
||||||
return array_merge($this->mergeDefaults($matches, array ( 'who' => 'World!',)), array('_route' => 'helloWorld'));
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'helloWorld')), array ( 'who' => 'World!',));
|
||||||
}
|
}
|
||||||
|
|
||||||
// overridden2
|
// overridden2
|
||||||
@ -206,19 +184,14 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
|
|
||||||
// foo3
|
// foo3
|
||||||
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo3';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo3')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bar3
|
// bar3
|
||||||
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/(?<_locale>[^/]++)/b/(?<bar>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'bar3';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'bar3')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 === strpos($pathinfo, '/a')) {
|
|
||||||
if (0 === strpos($pathinfo, '/aba')) {
|
if (0 === strpos($pathinfo, '/aba')) {
|
||||||
// ababa
|
// ababa
|
||||||
if ($pathinfo === '/ababa') {
|
if ($pathinfo === '/ababa') {
|
||||||
@ -227,13 +200,103 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
|
|
||||||
// foo4
|
// foo4
|
||||||
if (preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/aba/(?<foo>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'foo4';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'foo4')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hostname = $this->context->getHost();
|
||||||
|
|
||||||
|
if (preg_match('#^a\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route1
|
||||||
|
if ($pathinfo === '/route1') {
|
||||||
|
return array('_route' => 'route1');
|
||||||
|
}
|
||||||
|
|
||||||
|
// route2
|
||||||
|
if ($pathinfo === '/c2/route2') {
|
||||||
|
return array('_route' => 'route2');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^b\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route3
|
||||||
|
if ($pathinfo === '/c2/route3') {
|
||||||
|
return array('_route' => 'route3');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^a\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route4
|
||||||
|
if ($pathinfo === '/route4') {
|
||||||
|
return array('_route' => 'route4');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^c\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route5
|
||||||
|
if ($pathinfo === '/route5') {
|
||||||
|
return array('_route' => 'route5');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// route6
|
||||||
|
if ($pathinfo === '/route6') {
|
||||||
|
return array('_route' => 'route6');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^(?<var1>[^\\.]++)\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
if (0 === strpos($pathinfo, '/route1')) {
|
||||||
|
// route11
|
||||||
|
if ($pathinfo === '/route11') {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, array('_route' => 'route11')), array ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// route12
|
||||||
|
if ($pathinfo === '/route12') {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, array('_route' => 'route12')), array ( 'var1' => 'val',));
|
||||||
|
}
|
||||||
|
|
||||||
|
// route13
|
||||||
|
if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, $matches, array('_route' => 'route13')), array ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// route14
|
||||||
|
if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($hostnameMatches, $matches, array('_route' => 'route14')), array ( 'var1' => 'val',));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preg_match('#^c\\.example\\.com$#s', $hostname, $hostnameMatches)) {
|
||||||
|
// route15
|
||||||
|
if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route15')), array ());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === strpos($pathinfo, '/route1')) {
|
||||||
|
// route16
|
||||||
|
if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?<name>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'route16')), array ( 'var1' => 'val',));
|
||||||
|
}
|
||||||
|
|
||||||
|
// route17
|
||||||
|
if ($pathinfo === '/route17') {
|
||||||
|
return array('_route' => 'route17');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === strpos($pathinfo, '/a')) {
|
||||||
// a
|
// a
|
||||||
if ($pathinfo === '/a/a...') {
|
if ($pathinfo === '/a/a...') {
|
||||||
return array('_route' => 'a');
|
return array('_route' => 'a');
|
||||||
@ -242,16 +305,12 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
|||||||
if (0 === strpos($pathinfo, '/a/b')) {
|
if (0 === strpos($pathinfo, '/a/b')) {
|
||||||
// b
|
// b
|
||||||
if (preg_match('#^/a/b/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/a/b/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'b';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'b')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// c
|
// c
|
||||||
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'c';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'c')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
|||||||
|
|
||||||
// dynamic
|
// dynamic
|
||||||
if (preg_match('#^/rootprefix/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
if (preg_match('#^/rootprefix/(?<var>[^/]++)$#s', $pathinfo, $matches)) {
|
||||||
$matches['_route'] = 'dynamic';
|
return $this->mergeDefaults(array_replace($matches, array('_route' => 'dynamic')), array ());
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,9 @@ $collection = new RouteCollection();
|
|||||||
$collection->add('blog_show', new Route(
|
$collection->add('blog_show', new Route(
|
||||||
'/blog/{slug}',
|
'/blog/{slug}',
|
||||||
array('_controller' => 'MyBlogBundle:Blog:show'),
|
array('_controller' => 'MyBlogBundle:Blog:show'),
|
||||||
array(),
|
array('_method' => 'GET', 'locale' => '\w+'),
|
||||||
array('compiler_class' => 'RouteCompiler')
|
array('compiler_class' => 'RouteCompiler'),
|
||||||
|
'{locale}.example.com'
|
||||||
));
|
));
|
||||||
|
|
||||||
return $collection;
|
return $collection;
|
||||||
|
@ -4,9 +4,10 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
<route id="blog_show" pattern="/blog/{slug}">
|
<route id="blog_show" pattern="/blog/{slug}" hostname-pattern="{locale}.example.com">
|
||||||
<default key="_controller">MyBundle:Blog:show</default>
|
<default key="_controller">MyBundle:Blog:show</default>
|
||||||
<requirement key="_method">GET</requirement>
|
<requirement key="_method">GET</requirement>
|
||||||
|
<requirement key="locale">\w+</requirement>
|
||||||
<option key="compiler_class">RouteCompiler</option>
|
<option key="compiler_class">RouteCompiler</option>
|
||||||
</route>
|
</route>
|
||||||
</routes>
|
</routes>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
blog_show:
|
blog_show:
|
||||||
pattern: /blog/{slug}
|
pattern: /blog/{slug}
|
||||||
defaults: { _controller: MyBlogBundle:Blog:show }
|
defaults: { _controller: MyBlogBundle:Blog:show }
|
||||||
|
hostname_pattern: "{locale}.example.com"
|
||||||
|
requirements: { '_method': 'GET', 'locale': '\w+' }
|
||||||
options:
|
options:
|
||||||
compiler_class: RouteCompiler
|
compiler_class: RouteCompiler
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
xsi:schemaLocation="http://symfony.com/schema/routing http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||||
|
|
||||||
<import resource="validpattern.xml" prefix="/{foo}">
|
<import resource="validpattern.xml" prefix="/{foo}" hostname-pattern="{locale}.example.com">
|
||||||
<default key="foo">foo</default>
|
<default key="foo">123</default>
|
||||||
<requirement key="foo">\d+</requirement>
|
<requirement key="foo">\d+</requirement>
|
||||||
<option key="foo">bar</option>
|
<option key="foo">bar</option>
|
||||||
</import>
|
</import>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
blog_show:
|
blog_show:
|
||||||
resource: validpattern.yml
|
resource: validpattern.yml
|
||||||
prefix: /{foo}
|
prefix: /{foo}
|
||||||
defaults: { 'foo': 'foo' }
|
defaults: { 'foo': '123' }
|
||||||
requirements: { 'foo': '\d+' }
|
requirements: { 'foo': '\d+' }
|
||||||
options: { 'foo': 'bar' }
|
options: { 'foo': 'bar' }
|
||||||
|
hostname_pattern: "{locale}.example.com"
|
||||||
|
@ -377,6 +377,62 @@ class UrlGeneratorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->getGenerator($routes)->generate('test', array('page' => 'do.t', '_format' => 'html'));
|
$this->getGenerator($routes)->generate('test', array('page' => 'do.t', '_format' => 'html'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWithHostnameDifferentFromContext()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
|
||||||
|
|
||||||
|
$this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', array('name' =>'Fabien', 'locale' => 'fr')));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithHostnameSameAsContext()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
|
||||||
|
|
||||||
|
$this->assertEquals('/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' =>'Fabien', 'locale' => 'fr')));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithHostnameSameAsContextAndAbsolute()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/{name}', array(), array(), array(), '{locale}.example.com'));
|
||||||
|
|
||||||
|
$this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, array('host' => 'fr.example.com'))->generate('test', array('name' =>'Fabien', 'locale' => 'fr'), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
|
||||||
|
*/
|
||||||
|
public function testUrlWithInvalidParameterInHostname()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/', array(), array('foo' => 'bar'), array(), '{foo}.example.com'));
|
||||||
|
$this->getGenerator($routes)->generate('test', array('foo' => 'baz'), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
|
||||||
|
*/
|
||||||
|
public function testUrlWithInvalidParameterInHostnameWhenParamHasADefaultValue()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/', array('foo' => 'bar'), array('foo' => 'bar'), array(), '{foo}.example.com'));
|
||||||
|
$this->getGenerator($routes)->generate('test', array('foo' => 'baz'), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\Routing\Exception\InvalidParameterException
|
||||||
|
*/
|
||||||
|
public function testUrlWithInvalidParameterEqualsDefaultValueInHostname()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/', array('foo' => 'baz'), array('foo' => 'bar'), array(), '{foo}.example.com'));
|
||||||
|
$this->getGenerator($routes)->generate('test', array('foo' => 'baz'), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testUrlWithInvalidParameterInHostnameInNonStrictMode()
|
||||||
|
{
|
||||||
|
$routes = $this->getRoutes('test', new Route('/', array(), array('foo' => 'bar'), array(), '{foo}.example.com'));
|
||||||
|
$generator = $this->getGenerator($routes);
|
||||||
|
$generator->setStrictRequirements(false);
|
||||||
|
$this->assertNull($generator->generate('test', array('foo' => 'baz'), false));
|
||||||
|
}
|
||||||
|
|
||||||
protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
|
protected function getGenerator(RouteCollection $routes, array $parameters = array(), $logger = null)
|
||||||
{
|
{
|
||||||
$context = new RequestContext('/app.php');
|
$context = new RequestContext('/app.php');
|
||||||
|
@ -47,6 +47,10 @@ class PhpFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(1, count($routes), 'One route is loaded');
|
$this->assertEquals(1, count($routes), 'One route is loaded');
|
||||||
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
||||||
$route = $routes['blog_show'];
|
$route = $routes['blog_show'];
|
||||||
|
$this->assertEquals('/blog/{slug}', $route->getPattern());
|
||||||
|
$this->assertEquals('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
|
||||||
|
$this->assertEquals('GET', $route->getRequirement('_method'));
|
||||||
|
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
|
||||||
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,11 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(1, count($routes), 'One route is loaded');
|
$this->assertEquals(1, count($routes), 'One route is loaded');
|
||||||
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
||||||
$route = $routes['blog_show'];
|
$route = $routes['blog_show'];
|
||||||
|
$this->assertEquals('/blog/{slug}', $route->getPattern());
|
||||||
|
$this->assertEquals('MyBundle:Blog:show', $route->getDefault('_controller'));
|
||||||
|
$this->assertEquals('GET', $route->getRequirement('_method'));
|
||||||
|
$this->assertEquals('\w+', $route->getRequirement('locale'));
|
||||||
|
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
|
||||||
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,9 +64,12 @@ class XmlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertEquals(1, count($routes), 'One route is loaded');
|
$this->assertEquals(1, count($routes), 'One route is loaded');
|
||||||
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
||||||
$this->assertEquals('foo', $routes['blog_show']->getDefault('foo'));
|
$this->assertEquals('/{foo}/blog/{slug}', $routes['blog_show']->getPattern());
|
||||||
|
$this->assertEquals('MyBundle:Blog:show', $routes['blog_show']->getDefault('_controller'));
|
||||||
|
$this->assertEquals('123', $routes['blog_show']->getDefault('foo'));
|
||||||
$this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
|
$this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
|
||||||
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
|
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
|
||||||
|
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostnamePattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,6 +88,11 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(1, count($routes), 'One route is loaded');
|
$this->assertEquals(1, count($routes), 'One route is loaded');
|
||||||
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
||||||
$route = $routes['blog_show'];
|
$route = $routes['blog_show'];
|
||||||
|
$this->assertEquals('/blog/{slug}', $route->getPattern());
|
||||||
|
$this->assertEquals('MyBlogBundle:Blog:show', $route->getDefault('_controller'));
|
||||||
|
$this->assertEquals('GET', $route->getRequirement('_method'));
|
||||||
|
$this->assertEquals('\w+', $route->getRequirement('locale'));
|
||||||
|
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern());
|
||||||
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
$this->assertEquals('RouteCompiler', $route->getOption('compiler_class'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,9 +104,12 @@ class YamlFileLoaderTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertEquals(1, count($routes), 'One route is loaded');
|
$this->assertEquals(1, count($routes), 'One route is loaded');
|
||||||
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
$this->assertContainsOnly('Symfony\Component\Routing\Route', $routes);
|
||||||
$this->assertEquals('foo', $routes['blog_show']->getDefault('foo'));
|
$this->assertEquals('/{foo}/blog/{slug}', $routes['blog_show']->getPattern());
|
||||||
|
$this->assertEquals('MyBlogBundle:Blog:show', $routes['blog_show']->getDefault('_controller'));
|
||||||
|
$this->assertEquals('123', $routes['blog_show']->getDefault('foo'));
|
||||||
$this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
|
$this->assertEquals('\d+', $routes['blog_show']->getRequirement('foo'));
|
||||||
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
|
$this->assertEquals('bar', $routes['blog_show']->getOption('foo'));
|
||||||
|
$this->assertEquals('{locale}.example.com', $routes['blog_show']->getHostnamePattern());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -118,6 +118,20 @@ class ApacheUrlMatcherTest extends \PHPUnit_Framework_TestCase
|
|||||||
'_route' => 'hello',
|
'_route' => 'hello',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
array(
|
||||||
|
'REDIRECT_REDIRECT_ envs',
|
||||||
|
'/hello/world',
|
||||||
|
array(
|
||||||
|
'REDIRECT_REDIRECT__ROUTING_route' => 'hello',
|
||||||
|
'REDIRECT_REDIRECT__ROUTING_param__controller' => 'AcmeBundle:Default:index',
|
||||||
|
'REDIRECT_REDIRECT__ROUTING_param_name' => 'world',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'_controller' => 'AcmeBundle:Default:index',
|
||||||
|
'name' => 'world',
|
||||||
|
'_route' => 'hello',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,71 @@ class ApacheMatcherDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
$collection->add('baz7', new Route(
|
$collection->add('baz7', new Route(
|
||||||
'/te st/baz'
|
'/te st/baz'
|
||||||
));
|
));
|
||||||
|
// space preceded with \ in path
|
||||||
|
$collection->add('baz8', new Route(
|
||||||
|
'/te\\ st/baz'
|
||||||
|
));
|
||||||
|
// space preceded with \ in requirement
|
||||||
|
$collection->add('baz9', new Route(
|
||||||
|
'/test/{baz}',
|
||||||
|
array(),
|
||||||
|
array(
|
||||||
|
'baz' => 'te\\\\ st',
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$collection1 = new RouteCollection();
|
||||||
|
|
||||||
|
$route1 = new Route('/route1', array(), array(), array(), 'a.example.com');
|
||||||
|
$collection1->add('route1', $route1);
|
||||||
|
|
||||||
|
$collection2 = new RouteCollection();
|
||||||
|
|
||||||
|
$route2 = new Route('/route2', array(), array(), array(), 'a.example.com');
|
||||||
|
$collection2->add('route2', $route2);
|
||||||
|
|
||||||
|
$route3 = new Route('/route3', array(), array(), array(), 'b.example.com');
|
||||||
|
$collection2->add('route3', $route3);
|
||||||
|
|
||||||
|
$collection1->addCollection($collection2, '/c2');
|
||||||
|
|
||||||
|
$route4 = new Route('/route4', array(), array(), array(), 'a.example.com');
|
||||||
|
$collection1->add('route4', $route4);
|
||||||
|
|
||||||
|
$route5 = new Route('/route5', array(), array(), array(), 'c.example.com');
|
||||||
|
$collection1->add('route5', $route5);
|
||||||
|
|
||||||
|
$route6 = new Route('/route6', array(), array(), array(), null);
|
||||||
|
$collection1->add('route6', $route6);
|
||||||
|
|
||||||
|
$collection->addCollection($collection1);
|
||||||
|
|
||||||
|
// hostname and variables
|
||||||
|
|
||||||
|
$collection1 = new RouteCollection();
|
||||||
|
|
||||||
|
$route11 = new Route('/route11', array(), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route11', $route11);
|
||||||
|
|
||||||
|
$route12 = new Route('/route12', array('var1' => 'val'), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route12', $route12);
|
||||||
|
|
||||||
|
$route13 = new Route('/route13/{name}', array(), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route13', $route13);
|
||||||
|
|
||||||
|
$route14 = new Route('/route14/{name}', array('var1' => 'val'), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route14', $route14);
|
||||||
|
|
||||||
|
$route15 = new Route('/route15/{name}', array(), array(), array(), 'c.example.com');
|
||||||
|
$collection1->add('route15', $route15);
|
||||||
|
|
||||||
|
$route16 = new Route('/route16/{name}', array('var1' => 'val'), array(), array(), null);
|
||||||
|
$collection1->add('route16', $route16);
|
||||||
|
|
||||||
|
$route17 = new Route('/route17', array(), array(), array(), null);
|
||||||
|
$collection1->add('route17', $route17);
|
||||||
|
|
||||||
|
$collection->addCollection($collection1);
|
||||||
|
|
||||||
return $collection;
|
return $collection;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\Routing\Test\Matcher\Dumper;
|
||||||
|
|
||||||
|
use Symfony\Component\Routing\Matcher\Dumper\DumperCollection;
|
||||||
|
|
||||||
|
class DumperCollectionTest extends \PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testGetRoot()
|
||||||
|
{
|
||||||
|
$a = new DumperCollection();
|
||||||
|
|
||||||
|
$b = new DumperCollection();
|
||||||
|
$a->add($b);
|
||||||
|
|
||||||
|
$c = new DumperCollection();
|
||||||
|
$b->add($c);
|
||||||
|
|
||||||
|
$d = new DumperCollection();
|
||||||
|
$c->add($d);
|
||||||
|
|
||||||
|
$this->assertSame($a, $c->getRoot());
|
||||||
|
}
|
||||||
|
}
|
@ -112,4 +112,3 @@ EOF;
|
|||||||
return $string;
|
return $string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
$basePath = __DIR__.'/../../Fixtures/dumper/';
|
$basePath = __DIR__.'/../../Fixtures/dumper/';
|
||||||
|
|
||||||
$dumper = new PhpMatcherDumper($collection);
|
$dumper = new PhpMatcherDumper($collection);
|
||||||
|
|
||||||
$this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.');
|
$this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,6 +155,59 @@ class PhpMatcherDumperTest extends \PHPUnit_Framework_TestCase
|
|||||||
$collection1->add('foo4', new Route('/{foo}'));
|
$collection1->add('foo4', new Route('/{foo}'));
|
||||||
$collection->addCollection($collection1, '/aba');
|
$collection->addCollection($collection1, '/aba');
|
||||||
|
|
||||||
|
// prefix and hostname
|
||||||
|
|
||||||
|
$collection1 = new RouteCollection();
|
||||||
|
|
||||||
|
$route1 = new Route('/route1', array(), array(), array(), 'a.example.com');
|
||||||
|
$collection1->add('route1', $route1);
|
||||||
|
|
||||||
|
$collection2 = new RouteCollection();
|
||||||
|
|
||||||
|
$route2 = new Route('/c2/route2', array(), array(), array(), 'a.example.com');
|
||||||
|
$collection1->add('route2', $route2);
|
||||||
|
|
||||||
|
$route3 = new Route('/c2/route3', array(), array(), array(), 'b.example.com');
|
||||||
|
$collection1->add('route3', $route3);
|
||||||
|
|
||||||
|
$route4 = new Route('/route4', array(), array(), array(), 'a.example.com');
|
||||||
|
$collection1->add('route4', $route4);
|
||||||
|
|
||||||
|
$route5 = new Route('/route5', array(), array(), array(), 'c.example.com');
|
||||||
|
$collection1->add('route5', $route5);
|
||||||
|
|
||||||
|
$route6 = new Route('/route6', array(), array(), array(), null);
|
||||||
|
$collection1->add('route6', $route6);
|
||||||
|
|
||||||
|
$collection->addCollection($collection1);
|
||||||
|
|
||||||
|
// hostname and variables
|
||||||
|
|
||||||
|
$collection1 = new RouteCollection();
|
||||||
|
|
||||||
|
$route11 = new Route('/route11', array(), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route11', $route11);
|
||||||
|
|
||||||
|
$route12 = new Route('/route12', array('var1' => 'val'), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route12', $route12);
|
||||||
|
|
||||||
|
$route13 = new Route('/route13/{name}', array(), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route13', $route13);
|
||||||
|
|
||||||
|
$route14 = new Route('/route14/{name}', array('var1' => 'val'), array(), array(), '{var1}.example.com');
|
||||||
|
$collection1->add('route14', $route14);
|
||||||
|
|
||||||
|
$route15 = new Route('/route15/{name}', array(), array(), array(), 'c.example.com');
|
||||||
|
$collection1->add('route15', $route15);
|
||||||
|
|
||||||
|
$route16 = new Route('/route16/{name}', array('var1' => 'val'), array(), array(), null);
|
||||||
|
$collection1->add('route16', $route16);
|
||||||
|
|
||||||
|
$route17 = new Route('/route17', array(), array(), array(), null);
|
||||||
|
$collection1->add('route17', $route17);
|
||||||
|
|
||||||
|
$collection->addCollection($collection1);
|
||||||
|
|
||||||
// multiple sub-collections with a single route and a prefix each
|
// multiple sub-collections with a single route and a prefix each
|
||||||
$collection1 = new RouteCollection();
|
$collection1 = new RouteCollection();
|
||||||
$collection1->add('a', new Route('/a...'));
|
$collection1->add('a', new Route('/a...'));
|
||||||
|
@ -328,4 +328,39 @@ class UrlMatcherTest extends \PHPUnit_Framework_TestCase
|
|||||||
$matcher = new UrlMatcher($coll, new RequestContext());
|
$matcher = new UrlMatcher($coll, new RequestContext());
|
||||||
$this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523'));
|
$this->assertEquals(array('foo' => 'bar%23', '_route' => 'foo'), $matcher->match('/foo/bar%2523'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testWithHostname()
|
||||||
|
{
|
||||||
|
$coll = new RouteCollection();
|
||||||
|
$coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com'));
|
||||||
|
|
||||||
|
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
|
||||||
|
$this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testWithHostnameOnRouteCollection()
|
||||||
|
{
|
||||||
|
$coll = new RouteCollection();
|
||||||
|
$coll->add('foo', new Route('/foo/{foo}'));
|
||||||
|
$coll->add('bar', new Route('/bar/{foo}', array(), array(), array(), '{locale}.example.net'));
|
||||||
|
$coll->setHostnamePattern('{locale}.example.com');
|
||||||
|
|
||||||
|
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
|
||||||
|
$this->assertEquals(array('foo' => 'bar', '_route' => 'foo', 'locale' => 'en'), $matcher->match('/foo/bar'));
|
||||||
|
|
||||||
|
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com'));
|
||||||
|
$this->assertEquals(array('foo' => 'bar', '_route' => 'bar', 'locale' => 'en'), $matcher->match('/bar/bar'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Symfony\Component\Routing\Exception\ResourceNotFoundException
|
||||||
|
*/
|
||||||
|
public function testWithOutHostnameHostnameDoesNotMatch()
|
||||||
|
{
|
||||||
|
$coll = new RouteCollection();
|
||||||
|
$coll->add('foo', new Route('/foo/{foo}', array(), array(), array(), '{locale}.example.com'));
|
||||||
|
|
||||||
|
$matcher = new UrlMatcher($coll, new RequestContext('', 'GET', 'example.com'));
|
||||||
|
$matcher->match('/foo/bar');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,4 +297,18 @@ class RouteCollectionTest extends \PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
$this->assertEquals($rootCollection_A, $rootCollection_B);
|
$this->assertEquals($rootCollection_A, $rootCollection_B);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSetHostnamePattern()
|
||||||
|
{
|
||||||
|
$collection = new RouteCollection();
|
||||||
|
$routea = new Route('/a');
|
||||||
|
$routeb = new Route('/b', array(), array(), array(), '{locale}.example.net');
|
||||||
|
$collection->add('a', $routea);
|
||||||
|
$collection->add('b', $routeb);
|
||||||
|
|
||||||
|
$collection->setHostnamePattern('{locale}.example.com');
|
||||||
|
|
||||||
|
$this->assertEquals('{locale}.example.com', $routea->getHostnamePattern());
|
||||||
|
$this->assertEquals('{locale}.example.com', $routeb->getHostnamePattern());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,4 +179,75 @@ class RouteCompilerTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('1e2')
|
array('1e2')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideCompileWithHostnameData
|
||||||
|
*/
|
||||||
|
public function testCompileWithHostname($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostnameRegex, $hostnameVariables, $hostnameTokens)
|
||||||
|
{
|
||||||
|
$r = new \ReflectionClass('Symfony\\Component\\Routing\\Route');
|
||||||
|
$route = $r->newInstanceArgs($arguments);
|
||||||
|
|
||||||
|
$compiled = $route->compile();
|
||||||
|
$this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)');
|
||||||
|
$this->assertEquals($regex, str_replace(array("\n", ' '), '', $compiled->getRegex()), $name.' (regex)');
|
||||||
|
$this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)');
|
||||||
|
$this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)');
|
||||||
|
$this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)');
|
||||||
|
$this->assertEquals($hostnameRegex, str_replace(array("\n", ' '), '', $compiled->getHostnameRegex()), $name.' (hostname regex)');
|
||||||
|
$this->assertEquals($hostnameVariables, $compiled->getHostnameVariables(), $name.' (hostname variables)');
|
||||||
|
$this->assertEquals($hostnameTokens, $compiled->getHostnameTokens(), $name.' (hostname tokens)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideCompileWithHostnameData()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
'Route with hostname pattern',
|
||||||
|
array('/hello', array(), array(), array(), 'www.example.com'),
|
||||||
|
'/hello', '#^/hello$#s', array(), array(), array(
|
||||||
|
array('text', '/hello'),
|
||||||
|
),
|
||||||
|
'#^www\.example\.com$#s', array(), array(
|
||||||
|
array('text', 'www.example.com'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Route with hostname pattern and some variables',
|
||||||
|
array('/hello/{name}', array(), array(), array(), 'www.example.{tld}'),
|
||||||
|
'/hello', '#^/hello/(?<name>[^/]++)$#s', array('tld', 'name'), array('name'), array(
|
||||||
|
array('variable', '/', '[^/]++', 'name'),
|
||||||
|
array('text', '/hello'),
|
||||||
|
),
|
||||||
|
'#^www\.example\.(?<tld>[^\.]++)$#s', array('tld'), array(
|
||||||
|
array('variable', '.', '[^\.]++', 'tld'),
|
||||||
|
array('text', 'www.example'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Route with variable at begining of hostname',
|
||||||
|
array('/hello', array(), array(), array(), '{locale}.example.{tld}'),
|
||||||
|
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
|
||||||
|
array('text', '/hello'),
|
||||||
|
),
|
||||||
|
'#^(?<locale>[^\.]++)\.example\.(?<tld>[^\.]++)$#s', array('locale', 'tld'), array(
|
||||||
|
array('variable', '.', '[^\.]++', 'tld'),
|
||||||
|
array('text', '.example'),
|
||||||
|
array('variable', '', '[^\.]++', 'locale'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'Route with hostname variables that has a default value',
|
||||||
|
array('/hello', array('locale' => 'a', 'tld' => 'b'), array(), array(), '{locale}.example.{tld}'),
|
||||||
|
'/hello', '#^/hello$#s', array('locale', 'tld'), array(), array(
|
||||||
|
array('text', '/hello'),
|
||||||
|
),
|
||||||
|
'#^(?<locale>[^\.]++)\.example\.(?<tld>[^\.]++)$#s', array('locale', 'tld'), array(
|
||||||
|
array('variable', '.', '[^\.]++', 'tld'),
|
||||||
|
array('text', '.example'),
|
||||||
|
array('variable', '', '[^\.]++', 'locale'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,12 @@ class RouteTest extends \PHPUnit_Framework_TestCase
|
|||||||
{
|
{
|
||||||
public function testConstructor()
|
public function testConstructor()
|
||||||
{
|
{
|
||||||
$route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'));
|
$route = new Route('/{foo}', array('foo' => 'bar'), array('foo' => '\d+'), array('foo' => 'bar'), '{locale}.example.com');
|
||||||
$this->assertEquals('/{foo}', $route->getPattern(), '__construct() takes a pattern as its first argument');
|
$this->assertEquals('/{foo}', $route->getPattern(), '__construct() takes a pattern as its first argument');
|
||||||
$this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '__construct() takes defaults as its second argument');
|
$this->assertEquals(array('foo' => 'bar'), $route->getDefaults(), '__construct() takes defaults as its second argument');
|
||||||
$this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '__construct() takes requirements as its third argument');
|
$this->assertEquals(array('foo' => '\d+'), $route->getRequirements(), '__construct() takes requirements as its third argument');
|
||||||
$this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument');
|
$this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument');
|
||||||
|
$this->assertEquals('{locale}.example.com', $route->getHostnamePattern(), '__construct() takes a hostname pattern as its fifth argument');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPattern()
|
public function testPattern()
|
||||||
@ -121,6 +122,13 @@ class RouteTest extends \PHPUnit_Framework_TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHostnamePattern()
|
||||||
|
{
|
||||||
|
$route = new Route('/');
|
||||||
|
$route->setHostnamePattern('{locale}.example.net');
|
||||||
|
$this->assertEquals('{locale}.example.net', $route->getHostnamePattern(), '->setHostnamePattern() sets the hostname pattern');
|
||||||
|
}
|
||||||
|
|
||||||
public function testCompile()
|
public function testCompile()
|
||||||
{
|
{
|
||||||
$route = new Route('/{foo}');
|
$route = new Route('/{foo}');
|
||||||
|
Reference in New Issue
Block a user