feature #28865 [Routing] allow using compiled matchers and generators without dumping PHP code (nicolas-grekas)
This PR was merged into the 4.3-dev branch.
Discussion
----------
[Routing] allow using compiled matchers and generators without dumping PHP code
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | #29590
| License | MIT
| Doc PR | https://github.com/symfony/symfony-docs/pull/10790
This is a resurrection of #25909 to make matcher+generator dumpers output PHP arrays instead of PHP code.
Don't be fooled by the diff stats, it's mostly fixtures.
This PR should contribute to making the Routing component easier to use standalone.
On the way back from SFLive USA.
![image](https://user-images.githubusercontent.com/243674/46920076-784e1b80-cf9d-11e8-86e7-850fffb409de.png)
Commits
-------
f0a519ac7d
[Routing] allow using compiled matchers and generators without dumping PHP code
This commit is contained in:
commit
d0effcd35c
@ -82,8 +82,11 @@ use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyInitializableExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyListExtractorInterface;
|
||||
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
|
||||
use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
|
||||
use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader;
|
||||
use Symfony\Component\Routing\Loader\AnnotationFileLoader;
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
|
||||
use Symfony\Component\Serializer\Encoder\DecoderInterface;
|
||||
@ -754,6 +757,12 @@ class FrameworkExtension extends Extension
|
||||
if (isset($config['type'])) {
|
||||
$argument['resource_type'] = $config['type'];
|
||||
}
|
||||
if (!class_exists(CompiledUrlMatcher::class)) {
|
||||
$argument['matcher_class'] = $argument['matcher_base_class'];
|
||||
$argument['matcher_dumper_class'] = PhpMatcherDumper::class;
|
||||
$argument['generator_class'] = $argument['generator_base_class'];
|
||||
$argument['generator_dumper_class'] = PhpGeneratorDumper::class;
|
||||
}
|
||||
$router->replaceArgument(2, $argument);
|
||||
|
||||
$container->setParameter('request_listener.http_port', $config['http_port']);
|
||||
|
@ -59,13 +59,13 @@
|
||||
<argument type="collection">
|
||||
<argument key="cache_dir">%kernel.cache_dir%</argument>
|
||||
<argument key="debug">%kernel.debug%</argument>
|
||||
<argument key="generator_class">Symfony\Component\Routing\Generator\UrlGenerator</argument>
|
||||
<argument key="generator_class">Symfony\Component\Routing\Generator\CompiledUrlGenerator</argument>
|
||||
<argument key="generator_base_class">Symfony\Component\Routing\Generator\UrlGenerator</argument>
|
||||
<argument key="generator_dumper_class">Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper</argument>
|
||||
<argument key="generator_dumper_class">Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper</argument>
|
||||
<argument key="generator_cache_class">%router.cache_class_prefix%UrlGenerator</argument>
|
||||
<argument key="matcher_class">Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher</argument>
|
||||
<argument key="matcher_class">Symfony\Bundle\FrameworkBundle\Routing\RedirectableCompiledUrlMatcher</argument>
|
||||
<argument key="matcher_base_class">Symfony\Bundle\FrameworkBundle\Routing\RedirectableUrlMatcher</argument>
|
||||
<argument key="matcher_dumper_class">Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper</argument>
|
||||
<argument key="matcher_dumper_class">Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper</argument>
|
||||
<argument key="matcher_cache_class">%router.cache_class_prefix%UrlMatcher</argument>
|
||||
</argument>
|
||||
<argument type="service" id="router.request_context" on-invalid="ignore" />
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RedirectableCompiledUrlMatcher extends CompiledUrlMatcher implements RedirectableUrlMatcherInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function redirect($path, $route, $scheme = null)
|
||||
{
|
||||
return [
|
||||
'_controller' => 'Symfony\\Bundle\\FrameworkBundle\\Controller\\RedirectController::urlRedirectAction',
|
||||
'path' => $path,
|
||||
'permanent' => true,
|
||||
'scheme' => $scheme,
|
||||
'httpPort' => $this->context->getHttpPort(),
|
||||
'httpsPort' => $this->context->getHttpsPort(),
|
||||
'_route' => $route,
|
||||
];
|
||||
}
|
||||
}
|
@ -11,10 +11,14 @@
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Routing;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3.', RedirectableUrlMatcher::class), E_USER_DEPRECATED);
|
||||
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcher as BaseMatcher;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.3
|
||||
*/
|
||||
class RedirectableUrlMatcher extends BaseMatcher
|
||||
{
|
||||
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Tests\Routing;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bundle\FrameworkBundle\Routing\RedirectableCompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @requires function \Symfony\Component\Routing\Matcher\CompiledUrlMatcher::match
|
||||
*/
|
||||
class RedirectableCompiledUrlMatcherTest extends TestCase
|
||||
{
|
||||
public function testRedirectWhenNoSlash()
|
||||
{
|
||||
$routes = new RouteCollection();
|
||||
$routes->add('foo', new Route('/foo/'));
|
||||
|
||||
$matcher = $this->getMatcher($routes, $context = new RequestContext());
|
||||
|
||||
$this->assertEquals([
|
||||
'_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction',
|
||||
'path' => '/foo/',
|
||||
'permanent' => true,
|
||||
'scheme' => null,
|
||||
'httpPort' => $context->getHttpPort(),
|
||||
'httpsPort' => $context->getHttpsPort(),
|
||||
'_route' => 'foo',
|
||||
],
|
||||
$matcher->match('/foo')
|
||||
);
|
||||
}
|
||||
|
||||
public function testSchemeRedirect()
|
||||
{
|
||||
$routes = new RouteCollection();
|
||||
$routes->add('foo', new Route('/foo', [], [], [], '', ['https']));
|
||||
|
||||
$matcher = $this->getMatcher($routes, $context = new RequestContext());
|
||||
|
||||
$this->assertEquals([
|
||||
'_controller' => 'Symfony\Bundle\FrameworkBundle\Controller\RedirectController::urlRedirectAction',
|
||||
'path' => '/foo',
|
||||
'permanent' => true,
|
||||
'scheme' => 'https',
|
||||
'httpPort' => $context->getHttpPort(),
|
||||
'httpsPort' => $context->getHttpsPort(),
|
||||
'_route' => 'foo',
|
||||
],
|
||||
$matcher->match('/foo')
|
||||
);
|
||||
}
|
||||
|
||||
private function getMatcher(RouteCollection $routes, RequestContext $context)
|
||||
{
|
||||
$dumper = new CompiledUrlMatcherDumper($routes);
|
||||
|
||||
return new RedirectableCompiledUrlMatcher($dumper->getCompiledRoutes(), $context);
|
||||
}
|
||||
}
|
@ -17,6 +17,9 @@ use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class RedirectableUrlMatcherTest extends TestCase
|
||||
{
|
||||
public function testRedirectWhenNoSlash()
|
||||
|
@ -1,6 +1,13 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* added `CompiledUrlMatcher` and `CompiledUrlMatcherDumper`
|
||||
* added `CompiledUrlGenerator` and `CompiledUrlGeneratorDumper`
|
||||
* deprecated `PhpUrlGeneratorDumped` and `PhpMatcherDumper`
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
|
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Generator;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Routing\Exception\RouteNotFoundException;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* Generates URLs based on rules dumped by CompiledUrlGeneratorDumper.
|
||||
*/
|
||||
class CompiledUrlGenerator extends UrlGenerator
|
||||
{
|
||||
private $compiledRoutes = [];
|
||||
private $defaultLocale;
|
||||
|
||||
public function __construct(array $compiledRoutes, RequestContext $context, LoggerInterface $logger = null, string $defaultLocale = null)
|
||||
{
|
||||
$this->compiledRoutes = $compiledRoutes;
|
||||
$this->context = $context;
|
||||
$this->logger = $logger;
|
||||
$this->defaultLocale = $defaultLocale;
|
||||
}
|
||||
|
||||
public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)
|
||||
{
|
||||
$locale = $parameters['_locale']
|
||||
?? $this->context->getParameter('_locale')
|
||||
?: $this->defaultLocale;
|
||||
|
||||
if (null !== $locale) {
|
||||
do {
|
||||
if (($this->compiledRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) {
|
||||
unset($parameters['_locale']);
|
||||
$name .= '.'.$locale;
|
||||
break;
|
||||
}
|
||||
} while (false !== $locale = strstr($locale, '_', true));
|
||||
}
|
||||
|
||||
if (!isset($this->compiledRoutes[$name])) {
|
||||
throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name));
|
||||
}
|
||||
|
||||
list($variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes) = $this->compiledRoutes[$name];
|
||||
|
||||
return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Generator\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
|
||||
/**
|
||||
* CompiledUrlGeneratorDumper creates a PHP array to be used with CompiledUrlGenerator.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CompiledUrlGeneratorDumper extends GeneratorDumper
|
||||
{
|
||||
public function getCompiledRoutes(): array
|
||||
{
|
||||
$compiledRoutes = [];
|
||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
|
||||
$compiledRoutes[$name] = [
|
||||
$compiledRoute->getVariables(),
|
||||
$route->getDefaults(),
|
||||
$route->getRequirements(),
|
||||
$compiledRoute->getTokens(),
|
||||
$compiledRoute->getHostTokens(),
|
||||
$route->getSchemes(),
|
||||
];
|
||||
}
|
||||
|
||||
return $compiledRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump(array $options = [])
|
||||
{
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
// This file has been auto-generated by the Symfony Routing Component.
|
||||
|
||||
return [{$this->generateDeclaredRoutes()}
|
||||
];
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates PHP code representing an array of defined routes
|
||||
* together with the routes properties (e.g. requirements).
|
||||
*/
|
||||
private function generateDeclaredRoutes(): string
|
||||
{
|
||||
$routes = '';
|
||||
foreach ($this->getCompiledRoutes() as $name => $properties) {
|
||||
$routes .= sprintf("\n '%s' => %s,", $name, CompiledUrlMatcherDumper::export($properties));
|
||||
}
|
||||
|
||||
return $routes;
|
||||
}
|
||||
}
|
@ -11,13 +11,17 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Generator\Dumper;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "CompiledUrlGeneratorDumper" instead.', PhpGeneratorDumper::class), E_USER_DEPRECATED);
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
|
||||
/**
|
||||
* PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use CompiledUrlGeneratorDumper instead.
|
||||
*/
|
||||
class PhpGeneratorDumper extends GeneratorDumper
|
||||
{
|
||||
@ -92,7 +96,7 @@ EOF;
|
||||
$properties[] = $compiledRoute->getHostTokens();
|
||||
$properties[] = $route->getSchemes();
|
||||
|
||||
$routes .= sprintf(" '%s' => %s,\n", $name, PhpMatcherDumper::export($properties));
|
||||
$routes .= sprintf(" '%s' => %s,\n", $name, CompiledUrlMatcherDumper::export($properties));
|
||||
}
|
||||
$routes .= ' ]';
|
||||
|
||||
|
31
src/Symfony/Component/Routing/Matcher/CompiledUrlMatcher.php
Normal file
31
src/Symfony/Component/Routing/Matcher/CompiledUrlMatcher.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
* Matches URLs based on rules dumped by CompiledUrlMatcherDumper.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CompiledUrlMatcher extends UrlMatcher
|
||||
{
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(array $compiledRoutes, RequestContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
list($this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition) = $compiledRoutes;
|
||||
}
|
||||
}
|
@ -0,0 +1,500 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* CompiledUrlMatcherDumper creates PHP arrays to be used with CompiledUrlMatcher.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CompiledUrlMatcherDumper extends MatcherDumper
|
||||
{
|
||||
private $expressionLanguage;
|
||||
private $signalingException;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
*/
|
||||
private $expressionLanguageProviders = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function dump(array $options = [])
|
||||
{
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
{$this->generateCompiledRoutes()}];
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
||||
{
|
||||
$this->expressionLanguageProviders[] = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the arrays for CompiledUrlMatcher's constructor.
|
||||
*/
|
||||
public function getCompiledRoutes(bool $forDump = false): array
|
||||
{
|
||||
// Group hosts by same-suffix, re-order when possible
|
||||
$matchHost = false;
|
||||
$routes = new StaticPrefixCollection();
|
||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||
if ($host = $route->getHost()) {
|
||||
$matchHost = true;
|
||||
$host = '/'.strtr(strrev($host), '}.{', '(/)');
|
||||
}
|
||||
|
||||
$routes->addRoute($host ?: '/(.*)', [$name, $route]);
|
||||
}
|
||||
|
||||
if ($matchHost) {
|
||||
$compiledRoutes = [true];
|
||||
$routes = $routes->populateCollection(new RouteCollection());
|
||||
} else {
|
||||
$compiledRoutes = [false];
|
||||
$routes = $this->getRoutes();
|
||||
}
|
||||
|
||||
list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes);
|
||||
|
||||
$conditions = [null];
|
||||
$compiledRoutes[] = $this->compileStaticRoutes($staticRoutes, $conditions);
|
||||
$chunkLimit = \count($dynamicRoutes);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
$this->signalingException = new \RuntimeException('preg_match(): Compilation failed: regular expression is too large');
|
||||
$compiledRoutes = array_merge($compiledRoutes, $this->compileDynamicRoutes($dynamicRoutes, $matchHost, $chunkLimit, $conditions));
|
||||
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
if (1 < $chunkLimit && $this->signalingException === $e) {
|
||||
$chunkLimit = 1 + ($chunkLimit >> 1);
|
||||
continue;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($forDump) {
|
||||
$compiledRoutes[2] = $compiledRoutes[4];
|
||||
}
|
||||
unset($conditions[0]);
|
||||
|
||||
if ($conditions) {
|
||||
foreach ($conditions as $expression => $condition) {
|
||||
$conditions[$expression] = "case {$condition}: return {$expression};";
|
||||
}
|
||||
|
||||
$checkConditionCode = <<<EOF
|
||||
static function (\$condition, \$context, \$request) { // \$checkCondition
|
||||
switch (\$condition) {
|
||||
{$this->indent(implode("\n", $conditions), 3)}
|
||||
}
|
||||
}
|
||||
EOF;
|
||||
$compiledRoutes[4] = $forDump ? $checkConditionCode .= ",\n" : eval('return '.$checkConditionCode.';');
|
||||
} else {
|
||||
$compiledRoutes[4] = $forDump ? " null, // \$checkCondition\n" : null;
|
||||
}
|
||||
|
||||
return $compiledRoutes;
|
||||
}
|
||||
|
||||
private function generateCompiledRoutes(): string
|
||||
{
|
||||
list($matchHost, $staticRoutes, $regexpCode, $dynamicRoutes, $checkConditionCode) = $this->getCompiledRoutes(true);
|
||||
|
||||
$code = self::export($matchHost).', // $matchHost'."\n";
|
||||
|
||||
$code .= '[ // $staticRoutes'."\n";
|
||||
foreach ($staticRoutes as $path => $routes) {
|
||||
$code .= sprintf(" %s => [\n", self::export($path));
|
||||
foreach ($routes as $route) {
|
||||
$code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", ...array_map([__CLASS__, 'export'], $route));
|
||||
}
|
||||
$code .= " ],\n";
|
||||
}
|
||||
$code .= "],\n";
|
||||
|
||||
$code .= sprintf("[ // \$regexpList%s\n],\n", $regexpCode);
|
||||
|
||||
$code .= '[ // $dynamicRoutes'."\n";
|
||||
foreach ($dynamicRoutes as $path => $routes) {
|
||||
$code .= sprintf(" %s => [\n", self::export($path));
|
||||
foreach ($routes as $route) {
|
||||
$code .= sprintf(" [%s, %s, %s, %s, %s, %s, %s],\n", ...array_map([__CLASS__, 'export'], $route));
|
||||
}
|
||||
$code .= " ],\n";
|
||||
}
|
||||
$code .= "],\n";
|
||||
$code = preg_replace('/ => \[\n (\[.+?),\n \],/', ' => [$1],', $code);
|
||||
|
||||
return $this->indent($code, 1).$checkConditionCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits static routes from dynamic routes, so that they can be matched first, using a simple switch.
|
||||
*/
|
||||
private function groupStaticRoutes(RouteCollection $collection): array
|
||||
{
|
||||
$staticRoutes = $dynamicRegex = [];
|
||||
$dynamicRoutes = new RouteCollection();
|
||||
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
$hostRegex = $compiledRoute->getHostRegex();
|
||||
$regex = $compiledRoute->getRegex();
|
||||
if ($hasTrailingSlash = '/' !== $route->getPath()) {
|
||||
$pos = strrpos($regex, '$');
|
||||
$hasTrailingSlash = '/' === $regex[$pos - 1];
|
||||
$regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash);
|
||||
}
|
||||
|
||||
if (!$compiledRoute->getPathVariables()) {
|
||||
$host = !$compiledRoute->getHostVariables() ? $route->getHost() : '';
|
||||
$url = $route->getPath();
|
||||
if ($hasTrailingSlash) {
|
||||
$url = substr($url, 0, -1);
|
||||
}
|
||||
foreach ($dynamicRegex as list($hostRx, $rx)) {
|
||||
if (preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
|
||||
$dynamicRegex[] = [$hostRegex, $regex];
|
||||
$dynamicRoutes->add($name, $route);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$staticRoutes[$url][$name] = [$route, $hasTrailingSlash];
|
||||
} else {
|
||||
$dynamicRegex[] = [$hostRegex, $regex];
|
||||
$dynamicRoutes->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return [$staticRoutes, $dynamicRoutes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles static routes in a switch statement.
|
||||
*
|
||||
* Condition-less paths are put in a static array in the switch's default, with generic matching logic.
|
||||
* Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function compileStaticRoutes(array $staticRoutes, array &$conditions): array
|
||||
{
|
||||
if (!$staticRoutes) {
|
||||
return [];
|
||||
}
|
||||
$compiledRoutes = [];
|
||||
|
||||
foreach ($staticRoutes as $url => $routes) {
|
||||
$compiledRoutes[$url] = [];
|
||||
foreach ($routes as $name => list($route, $hasTrailingSlash)) {
|
||||
$compiledRoutes[$url][] = $this->compileRoute($route, $name, !$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex() ?: null, $hasTrailingSlash, false, $conditions);
|
||||
}
|
||||
}
|
||||
|
||||
return $compiledRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a regular expression followed by a switch statement to match dynamic routes.
|
||||
*
|
||||
* The regular expression matches both the host and the pathinfo at the same time. For stellar performance,
|
||||
* it is built as a tree of patterns, with re-ordering logic to group same-prefix routes together when possible.
|
||||
*
|
||||
* Patterns are named so that we know which one matched (https://pcre.org/current/doc/html/pcre2syntax.html#SEC23).
|
||||
* This name is used to "switch" to the additional logic required to match the final route.
|
||||
*
|
||||
* Condition-less paths are put in a static array in the switch's default, with generic matching logic.
|
||||
* Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases.
|
||||
*
|
||||
* Last but not least:
|
||||
* - Because it is not possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated.
|
||||
* - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the
|
||||
* matching-but-failing subpattern is blacklisted by replacing its name by "(*F)", which forces a failure-to-match.
|
||||
* To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur.
|
||||
*/
|
||||
private function compileDynamicRoutes(RouteCollection $collection, bool $matchHost, int $chunkLimit, array &$conditions): array
|
||||
{
|
||||
if (!$collection->all()) {
|
||||
return [[], [], ''];
|
||||
}
|
||||
$regexpList = [];
|
||||
$code = '';
|
||||
$state = (object) [
|
||||
'regexMark' => 0,
|
||||
'regex' => [],
|
||||
'routes' => [],
|
||||
'mark' => 0,
|
||||
'markTail' => 0,
|
||||
'hostVars' => [],
|
||||
'vars' => [],
|
||||
];
|
||||
$state->getVars = static function ($m) use ($state) {
|
||||
if ('_route' === $m[1]) {
|
||||
return '?:';
|
||||
}
|
||||
|
||||
$state->vars[] = $m[1];
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
$chunkSize = 0;
|
||||
$prev = null;
|
||||
$perModifiers = [];
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
preg_match('#[a-zA-Z]*$#', $route->compile()->getRegex(), $rx);
|
||||
if ($chunkLimit < ++$chunkSize || $prev !== $rx[0] && $route->compile()->getPathVariables()) {
|
||||
$chunkSize = 1;
|
||||
$routes = new RouteCollection();
|
||||
$perModifiers[] = [$rx[0], $routes];
|
||||
$prev = $rx[0];
|
||||
}
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
|
||||
foreach ($perModifiers as list($modifiers, $routes)) {
|
||||
$prev = false;
|
||||
$perHost = [];
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
$regex = $route->compile()->getHostRegex();
|
||||
if ($prev !== $regex) {
|
||||
$routes = new RouteCollection();
|
||||
$perHost[] = [$regex, $routes];
|
||||
$prev = $regex;
|
||||
}
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
$prev = false;
|
||||
$rx = '{^(?';
|
||||
$code .= "\n {$state->mark} => ".self::export($rx);
|
||||
$startingMark = $state->mark;
|
||||
$state->mark += \strlen($rx);
|
||||
$state->regex = $rx;
|
||||
|
||||
foreach ($perHost as list($hostRegex, $routes)) {
|
||||
if ($matchHost) {
|
||||
if ($hostRegex) {
|
||||
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx);
|
||||
$state->vars = [];
|
||||
$hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.';
|
||||
$state->hostVars = $state->vars;
|
||||
} else {
|
||||
$hostRegex = '(?:(?:[^./]*+\.)++)';
|
||||
$state->hostVars = [];
|
||||
}
|
||||
$state->mark += \strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?");
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$prev = true;
|
||||
}
|
||||
|
||||
$tree = new StaticPrefixCollection();
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $route->compile()->getRegex(), $rx);
|
||||
|
||||
$state->vars = [];
|
||||
$regex = preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]);
|
||||
if ($hasTrailingSlash = '/' !== $regex && '/' === $regex[-1]) {
|
||||
$regex = substr($regex, 0, -1);
|
||||
}
|
||||
$hasTrailingVar = (bool) preg_match('#\{\w+\}/?$#', $route->getPath());
|
||||
|
||||
$tree->addRoute($regex, [$name, $regex, $state->vars, $route, $hasTrailingSlash, $hasTrailingVar]);
|
||||
}
|
||||
|
||||
$code .= $this->compileStaticPrefixCollection($tree, $state, 0, $conditions);
|
||||
}
|
||||
if ($matchHost) {
|
||||
$code .= "\n .')'";
|
||||
$state->regex .= ')';
|
||||
}
|
||||
$rx = ")/?$}{$modifiers}";
|
||||
$code .= "\n .'{$rx}',";
|
||||
$state->regex .= $rx;
|
||||
$state->markTail = 0;
|
||||
|
||||
// if the regex is too large, throw a signaling exception to recompute with smaller chunk size
|
||||
set_error_handler(function ($type, $message) { throw 0 === strpos($message, $this->signalingException->getMessage()) ? $this->signalingException : new \ErrorException($message); });
|
||||
try {
|
||||
preg_match($state->regex, '');
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
$regexpList[$startingMark] = $state->regex;
|
||||
}
|
||||
|
||||
$state->routes[$state->mark][] = [null, null, null, null, false, false, 0];
|
||||
unset($state->getVars);
|
||||
|
||||
return [$regexpList, $state->routes, $code];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a regexp tree of subpatterns that matches nested same-prefix routes.
|
||||
*
|
||||
* @param \stdClass $state A simple state object that keeps track of the progress of the compilation,
|
||||
* and gathers the generated switch's "case" and "default" statements
|
||||
*/
|
||||
private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \stdClass $state, int $prefixLen, array &$conditions): string
|
||||
{
|
||||
$code = '';
|
||||
$prevRegex = null;
|
||||
$routes = $tree->getRoutes();
|
||||
|
||||
foreach ($routes as $i => $route) {
|
||||
if ($route instanceof StaticPrefixCollection) {
|
||||
$prevRegex = null;
|
||||
$prefix = substr($route->getPrefix(), $prefixLen);
|
||||
$state->mark += \strlen($rx = "|{$prefix}(?");
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$code .= $this->indent($this->compileStaticPrefixCollection($route, $state, $prefixLen + \strlen($prefix), $conditions));
|
||||
$code .= "\n .')'";
|
||||
$state->regex .= ')';
|
||||
++$state->markTail;
|
||||
continue;
|
||||
}
|
||||
|
||||
list($name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar) = $route;
|
||||
$compiledRoute = $route->compile();
|
||||
$vars = array_merge($state->hostVars, $vars);
|
||||
|
||||
if ($compiledRoute->getRegex() === $prevRegex) {
|
||||
$state->routes[$state->mark][] = $this->compileRoute($route, $name, $vars, $hasTrailingSlash, $hasTrailingVar, $conditions);
|
||||
continue;
|
||||
}
|
||||
|
||||
$state->mark += 3 + $state->markTail + \strlen($regex) - $prefixLen;
|
||||
$state->markTail = 2 + \strlen($state->mark);
|
||||
$rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark);
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
|
||||
$prevRegex = $compiledRoute->getRegex();
|
||||
$state->routes[$state->mark] = [$this->compileRoute($route, $name, $vars, $hasTrailingSlash, $hasTrailingVar, $conditions)];
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single Route to PHP code used to match it against the path info.
|
||||
*/
|
||||
private function compileRoute(Route $route, string $name, $vars, bool $hasTrailingSlash, bool $hasTrailingVar, array &$conditions): array
|
||||
{
|
||||
$defaults = $route->getDefaults();
|
||||
|
||||
if (isset($defaults['_canonical_route'])) {
|
||||
$name = $defaults['_canonical_route'];
|
||||
unset($defaults['_canonical_route']);
|
||||
}
|
||||
|
||||
if ($condition = $route->getCondition()) {
|
||||
$condition = $this->getExpressionLanguage()->compile($condition, ['context', 'request']);
|
||||
$condition = $conditions[$condition] ?? $conditions[$condition] = (false !== strpos($condition, '$request') ? 1 : -1) * \count($conditions);
|
||||
} else {
|
||||
$condition = null;
|
||||
}
|
||||
|
||||
return [
|
||||
['_route' => $name] + $defaults,
|
||||
$vars,
|
||||
array_flip($route->getMethods()) ?: null,
|
||||
array_flip($route->getSchemes()) ?: null,
|
||||
$hasTrailingSlash,
|
||||
$hasTrailingVar,
|
||||
$condition,
|
||||
];
|
||||
}
|
||||
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
throw new \LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
}
|
||||
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
private function indent($code, $level = 1)
|
||||
{
|
||||
return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function export($value): string
|
||||
{
|
||||
if (null === $value) {
|
||||
return 'null';
|
||||
}
|
||||
if (!\is_array($value)) {
|
||||
if (\is_object($value)) {
|
||||
throw new \InvalidArgumentException('Symfony\Component\Routing\Route cannot contain objects.');
|
||||
}
|
||||
|
||||
return str_replace("\n", '\'."\n".\'', var_export($value, true));
|
||||
}
|
||||
if (!$value) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$export = '[';
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if ($i === $k) {
|
||||
++$i;
|
||||
} else {
|
||||
$export .= self::export($k).' => ';
|
||||
|
||||
if (\is_int($k) && $i < $k) {
|
||||
$i = 1 + $k;
|
||||
}
|
||||
}
|
||||
|
||||
$export .= self::export($v).', ';
|
||||
}
|
||||
|
||||
return substr_replace($export, ']', -2);
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait PhpMatcherTrait
|
||||
trait CompiledUrlMatcherTrait
|
||||
{
|
||||
private $matchHost = false;
|
||||
private $staticRoutes = [];
|
||||
@ -126,9 +126,14 @@ trait PhpMatcherTrait
|
||||
foreach ($this->regexpList as $offset => $regex) {
|
||||
while (preg_match($regex, $matchedPathinfo, $matches)) {
|
||||
foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as list($ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash, $hasTrailingVar, $condition)) {
|
||||
if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) {
|
||||
if (null !== $condition) {
|
||||
if (0 === $condition) { // marks the last route in the regexp
|
||||
continue 3;
|
||||
}
|
||||
if (!($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($trimmedPathinfo === $pathinfo || !$hasTrailingVar) {
|
||||
// no-op
|
@ -11,10 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Matcher\Dumper;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.2, use "CompiledUrlMatcherDumper" instead.', PhpMatcherDumper::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
|
||||
@ -23,17 +20,11 @@ use Symfony\Component\Routing\RouteCollection;
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated since Symfony 4.2, use CompiledUrlMatcherDumper instead.
|
||||
*/
|
||||
class PhpMatcherDumper extends MatcherDumper
|
||||
class PhpMatcherDumper extends CompiledUrlMatcherDumper
|
||||
{
|
||||
private $expressionLanguage;
|
||||
private $signalingException;
|
||||
|
||||
/**
|
||||
* @var ExpressionFunctionProviderInterface[]
|
||||
*/
|
||||
private $expressionLanguageProviders = [];
|
||||
|
||||
/**
|
||||
* Dumps a set of routes to a PHP class.
|
||||
*
|
||||
@ -53,13 +44,17 @@ class PhpMatcherDumper extends MatcherDumper
|
||||
'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||
], $options);
|
||||
|
||||
// trailing slash support is only enabled if we know how to redirect the user
|
||||
$interfaces = class_implements($options['base_class']);
|
||||
$code = parent::dump();
|
||||
$code = preg_replace('#\n ([^ ].*?) // \$(\w++)$#m', "\n \$this->$2 = $1", $code);
|
||||
$code = str_replace(",\n $", ";\n $", $code);
|
||||
$code = substr($code, strpos($code, '$this') - 4, -5).";\n";
|
||||
$code = preg_replace('/^ \$this->\w++ = (?:null|false|\[\n \]);\n/m', '', $code);
|
||||
$code = str_replace("\n ", "\n ", "\n".$code);
|
||||
|
||||
return <<<EOF
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -68,424 +63,13 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class {$options['class']} extends {$options['base_class']}
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext \$context)
|
||||
{
|
||||
\$this->context = \$context;
|
||||
{$this->generateProperties()} }
|
||||
\$this->context = \$context;{$code} }
|
||||
}
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
|
||||
{
|
||||
$this->expressionLanguageProviders[] = $provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the code for the match method implementing UrlMatcherInterface.
|
||||
*/
|
||||
private function generateProperties(): string
|
||||
{
|
||||
// Group hosts by same-suffix, re-order when possible
|
||||
$matchHost = false;
|
||||
$routes = new StaticPrefixCollection();
|
||||
foreach ($this->getRoutes()->all() as $name => $route) {
|
||||
if ($host = $route->getHost()) {
|
||||
$matchHost = true;
|
||||
$host = '/'.strtr(strrev($host), '}.{', '(/)');
|
||||
}
|
||||
|
||||
$routes->addRoute($host ?: '/(.*)', [$name, $route]);
|
||||
}
|
||||
|
||||
if ($matchHost) {
|
||||
$code = '$this->matchHost = true;'."\n";
|
||||
$routes = $routes->populateCollection(new RouteCollection());
|
||||
} else {
|
||||
$code = '';
|
||||
$routes = $this->getRoutes();
|
||||
}
|
||||
|
||||
list($staticRoutes, $dynamicRoutes) = $this->groupStaticRoutes($routes);
|
||||
|
||||
$conditions = [null];
|
||||
$code .= $this->compileStaticRoutes($staticRoutes, $conditions);
|
||||
$chunkLimit = \count($dynamicRoutes);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
$this->signalingException = new \RuntimeException('preg_match(): Compilation failed: regular expression is too large');
|
||||
$code .= $this->compileDynamicRoutes($dynamicRoutes, $matchHost, $chunkLimit, $conditions);
|
||||
break;
|
||||
} catch (\Exception $e) {
|
||||
if (1 < $chunkLimit && $this->signalingException === $e) {
|
||||
$chunkLimit = 1 + ($chunkLimit >> 1);
|
||||
continue;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
unset($conditions[0]);
|
||||
|
||||
if (!$conditions) {
|
||||
return $this->indent($code, 2);
|
||||
}
|
||||
|
||||
foreach ($conditions as $expression => $condition) {
|
||||
$conditions[$expression] = "case {$condition}: return {$expression};";
|
||||
}
|
||||
|
||||
return $this->indent($code, 2).<<<EOF
|
||||
\$this->checkCondition = static function (\$condition, \$context, \$request) {
|
||||
switch (\$condition) {
|
||||
{$this->indent(implode("\n", $conditions), 4)}
|
||||
}
|
||||
};
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits static routes from dynamic routes, so that they can be matched first, using a simple switch.
|
||||
*/
|
||||
private function groupStaticRoutes(RouteCollection $collection): array
|
||||
{
|
||||
$staticRoutes = $dynamicRegex = [];
|
||||
$dynamicRoutes = new RouteCollection();
|
||||
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
$compiledRoute = $route->compile();
|
||||
$hostRegex = $compiledRoute->getHostRegex();
|
||||
$regex = $compiledRoute->getRegex();
|
||||
if ($hasTrailingSlash = '/' !== $route->getPath()) {
|
||||
$pos = strrpos($regex, '$');
|
||||
$hasTrailingSlash = '/' === $regex[$pos - 1];
|
||||
$regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash);
|
||||
}
|
||||
|
||||
if (!$compiledRoute->getPathVariables()) {
|
||||
$host = !$compiledRoute->getHostVariables() ? $route->getHost() : '';
|
||||
$url = $route->getPath();
|
||||
if ($hasTrailingSlash) {
|
||||
$url = substr($url, 0, -1);
|
||||
}
|
||||
foreach ($dynamicRegex as list($hostRx, $rx)) {
|
||||
if (preg_match($rx, $url) && (!$host || !$hostRx || preg_match($hostRx, $host))) {
|
||||
$dynamicRegex[] = [$hostRegex, $regex];
|
||||
$dynamicRoutes->add($name, $route);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
$staticRoutes[$url][$name] = [$route, $hasTrailingSlash];
|
||||
} else {
|
||||
$dynamicRegex[] = [$hostRegex, $regex];
|
||||
$dynamicRoutes->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
||||
return [$staticRoutes, $dynamicRoutes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles static routes in a switch statement.
|
||||
*
|
||||
* Condition-less paths are put in a static array in the switch's default, with generic matching logic.
|
||||
* Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
private function compileStaticRoutes(array $staticRoutes, array &$conditions): string
|
||||
{
|
||||
if (!$staticRoutes) {
|
||||
return '';
|
||||
}
|
||||
$code = '';
|
||||
|
||||
foreach ($staticRoutes as $url => $routes) {
|
||||
$code .= self::export($url)." => [\n";
|
||||
foreach ($routes as $name => list($route, $hasTrailingSlash)) {
|
||||
$code .= $this->compileRoute($route, $name, !$route->compile()->getHostVariables() ? $route->getHost() : $route->compile()->getHostRegex() ?: null, $hasTrailingSlash, false, $conditions);
|
||||
}
|
||||
$code .= "],\n";
|
||||
}
|
||||
|
||||
if ($code) {
|
||||
return "\$this->staticRoutes = [\n{$this->indent($code, 1)}];\n";
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a regular expression followed by a switch statement to match dynamic routes.
|
||||
*
|
||||
* The regular expression matches both the host and the pathinfo at the same time. For stellar performance,
|
||||
* it is built as a tree of patterns, with re-ordering logic to group same-prefix routes together when possible.
|
||||
*
|
||||
* Patterns are named so that we know which one matched (https://pcre.org/current/doc/html/pcre2syntax.html#SEC23).
|
||||
* This name is used to "switch" to the additional logic required to match the final route.
|
||||
*
|
||||
* Condition-less paths are put in a static array in the switch's default, with generic matching logic.
|
||||
* Paths that can match two or more routes, or have user-specified conditions are put in separate switch's cases.
|
||||
*
|
||||
* Last but not least:
|
||||
* - Because it is not possibe to mix unicode/non-unicode patterns in a single regexp, several of them can be generated.
|
||||
* - The same regexp can be used several times when the logic in the switch rejects the match. When this happens, the
|
||||
* matching-but-failing subpattern is blacklisted by replacing its name by "(*F)", which forces a failure-to-match.
|
||||
* To ease this backlisting operation, the name of subpatterns is also the string offset where the replacement should occur.
|
||||
*/
|
||||
private function compileDynamicRoutes(RouteCollection $collection, bool $matchHost, int $chunkLimit, array &$conditions): string
|
||||
{
|
||||
if (!$collection->all()) {
|
||||
return '';
|
||||
}
|
||||
$code = '';
|
||||
$state = (object) [
|
||||
'regex' => '',
|
||||
'routes' => '',
|
||||
'mark' => 0,
|
||||
'markTail' => 0,
|
||||
'hostVars' => [],
|
||||
'vars' => [],
|
||||
];
|
||||
$state->getVars = static function ($m) use ($state) {
|
||||
if ('_route' === $m[1]) {
|
||||
return '?:';
|
||||
}
|
||||
|
||||
$state->vars[] = $m[1];
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
$chunkSize = 0;
|
||||
$prev = null;
|
||||
$perModifiers = [];
|
||||
foreach ($collection->all() as $name => $route) {
|
||||
preg_match('#[a-zA-Z]*$#', $route->compile()->getRegex(), $rx);
|
||||
if ($chunkLimit < ++$chunkSize || $prev !== $rx[0] && $route->compile()->getPathVariables()) {
|
||||
$chunkSize = 1;
|
||||
$routes = new RouteCollection();
|
||||
$perModifiers[] = [$rx[0], $routes];
|
||||
$prev = $rx[0];
|
||||
}
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
|
||||
foreach ($perModifiers as list($modifiers, $routes)) {
|
||||
$prev = false;
|
||||
$perHost = [];
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
$regex = $route->compile()->getHostRegex();
|
||||
if ($prev !== $regex) {
|
||||
$routes = new RouteCollection();
|
||||
$perHost[] = [$regex, $routes];
|
||||
$prev = $regex;
|
||||
}
|
||||
$routes->add($name, $route);
|
||||
}
|
||||
$prev = false;
|
||||
$rx = '{^(?';
|
||||
$code .= "\n {$state->mark} => ".self::export($rx);
|
||||
$state->mark += \strlen($rx);
|
||||
$state->regex = $rx;
|
||||
|
||||
foreach ($perHost as list($hostRegex, $routes)) {
|
||||
if ($matchHost) {
|
||||
if ($hostRegex) {
|
||||
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $hostRegex, $rx);
|
||||
$state->vars = [];
|
||||
$hostRegex = '(?i:'.preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]).')\.';
|
||||
$state->hostVars = $state->vars;
|
||||
} else {
|
||||
$hostRegex = '(?:(?:[^./]*+\.)++)';
|
||||
$state->hostVars = [];
|
||||
}
|
||||
$state->mark += \strlen($rx = ($prev ? ')' : '')."|{$hostRegex}(?");
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$prev = true;
|
||||
}
|
||||
|
||||
$tree = new StaticPrefixCollection();
|
||||
foreach ($routes->all() as $name => $route) {
|
||||
preg_match('#^.\^(.*)\$.[a-zA-Z]*$#', $route->compile()->getRegex(), $rx);
|
||||
|
||||
$state->vars = [];
|
||||
$regex = preg_replace_callback('#\?P<([^>]++)>#', $state->getVars, $rx[1]);
|
||||
if ($hasTrailingSlash = '/' !== $regex && '/' === $regex[-1]) {
|
||||
$regex = substr($regex, 0, -1);
|
||||
}
|
||||
$hasTrailingVar = (bool) preg_match('#\{\w+\}/?$#', $route->getPath());
|
||||
|
||||
$tree->addRoute($regex, [$name, $regex, $state->vars, $route, $hasTrailingSlash, $hasTrailingVar]);
|
||||
}
|
||||
|
||||
$code .= $this->compileStaticPrefixCollection($tree, $state, 0, $conditions);
|
||||
}
|
||||
if ($matchHost) {
|
||||
$code .= "\n .')'";
|
||||
$state->regex .= ')';
|
||||
}
|
||||
$rx = ")/?$}{$modifiers}";
|
||||
$code .= "\n .'{$rx}',";
|
||||
$state->regex .= $rx;
|
||||
$state->markTail = 0;
|
||||
|
||||
// if the regex is too large, throw a signaling exception to recompute with smaller chunk size
|
||||
set_error_handler(function ($type, $message) { throw 0 === strpos($message, $this->signalingException->getMessage()) ? $this->signalingException : new \ErrorException($message); });
|
||||
try {
|
||||
preg_match($state->regex, '');
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
unset($state->getVars);
|
||||
|
||||
return "\$this->regexpList = [{$code}\n];\n"
|
||||
."\$this->dynamicRoutes = [\n{$this->indent($state->routes, 1)}];\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a regexp tree of subpatterns that matches nested same-prefix routes.
|
||||
*
|
||||
* @param \stdClass $state A simple state object that keeps track of the progress of the compilation,
|
||||
* and gathers the generated switch's "case" and "default" statements
|
||||
*/
|
||||
private function compileStaticPrefixCollection(StaticPrefixCollection $tree, \stdClass $state, int $prefixLen, array &$conditions): string
|
||||
{
|
||||
$code = '';
|
||||
$prevRegex = null;
|
||||
$routes = $tree->getRoutes();
|
||||
|
||||
foreach ($routes as $i => $route) {
|
||||
if ($route instanceof StaticPrefixCollection) {
|
||||
$prevRegex = null;
|
||||
$prefix = substr($route->getPrefix(), $prefixLen);
|
||||
$state->mark += \strlen($rx = "|{$prefix}(?");
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
$code .= $this->indent($this->compileStaticPrefixCollection($route, $state, $prefixLen + \strlen($prefix), $conditions));
|
||||
$code .= "\n .')'";
|
||||
$state->regex .= ')';
|
||||
++$state->markTail;
|
||||
continue;
|
||||
}
|
||||
|
||||
list($name, $regex, $vars, $route, $hasTrailingSlash, $hasTrailingVar) = $route;
|
||||
$compiledRoute = $route->compile();
|
||||
$vars = array_merge($state->hostVars, $vars);
|
||||
|
||||
if ($compiledRoute->getRegex() === $prevRegex) {
|
||||
$state->routes = substr_replace($state->routes, $this->compileRoute($route, $name, $vars, $hasTrailingSlash, $hasTrailingVar, $conditions), -3, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
$state->mark += 3 + $state->markTail + \strlen($regex) - $prefixLen;
|
||||
$state->markTail = 2 + \strlen($state->mark);
|
||||
$rx = sprintf('|%s(*:%s)', substr($regex, $prefixLen), $state->mark);
|
||||
$code .= "\n .".self::export($rx);
|
||||
$state->regex .= $rx;
|
||||
|
||||
$prevRegex = $compiledRoute->getRegex();
|
||||
$state->routes .= sprintf("%s => [\n%s],\n", $state->mark, $this->compileRoute($route, $name, $vars, $hasTrailingSlash, $hasTrailingVar, $conditions));
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a single Route to PHP code used to match it against the path info.
|
||||
*/
|
||||
private function compileRoute(Route $route, string $name, $vars, bool $hasTrailingSlash, bool $hasTrailingVar, array &$conditions): string
|
||||
{
|
||||
$defaults = $route->getDefaults();
|
||||
|
||||
if (isset($defaults['_canonical_route'])) {
|
||||
$name = $defaults['_canonical_route'];
|
||||
unset($defaults['_canonical_route']);
|
||||
}
|
||||
|
||||
if ($condition = $route->getCondition()) {
|
||||
$condition = $this->getExpressionLanguage()->compile($condition, ['context', 'request']);
|
||||
$condition = $conditions[$condition] ?? $conditions[$condition] = (false !== strpos($condition, '$request') ? 1 : -1) * \count($conditions);
|
||||
} else {
|
||||
$condition = 'null';
|
||||
}
|
||||
|
||||
return sprintf(
|
||||
" [%s, %s, %s, %s, %s, %s, %s],\n",
|
||||
self::export(['_route' => $name] + $defaults),
|
||||
self::export($vars),
|
||||
self::export(array_flip($route->getMethods()) ?: null),
|
||||
self::export(array_flip($route->getSchemes()) ?: null),
|
||||
self::export($hasTrailingSlash),
|
||||
self::export($hasTrailingVar),
|
||||
$condition
|
||||
);
|
||||
}
|
||||
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
throw new \LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
|
||||
}
|
||||
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
private function indent($code, $level = 1)
|
||||
{
|
||||
$code = preg_replace('/ => \[\n (\[.+),\n\],/', ' => [$1],', $code);
|
||||
|
||||
return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function export($value): string
|
||||
{
|
||||
if (null === $value) {
|
||||
return 'null';
|
||||
}
|
||||
if (!\is_array($value)) {
|
||||
if (\is_object($value)) {
|
||||
throw new \InvalidArgumentException('Symfony\Component\Routing\Route cannot contain objects.');
|
||||
}
|
||||
|
||||
return str_replace("\n", '\'."\n".\'', var_export($value, true));
|
||||
}
|
||||
if (!$value) {
|
||||
return '[]';
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$export = '[';
|
||||
|
||||
foreach ($value as $k => $v) {
|
||||
if ($i === $k) {
|
||||
++$i;
|
||||
} else {
|
||||
$export .= self::export($k).' => ';
|
||||
|
||||
if (\is_int($k) && $i < $k) {
|
||||
$i = 1 + $k;
|
||||
}
|
||||
}
|
||||
|
||||
$export .= self::export($v).', ';
|
||||
}
|
||||
|
||||
return substr_replace($export, ']', -2);
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,17 @@ use Symfony\Component\Config\ConfigCacheInterface;
|
||||
use Symfony\Component\Config\Loader\LoaderInterface;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Generator\CompiledUrlGenerator;
|
||||
use Symfony\Component\Routing\Generator\ConfigurableRequirementsInterface;
|
||||
use Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper;
|
||||
use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface;
|
||||
use Symfony\Component\Routing\Generator\UrlGenerator;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface;
|
||||
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
|
||||
|
||||
/**
|
||||
@ -127,14 +133,14 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
$this->options = [
|
||||
'cache_dir' => null,
|
||||
'debug' => false,
|
||||
'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
||||
'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
|
||||
'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
|
||||
'generator_cache_class' => 'ProjectUrlGenerator',
|
||||
'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||
'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
|
||||
'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
|
||||
'matcher_cache_class' => 'ProjectUrlMatcher',
|
||||
'generator_class' => CompiledUrlGenerator::class,
|
||||
'generator_base_class' => UrlGenerator::class,
|
||||
'generator_dumper_class' => CompiledUrlGeneratorDumper::class,
|
||||
'generator_cache_class' => 'UrlGenerator',
|
||||
'matcher_class' => CompiledUrlMatcher::class,
|
||||
'matcher_base_class' => UrlMatcher::class,
|
||||
'matcher_dumper_class' => CompiledUrlMatcherDumper::class,
|
||||
'matcher_cache_class' => 'UrlMatcher',
|
||||
'resource_type' => null,
|
||||
'strict_requirements' => true,
|
||||
];
|
||||
@ -273,8 +279,14 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
return $this->matcher;
|
||||
}
|
||||
|
||||
$compiled = is_a($this->options['matcher_class'], CompiledUrlMatcher::class, true);
|
||||
|
||||
if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) {
|
||||
$this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context);
|
||||
$routes = $this->getRouteCollection();
|
||||
if ($compiled) {
|
||||
$routes = (new CompiledUrlMatcherDumper($routes))->getCompiledRoutes();
|
||||
}
|
||||
$this->matcher = new $this->options['matcher_class']($routes, $this->context);
|
||||
if (method_exists($this->matcher, 'addExpressionLanguageProvider')) {
|
||||
foreach ($this->expressionLanguageProviders as $provider) {
|
||||
$this->matcher->addExpressionLanguageProvider($provider);
|
||||
@ -302,6 +314,10 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
}
|
||||
);
|
||||
|
||||
if ($compiled) {
|
||||
return $this->matcher = new $this->options['matcher_class'](require $cache->getPath(), $this->context);
|
||||
}
|
||||
|
||||
if (!class_exists($this->options['matcher_cache_class'], false)) {
|
||||
require_once $cache->getPath();
|
||||
}
|
||||
@ -320,8 +336,14 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
return $this->generator;
|
||||
}
|
||||
|
||||
$compiled = is_a($this->options['generator_class'], CompiledUrlGenerator::class, true);
|
||||
|
||||
if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) {
|
||||
$this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->logger);
|
||||
$routes = $this->getRouteCollection();
|
||||
if ($compiled) {
|
||||
$routes = (new CompiledUrlGeneratorDumper($routes))->getCompiledRoutes();
|
||||
}
|
||||
$this->generator = new $this->options['generator_class']($routes, $this->context, $this->logger);
|
||||
} else {
|
||||
$cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['generator_cache_class'].'.php',
|
||||
function (ConfigCacheInterface $cache) {
|
||||
@ -336,12 +358,16 @@ class Router implements RouterInterface, RequestMatcherInterface
|
||||
}
|
||||
);
|
||||
|
||||
if ($compiled) {
|
||||
$this->generator = new $this->options['generator_class'](require $cache->getPath(), $this->context, $this->logger);
|
||||
} else {
|
||||
if (!class_exists($this->options['generator_cache_class'], false)) {
|
||||
require_once $cache->getPath();
|
||||
}
|
||||
|
||||
$this->generator = new $this->options['generator_cache_class']($this->context, $this->logger);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->generator instanceof ConfigurableRequirementsInterface) {
|
||||
$this->generator->setStrictRequirements($this->options['strict_requirements']);
|
||||
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
],
|
||||
[ // $regexpList
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
true, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/test/baz' => [[['_route' => 'baz'], null, null, null, false, false, null]],
|
||||
'/test/baz.html' => [[['_route' => 'baz2'], null, null, null, false, false, null]],
|
||||
'/test/baz3' => [[['_route' => 'baz3'], null, null, null, true, false, null]],
|
||||
'/foofoo' => [[['_route' => 'foofoo', 'def' => 'test'], null, null, null, false, false, null]],
|
||||
'/spa ce' => [[['_route' => 'space'], null, null, null, false, false, null]],
|
||||
'/multi/new' => [[['_route' => 'overridden2'], null, null, null, false, false, null]],
|
||||
'/multi/hey' => [[['_route' => 'hey'], null, null, null, true, false, null]],
|
||||
'/ababa' => [[['_route' => 'ababa'], null, null, null, false, false, null]],
|
||||
'/route1' => [[['_route' => 'route1'], 'a.example.com', null, null, false, false, null]],
|
||||
'/c2/route2' => [[['_route' => 'route2'], 'a.example.com', null, null, false, false, null]],
|
||||
'/route4' => [[['_route' => 'route4'], 'a.example.com', null, null, false, false, null]],
|
||||
'/c2/route3' => [[['_route' => 'route3'], 'b.example.com', null, null, false, false, null]],
|
||||
'/route5' => [[['_route' => 'route5'], 'c.example.com', null, null, false, false, null]],
|
||||
'/route6' => [[['_route' => 'route6'], null, null, null, false, false, null]],
|
||||
'/route11' => [[['_route' => 'route11'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
|
||||
'/route12' => [[['_route' => 'route12', 'var1' => 'val'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
|
||||
'/route17' => [[['_route' => 'route17'], null, null, null, false, false, null]],
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|(?:(?:[^./]*+\\.)++)(?'
|
||||
.'|/foo/(baz|symfony)(*:47)'
|
||||
.'|/bar(?'
|
||||
.'|/([^/]++)(*:70)'
|
||||
.'|head/([^/]++)(*:90)'
|
||||
.')'
|
||||
.'|/test/([^/]++)(?'
|
||||
.'|(*:115)'
|
||||
.')'
|
||||
.'|/([\']+)(*:131)'
|
||||
.'|/a/(?'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:160)'
|
||||
.'|(*:168)'
|
||||
.')'
|
||||
.'|(.*)(*:181)'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:204)'
|
||||
.'|(*:212)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/multi/hello(?:/([^/]++))?(*:248)'
|
||||
.'|/([^/]++)/b/([^/]++)(?'
|
||||
.'|(*:279)'
|
||||
.'|(*:287)'
|
||||
.')'
|
||||
.'|/aba/([^/]++)(*:309)'
|
||||
.')|(?i:([^\\.]++)\\.example\\.com)\\.(?'
|
||||
.'|/route1(?'
|
||||
.'|3/([^/]++)(*:371)'
|
||||
.'|4/([^/]++)(*:389)'
|
||||
.')'
|
||||
.')|(?i:c\\.example\\.com)\\.(?'
|
||||
.'|/route15/([^/]++)(*:441)'
|
||||
.')|(?:(?:[^./]*+\\.)++)(?'
|
||||
.'|/route16/([^/]++)(*:489)'
|
||||
.'|/a/(?'
|
||||
.'|a\\.\\.\\.(*:510)'
|
||||
.'|b/(?'
|
||||
.'|([^/]++)(*:531)'
|
||||
.'|c/([^/]++)(*:549)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
47 => [[['_route' => 'foo', 'def' => 'test'], ['bar'], null, null, false, true, null]],
|
||||
70 => [[['_route' => 'bar'], ['foo'], ['GET' => 0, 'HEAD' => 1], null, false, true, null]],
|
||||
90 => [[['_route' => 'barhead'], ['foo'], ['GET' => 0], null, false, true, null]],
|
||||
115 => [
|
||||
[['_route' => 'baz4'], ['foo'], null, null, true, true, null],
|
||||
[['_route' => 'baz5'], ['foo'], ['POST' => 0], null, true, true, null],
|
||||
[['_route' => 'baz.baz6'], ['foo'], ['PUT' => 0], null, true, true, null],
|
||||
],
|
||||
131 => [[['_route' => 'quoter'], ['quoter'], null, null, false, true, null]],
|
||||
160 => [[['_route' => 'foo1'], ['foo'], ['PUT' => 0], null, false, true, null]],
|
||||
168 => [[['_route' => 'bar1'], ['bar'], null, null, false, true, null]],
|
||||
181 => [[['_route' => 'overridden'], ['var'], null, null, false, true, null]],
|
||||
204 => [[['_route' => 'foo2'], ['foo1'], null, null, false, true, null]],
|
||||
212 => [[['_route' => 'bar2'], ['bar1'], null, null, false, true, null]],
|
||||
248 => [[['_route' => 'helloWorld', 'who' => 'World!'], ['who'], null, null, false, true, null]],
|
||||
279 => [[['_route' => 'foo3'], ['_locale', 'foo'], null, null, false, true, null]],
|
||||
287 => [[['_route' => 'bar3'], ['_locale', 'bar'], null, null, false, true, null]],
|
||||
309 => [[['_route' => 'foo4'], ['foo'], null, null, false, true, null]],
|
||||
371 => [[['_route' => 'route13'], ['var1', 'name'], null, null, false, true, null]],
|
||||
389 => [[['_route' => 'route14', 'var1' => 'val'], ['var1', 'name'], null, null, false, true, null]],
|
||||
441 => [[['_route' => 'route15'], ['name'], null, null, false, true, null]],
|
||||
489 => [[['_route' => 'route16', 'var1' => 'val'], ['name'], null, null, false, true, null]],
|
||||
510 => [[['_route' => 'a'], [], null, null, false, false, null]],
|
||||
531 => [[['_route' => 'b'], ['var'], null, null, false, true, null]],
|
||||
549 => [
|
||||
[['_route' => 'c'], ['var'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/(en|fr)/(?'
|
||||
.'|admin/post(?'
|
||||
.'|(*:32)'
|
||||
.'|/(?'
|
||||
.'|new(*:46)'
|
||||
.'|(\\d+)(*:58)'
|
||||
.'|(\\d+)/edit(*:75)'
|
||||
.'|(\\d+)/delete(*:94)'
|
||||
.')'
|
||||
.')'
|
||||
.'|blog(?'
|
||||
.'|(*:110)'
|
||||
.'|/(?'
|
||||
.'|rss\\.xml(*:130)'
|
||||
.'|p(?'
|
||||
.'|age/([^/]++)(*:154)'
|
||||
.'|osts/([^/]++)(*:175)'
|
||||
.')'
|
||||
.'|comments/(\\d+)/new(*:202)'
|
||||
.'|search(*:216)'
|
||||
.')'
|
||||
.')'
|
||||
.'|log(?'
|
||||
.'|in(*:234)'
|
||||
.'|out(*:245)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/(en|fr)?(*:264)'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
32 => [[['_route' => 'a', '_locale' => 'en'], ['_locale'], null, null, true, false, null]],
|
||||
46 => [[['_route' => 'b', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
58 => [[['_route' => 'c', '_locale' => 'en'], ['_locale', 'id'], null, null, false, true, null]],
|
||||
75 => [[['_route' => 'd', '_locale' => 'en'], ['_locale', 'id'], null, null, false, false, null]],
|
||||
94 => [[['_route' => 'e', '_locale' => 'en'], ['_locale', 'id'], null, null, false, false, null]],
|
||||
110 => [[['_route' => 'f', '_locale' => 'en'], ['_locale'], null, null, true, false, null]],
|
||||
130 => [[['_route' => 'g', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
154 => [[['_route' => 'h', '_locale' => 'en'], ['_locale', 'page'], null, null, false, true, null]],
|
||||
175 => [[['_route' => 'i', '_locale' => 'en'], ['_locale', 'page'], null, null, false, true, null]],
|
||||
202 => [[['_route' => 'j', '_locale' => 'en'], ['_locale', 'id'], null, null, false, false, null]],
|
||||
216 => [[['_route' => 'k', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
234 => [[['_route' => 'l', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
245 => [[['_route' => 'm', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
264 => [
|
||||
[['_route' => 'n', '_locale' => 'en'], ['_locale'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/abc([^/]++)/(?'
|
||||
.'|1(?'
|
||||
.'|(*:27)'
|
||||
.'|0(?'
|
||||
.'|(*:38)'
|
||||
.'|0(*:46)'
|
||||
.')'
|
||||
.')'
|
||||
.'|2(?'
|
||||
.'|(*:59)'
|
||||
.'|0(?'
|
||||
.'|(*:70)'
|
||||
.'|0(*:78)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
27 => [[['_route' => 'r1'], ['foo'], null, null, false, false, null]],
|
||||
38 => [[['_route' => 'r10'], ['foo'], null, null, false, false, null]],
|
||||
46 => [[['_route' => 'r100'], ['foo'], null, null, false, false, null]],
|
||||
59 => [[['_route' => 'r2'], ['foo'], null, null, false, false, null]],
|
||||
70 => [[['_route' => 'r20'], ['foo'], null, null, false, false, null]],
|
||||
78 => [
|
||||
[['_route' => 'r200'], ['foo'], null, null, false, false, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
true, // $matchHost
|
||||
[ // $staticRoutes
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|(?i:([^\\.]++)\\.exampple\\.com)\\.(?'
|
||||
.'|/abc([^/]++)(?'
|
||||
.'|(*:56)'
|
||||
.')'
|
||||
.')'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
56 => [
|
||||
[['_route' => 'r1'], ['foo', 'foo'], null, null, false, true, null],
|
||||
[['_route' => 'r2'], ['foo', 'foo'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
true, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/test/baz' => [[['_route' => 'baz'], null, null, null, false, false, null]],
|
||||
'/test/baz.html' => [[['_route' => 'baz2'], null, null, null, false, false, null]],
|
||||
'/test/baz3' => [[['_route' => 'baz3'], null, null, null, true, false, null]],
|
||||
'/foofoo' => [[['_route' => 'foofoo', 'def' => 'test'], null, null, null, false, false, null]],
|
||||
'/spa ce' => [[['_route' => 'space'], null, null, null, false, false, null]],
|
||||
'/multi/new' => [[['_route' => 'overridden2'], null, null, null, false, false, null]],
|
||||
'/multi/hey' => [[['_route' => 'hey'], null, null, null, true, false, null]],
|
||||
'/ababa' => [[['_route' => 'ababa'], null, null, null, false, false, null]],
|
||||
'/route1' => [[['_route' => 'route1'], 'a.example.com', null, null, false, false, null]],
|
||||
'/c2/route2' => [[['_route' => 'route2'], 'a.example.com', null, null, false, false, null]],
|
||||
'/route4' => [[['_route' => 'route4'], 'a.example.com', null, null, false, false, null]],
|
||||
'/c2/route3' => [[['_route' => 'route3'], 'b.example.com', null, null, false, false, null]],
|
||||
'/route5' => [[['_route' => 'route5'], 'c.example.com', null, null, false, false, null]],
|
||||
'/route6' => [[['_route' => 'route6'], null, null, null, false, false, null]],
|
||||
'/route11' => [[['_route' => 'route11'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
|
||||
'/route12' => [[['_route' => 'route12', 'var1' => 'val'], '#^(?P<var1>[^\\.]++)\\.example\\.com$#sDi', null, null, false, false, null]],
|
||||
'/route17' => [[['_route' => 'route17'], null, null, null, false, false, null]],
|
||||
'/secure' => [[['_route' => 'secure'], null, null, ['https' => 0], false, false, null]],
|
||||
'/nonsecure' => [[['_route' => 'nonsecure'], null, null, ['http' => 0], false, false, null]],
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|(?:(?:[^./]*+\\.)++)(?'
|
||||
.'|/foo/(baz|symfony)(*:47)'
|
||||
.'|/bar(?'
|
||||
.'|/([^/]++)(*:70)'
|
||||
.'|head/([^/]++)(*:90)'
|
||||
.')'
|
||||
.'|/test/([^/]++)(?'
|
||||
.'|(*:115)'
|
||||
.')'
|
||||
.'|/([\']+)(*:131)'
|
||||
.'|/a/(?'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:160)'
|
||||
.'|(*:168)'
|
||||
.')'
|
||||
.'|(.*)(*:181)'
|
||||
.'|b\'b/([^/]++)(?'
|
||||
.'|(*:204)'
|
||||
.'|(*:212)'
|
||||
.')'
|
||||
.')'
|
||||
.'|/multi/hello(?:/([^/]++))?(*:248)'
|
||||
.'|/([^/]++)/b/([^/]++)(?'
|
||||
.'|(*:279)'
|
||||
.'|(*:287)'
|
||||
.')'
|
||||
.'|/aba/([^/]++)(*:309)'
|
||||
.')|(?i:([^\\.]++)\\.example\\.com)\\.(?'
|
||||
.'|/route1(?'
|
||||
.'|3/([^/]++)(*:371)'
|
||||
.'|4/([^/]++)(*:389)'
|
||||
.')'
|
||||
.')|(?i:c\\.example\\.com)\\.(?'
|
||||
.'|/route15/([^/]++)(*:441)'
|
||||
.')|(?:(?:[^./]*+\\.)++)(?'
|
||||
.'|/route16/([^/]++)(*:489)'
|
||||
.'|/a/(?'
|
||||
.'|a\\.\\.\\.(*:510)'
|
||||
.'|b/(?'
|
||||
.'|([^/]++)(*:531)'
|
||||
.'|c/([^/]++)(*:549)'
|
||||
.')'
|
||||
.')'
|
||||
.')'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
47 => [[['_route' => 'foo', 'def' => 'test'], ['bar'], null, null, false, true, null]],
|
||||
70 => [[['_route' => 'bar'], ['foo'], ['GET' => 0, 'HEAD' => 1], null, false, true, null]],
|
||||
90 => [[['_route' => 'barhead'], ['foo'], ['GET' => 0], null, false, true, null]],
|
||||
115 => [
|
||||
[['_route' => 'baz4'], ['foo'], null, null, true, true, null],
|
||||
[['_route' => 'baz5'], ['foo'], ['POST' => 0], null, true, true, null],
|
||||
[['_route' => 'baz.baz6'], ['foo'], ['PUT' => 0], null, true, true, null],
|
||||
],
|
||||
131 => [[['_route' => 'quoter'], ['quoter'], null, null, false, true, null]],
|
||||
160 => [[['_route' => 'foo1'], ['foo'], ['PUT' => 0], null, false, true, null]],
|
||||
168 => [[['_route' => 'bar1'], ['bar'], null, null, false, true, null]],
|
||||
181 => [[['_route' => 'overridden'], ['var'], null, null, false, true, null]],
|
||||
204 => [[['_route' => 'foo2'], ['foo1'], null, null, false, true, null]],
|
||||
212 => [[['_route' => 'bar2'], ['bar1'], null, null, false, true, null]],
|
||||
248 => [[['_route' => 'helloWorld', 'who' => 'World!'], ['who'], null, null, false, true, null]],
|
||||
279 => [[['_route' => 'foo3'], ['_locale', 'foo'], null, null, false, true, null]],
|
||||
287 => [[['_route' => 'bar3'], ['_locale', 'bar'], null, null, false, true, null]],
|
||||
309 => [[['_route' => 'foo4'], ['foo'], null, null, false, true, null]],
|
||||
371 => [[['_route' => 'route13'], ['var1', 'name'], null, null, false, true, null]],
|
||||
389 => [[['_route' => 'route14', 'var1' => 'val'], ['var1', 'name'], null, null, false, true, null]],
|
||||
441 => [[['_route' => 'route15'], ['name'], null, null, false, true, null]],
|
||||
489 => [[['_route' => 'route16', 'var1' => 'val'], ['name'], null, null, false, true, null]],
|
||||
510 => [[['_route' => 'a'], [], null, null, false, false, null]],
|
||||
531 => [[['_route' => 'b'], ['var'], null, null, false, true, null]],
|
||||
549 => [
|
||||
[['_route' => 'c'], ['var'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/rootprefix/test' => [[['_route' => 'static'], null, null, null, false, false, null]],
|
||||
'/with-condition' => [[['_route' => 'with-condition'], null, null, null, false, false, -1]],
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/rootprefix/([^/]++)(*:27)'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
27 => [
|
||||
[['_route' => 'dynamic'], ['var'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
static function ($condition, $context, $request) { // $checkCondition
|
||||
switch ($condition) {
|
||||
case -1: return ($context->getMethod() == "GET");
|
||||
}
|
||||
},
|
||||
];
|
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/just_head' => [[['_route' => 'just_head'], null, ['HEAD' => 0], null, false, false, null]],
|
||||
'/head_and_get' => [[['_route' => 'head_and_get'], null, ['HEAD' => 0, 'GET' => 1], null, false, false, null]],
|
||||
'/get_and_head' => [[['_route' => 'get_and_head'], null, ['GET' => 0, 'HEAD' => 1], null, false, false, null]],
|
||||
'/post_and_head' => [[['_route' => 'post_and_head'], null, ['POST' => 0, 'HEAD' => 1], null, false, false, null]],
|
||||
'/put_and_post' => [
|
||||
[['_route' => 'put_and_post'], null, ['PUT' => 0, 'POST' => 1], null, false, false, null],
|
||||
[['_route' => 'put_and_get_and_head'], null, ['PUT' => 0, 'GET' => 1, 'HEAD' => 2], null, false, false, null],
|
||||
],
|
||||
],
|
||||
[ // $regexpList
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/a/11' => [[['_route' => 'a_first'], null, null, null, false, false, null]],
|
||||
'/a/22' => [[['_route' => 'a_second'], null, null, null, false, false, null]],
|
||||
'/a/333' => [[['_route' => 'a_third'], null, null, null, false, false, null]],
|
||||
'/a/44' => [[['_route' => 'a_fourth'], null, null, null, true, false, null]],
|
||||
'/a/55' => [[['_route' => 'a_fifth'], null, null, null, true, false, null]],
|
||||
'/a/66' => [[['_route' => 'a_sixth'], null, null, null, true, false, null]],
|
||||
'/nested/group/a' => [[['_route' => 'nested_a'], null, null, null, true, false, null]],
|
||||
'/nested/group/b' => [[['_route' => 'nested_b'], null, null, null, true, false, null]],
|
||||
'/nested/group/c' => [[['_route' => 'nested_c'], null, null, null, true, false, null]],
|
||||
'/slashed/group' => [[['_route' => 'slashed_a'], null, null, null, true, false, null]],
|
||||
'/slashed/group/b' => [[['_route' => 'slashed_b'], null, null, null, true, false, null]],
|
||||
'/slashed/group/c' => [[['_route' => 'slashed_c'], null, null, null, true, false, null]],
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/([^/]++)(*:16)'
|
||||
.'|/nested/([^/]++)(*:39)'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
16 => [[['_route' => 'a_wildcard'], ['param'], null, null, false, true, null]],
|
||||
39 => [
|
||||
[['_route' => 'nested_wildcard'], ['param'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/trailing/simple/no-methods' => [[['_route' => 'simple_trailing_slash_no_methods'], null, null, null, true, false, null]],
|
||||
'/trailing/simple/get-method' => [[['_route' => 'simple_trailing_slash_GET_method'], null, ['GET' => 0], null, true, false, null]],
|
||||
'/trailing/simple/head-method' => [[['_route' => 'simple_trailing_slash_HEAD_method'], null, ['HEAD' => 0], null, true, false, null]],
|
||||
'/trailing/simple/post-method' => [[['_route' => 'simple_trailing_slash_POST_method'], null, ['POST' => 0], null, true, false, null]],
|
||||
'/not-trailing/simple/no-methods' => [[['_route' => 'simple_not_trailing_slash_no_methods'], null, null, null, false, false, null]],
|
||||
'/not-trailing/simple/get-method' => [[['_route' => 'simple_not_trailing_slash_GET_method'], null, ['GET' => 0], null, false, false, null]],
|
||||
'/not-trailing/simple/head-method' => [[['_route' => 'simple_not_trailing_slash_HEAD_method'], null, ['HEAD' => 0], null, false, false, null]],
|
||||
'/not-trailing/simple/post-method' => [[['_route' => 'simple_not_trailing_slash_POST_method'], null, ['POST' => 0], null, false, false, null]],
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)(*:46)'
|
||||
.'|get\\-method/([^/]++)(*:73)'
|
||||
.'|head\\-method/([^/]++)(*:101)'
|
||||
.'|post\\-method/([^/]++)(*:130)'
|
||||
.')'
|
||||
.'|/not\\-trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)(*:183)'
|
||||
.'|get\\-method/([^/]++)(*:211)'
|
||||
.'|head\\-method/([^/]++)(*:240)'
|
||||
.'|post\\-method/([^/]++)(*:269)'
|
||||
.')'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
46 => [[['_route' => 'regex_trailing_slash_no_methods'], ['param'], null, null, true, true, null]],
|
||||
73 => [[['_route' => 'regex_trailing_slash_GET_method'], ['param'], ['GET' => 0], null, true, true, null]],
|
||||
101 => [[['_route' => 'regex_trailing_slash_HEAD_method'], ['param'], ['HEAD' => 0], null, true, true, null]],
|
||||
130 => [[['_route' => 'regex_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, true, true, null]],
|
||||
183 => [[['_route' => 'regex_not_trailing_slash_no_methods'], ['param'], null, null, false, true, null]],
|
||||
211 => [[['_route' => 'regex_not_trailing_slash_GET_method'], ['param'], ['GET' => 0], null, false, true, null]],
|
||||
240 => [[['_route' => 'regex_not_trailing_slash_HEAD_method'], ['param'], ['HEAD' => 0], null, false, true, null]],
|
||||
269 => [
|
||||
[['_route' => 'regex_not_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/trailing/simple/no-methods' => [[['_route' => 'simple_trailing_slash_no_methods'], null, null, null, true, false, null]],
|
||||
'/trailing/simple/get-method' => [[['_route' => 'simple_trailing_slash_GET_method'], null, ['GET' => 0], null, true, false, null]],
|
||||
'/trailing/simple/head-method' => [[['_route' => 'simple_trailing_slash_HEAD_method'], null, ['HEAD' => 0], null, true, false, null]],
|
||||
'/trailing/simple/post-method' => [[['_route' => 'simple_trailing_slash_POST_method'], null, ['POST' => 0], null, true, false, null]],
|
||||
'/not-trailing/simple/no-methods' => [[['_route' => 'simple_not_trailing_slash_no_methods'], null, null, null, false, false, null]],
|
||||
'/not-trailing/simple/get-method' => [[['_route' => 'simple_not_trailing_slash_GET_method'], null, ['GET' => 0], null, false, false, null]],
|
||||
'/not-trailing/simple/head-method' => [[['_route' => 'simple_not_trailing_slash_HEAD_method'], null, ['HEAD' => 0], null, false, false, null]],
|
||||
'/not-trailing/simple/post-method' => [[['_route' => 'simple_not_trailing_slash_POST_method'], null, ['POST' => 0], null, false, false, null]],
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)(*:46)'
|
||||
.'|get\\-method/([^/]++)(*:73)'
|
||||
.'|head\\-method/([^/]++)(*:101)'
|
||||
.'|post\\-method/([^/]++)(*:130)'
|
||||
.')'
|
||||
.'|/not\\-trailing/regex/(?'
|
||||
.'|no\\-methods/([^/]++)(*:183)'
|
||||
.'|get\\-method/([^/]++)(*:211)'
|
||||
.'|head\\-method/([^/]++)(*:240)'
|
||||
.'|post\\-method/([^/]++)(*:269)'
|
||||
.')'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
46 => [[['_route' => 'regex_trailing_slash_no_methods'], ['param'], null, null, true, true, null]],
|
||||
73 => [[['_route' => 'regex_trailing_slash_GET_method'], ['param'], ['GET' => 0], null, true, true, null]],
|
||||
101 => [[['_route' => 'regex_trailing_slash_HEAD_method'], ['param'], ['HEAD' => 0], null, true, true, null]],
|
||||
130 => [[['_route' => 'regex_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, true, true, null]],
|
||||
183 => [[['_route' => 'regex_not_trailing_slash_no_methods'], ['param'], null, null, false, true, null]],
|
||||
211 => [[['_route' => 'regex_not_trailing_slash_GET_method'], ['param'], ['GET' => 0], null, false, true, null]],
|
||||
240 => [[['_route' => 'regex_not_trailing_slash_HEAD_method'], ['param'], ['HEAD' => 0], null, false, true, null]],
|
||||
269 => [
|
||||
[['_route' => 'regex_not_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
false, // $matchHost
|
||||
[ // $staticRoutes
|
||||
],
|
||||
[ // $regexpList
|
||||
0 => '{^(?'
|
||||
.'|/(a)(*:11)'
|
||||
.')/?$}sD',
|
||||
11 => '{^(?'
|
||||
.'|/(.)(*:22)'
|
||||
.')/?$}sDu',
|
||||
22 => '{^(?'
|
||||
.'|/(.)(*:33)'
|
||||
.')/?$}sD',
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
11 => [[['_route' => 'a'], ['a'], null, null, false, true, null]],
|
||||
22 => [[['_route' => 'b'], ['a'], null, null, false, true, null]],
|
||||
33 => [
|
||||
[['_route' => 'c'], ['a'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file has been auto-generated
|
||||
* by the Symfony Routing Component.
|
||||
*/
|
||||
|
||||
return [
|
||||
true, // $matchHost
|
||||
[ // $staticRoutes
|
||||
'/' => [
|
||||
[['_route' => 'a'], '#^(?P<d>[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', null, null, false, false, null],
|
||||
[['_route' => 'c'], '#^(?P<e>[^\\.]++)\\.e\\.c\\.b\\.a$#sDi', null, null, false, false, null],
|
||||
[['_route' => 'b'], 'd.c.b.a', null, null, false, false, null],
|
||||
],
|
||||
],
|
||||
[ // $regexpList
|
||||
],
|
||||
[ // $dynamicRoutes
|
||||
],
|
||||
null, // $checkCondition
|
||||
];
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -107,7 +107,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
489 => [[['_route' => 'route16', 'var1' => 'val'], ['name'], null, null, false, true, null]],
|
||||
510 => [[['_route' => 'a'], [], null, null, false, false, null]],
|
||||
531 => [[['_route' => 'b'], ['var'], null, null, false, true, null]],
|
||||
549 => [[['_route' => 'c'], ['var'], null, null, false, true, null]],
|
||||
549 => [
|
||||
[['_route' => 'c'], ['var'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -2770,7 +2770,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
49567 => [[['_route' => '_974'], ['a', 'b', 'c'], null, null, false, false, null]],
|
||||
49620 => [[['_route' => '_835'], ['a', 'b', 'c'], null, null, false, false, null]],
|
||||
49668 => [[['_route' => '_934'], ['a', 'b', 'c'], null, null, false, false, null]],
|
||||
49718 => [[['_route' => '_869'], ['a', 'b', 'c'], null, null, false, false, null]],
|
||||
49718 => [
|
||||
[['_route' => '_869'], ['a', 'b', 'c'], null, null, false, false, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -60,7 +60,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
216 => [[['_route' => 'k', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
234 => [[['_route' => 'l', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
245 => [[['_route' => 'm', '_locale' => 'en'], ['_locale'], null, null, false, false, null]],
|
||||
264 => [[['_route' => 'n', '_locale' => 'en'], ['_locale'], null, null, false, true, null]],
|
||||
264 => [
|
||||
[['_route' => 'n', '_locale' => 'en'], ['_locale'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -40,7 +40,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
46 => [[['_route' => 'r100'], ['foo'], null, null, false, false, null]],
|
||||
59 => [[['_route' => 'r2'], ['foo'], null, null, false, false, null]],
|
||||
70 => [[['_route' => 'r20'], ['foo'], null, null, false, false, null]],
|
||||
78 => [[['_route' => 'r200'], ['foo'], null, null, false, false, null]],
|
||||
78 => [
|
||||
[['_route' => 'r200'], ['foo'], null, null, false, false, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -28,6 +28,7 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
56 => [
|
||||
[['_route' => 'r1'], ['foo', 'foo'], null, null, false, true, null],
|
||||
[['_route' => 'r2'], ['foo', 'foo'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -109,7 +109,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
489 => [[['_route' => 'route16', 'var1' => 'val'], ['name'], null, null, false, true, null]],
|
||||
510 => [[['_route' => 'a'], [], null, null, false, false, null]],
|
||||
531 => [[['_route' => 'b'], ['var'], null, null, false, true, null]],
|
||||
549 => [[['_route' => 'c'], ['var'], null, null, false, true, null]],
|
||||
549 => [
|
||||
[['_route' => 'c'], ['var'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -24,7 +24,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
.')/?$}sD',
|
||||
];
|
||||
$this->dynamicRoutes = [
|
||||
27 => [[['_route' => 'dynamic'], ['var'], null, null, false, true, null]],
|
||||
27 => [
|
||||
[['_route' => 'dynamic'], ['var'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
$this->checkCondition = static function ($condition, $context, $request) {
|
||||
switch ($condition) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -36,7 +36,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
];
|
||||
$this->dynamicRoutes = [
|
||||
16 => [[['_route' => 'a_wildcard'], ['param'], null, null, false, true, null]],
|
||||
39 => [[['_route' => 'nested_wildcard'], ['param'], null, null, false, true, null]],
|
||||
39 => [
|
||||
[['_route' => 'nested_wildcard'], ['param'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -48,7 +48,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
183 => [[['_route' => 'regex_not_trailing_slash_no_methods'], ['param'], null, null, false, true, null]],
|
||||
211 => [[['_route' => 'regex_not_trailing_slash_GET_method'], ['param'], ['GET' => 0], null, false, true, null]],
|
||||
240 => [[['_route' => 'regex_not_trailing_slash_HEAD_method'], ['param'], ['HEAD' => 0], null, false, true, null]],
|
||||
269 => [[['_route' => 'regex_not_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, false, true, null]],
|
||||
269 => [
|
||||
[['_route' => 'regex_not_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -48,7 +48,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Tests\Fixtures\Redirec
|
||||
183 => [[['_route' => 'regex_not_trailing_slash_no_methods'], ['param'], null, null, false, true, null]],
|
||||
211 => [[['_route' => 'regex_not_trailing_slash_GET_method'], ['param'], ['GET' => 0], null, false, true, null]],
|
||||
240 => [[['_route' => 'regex_not_trailing_slash_HEAD_method'], ['param'], ['HEAD' => 0], null, false, true, null]],
|
||||
269 => [[['_route' => 'regex_not_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, false, true, null]],
|
||||
269 => [
|
||||
[['_route' => 'regex_not_trailing_slash_POST_method'], ['param'], ['POST' => 0], null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
@ -28,7 +28,10 @@ class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
$this->dynamicRoutes = [
|
||||
11 => [[['_route' => 'a'], ['a'], null, null, false, true, null]],
|
||||
22 => [[['_route' => 'b'], ['a'], null, null, false, true, null]],
|
||||
33 => [[['_route' => 'c'], ['a'], null, null, false, true, null]],
|
||||
33 => [
|
||||
[['_route' => 'c'], ['a'], null, null, false, true, null],
|
||||
[null, null, null, null, false, false, 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherTrait;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
|
||||
/**
|
||||
@ -9,7 +9,7 @@ use Symfony\Component\Routing\RequestContext;
|
||||
*/
|
||||
class ProjectUrlMatcher extends Symfony\Component\Routing\Matcher\UrlMatcher
|
||||
{
|
||||
use PhpMatcherTrait;
|
||||
use CompiledUrlMatcherTrait;
|
||||
|
||||
public function __construct(RequestContext $context)
|
||||
{
|
||||
|
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Generator\Dumper;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Routing\Generator\CompiledUrlGenerator;
|
||||
use Symfony\Component\Routing\Generator\Dumper\CompiledUrlGeneratorDumper;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class CompiledUrlGeneratorDumperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var RouteCollection
|
||||
*/
|
||||
private $routeCollection;
|
||||
|
||||
/**
|
||||
* @var CompiledUrlGeneratorDumper
|
||||
*/
|
||||
private $generatorDumper;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $testTmpFilepath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $largeTestTmpFilepath;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->routeCollection = new RouteCollection();
|
||||
$this->generatorDumper = new CompiledUrlGeneratorDumper($this->routeCollection);
|
||||
$this->testTmpFilepath = sys_get_temp_dir().'/php_generator.'.$this->getName().'.php';
|
||||
$this->largeTestTmpFilepath = sys_get_temp_dir().'/php_generator.'.$this->getName().'.large.php';
|
||||
@unlink($this->testTmpFilepath);
|
||||
@unlink($this->largeTestTmpFilepath);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
@unlink($this->testTmpFilepath);
|
||||
|
||||
$this->routeCollection = null;
|
||||
$this->generatorDumper = null;
|
||||
$this->testTmpFilepath = null;
|
||||
}
|
||||
|
||||
public function testDumpWithRoutes()
|
||||
{
|
||||
$this->routeCollection->add('Test', new Route('/testing/{foo}'));
|
||||
$this->routeCollection->add('Test2', new Route('/testing2'));
|
||||
|
||||
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext('/app.php'));
|
||||
|
||||
$absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$relativeUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
$relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
|
||||
$this->assertEquals('http://localhost/app.php/testing/bar', $absoluteUrlWithParameter);
|
||||
$this->assertEquals('http://localhost/app.php/testing2', $absoluteUrlWithoutParameter);
|
||||
$this->assertEquals('/app.php/testing/bar', $relativeUrlWithParameter);
|
||||
$this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter);
|
||||
}
|
||||
|
||||
public function testDumpWithSimpleLocalizedRoutes()
|
||||
{
|
||||
$this->routeCollection->add('test', (new Route('/foo')));
|
||||
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test'));
|
||||
$this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl')->setDefault('_canonical_route', 'test'));
|
||||
|
||||
$code = $this->generatorDumper->dump();
|
||||
file_put_contents($this->testTmpFilepath, $code);
|
||||
|
||||
$context = new RequestContext('/app.php');
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, $context, null, 'en');
|
||||
|
||||
$urlWithDefaultLocale = $projectUrlGenerator->generate('test');
|
||||
$urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', ['_locale' => 'nl']);
|
||||
$context->setParameter('_locale', 'en');
|
||||
$urlWithEnglishContext = $projectUrlGenerator->generate('test');
|
||||
$context->setParameter('_locale', 'nl');
|
||||
$urlWithDutchContext = $projectUrlGenerator->generate('test');
|
||||
|
||||
$this->assertEquals('/app.php/testing/is/fun', $urlWithDefaultLocale);
|
||||
$this->assertEquals('/app.php/testen/is/leuk', $urlWithSpecifiedLocale);
|
||||
$this->assertEquals('/app.php/testing/is/fun', $urlWithEnglishContext);
|
||||
$this->assertEquals('/app.php/testen/is/leuk', $urlWithDutchContext);
|
||||
|
||||
// test with full route name
|
||||
$this->assertEquals('/app.php/testing/is/fun', $projectUrlGenerator->generate('test.en'));
|
||||
|
||||
$context->setParameter('_locale', 'de_DE');
|
||||
// test that it fall backs to another route when there is no matching localized route
|
||||
$this->assertEquals('/app.php/foo', $projectUrlGenerator->generate('test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
|
||||
* @expectedExceptionMessage Unable to generate a URL for the named route "test" as such route does not exist.
|
||||
*/
|
||||
public function testDumpWithRouteNotFoundLocalizedRoutes()
|
||||
{
|
||||
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test'));
|
||||
|
||||
$code = $this->generatorDumper->dump();
|
||||
file_put_contents($this->testTmpFilepath, $code);
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext('/app.php'), null, 'pl_PL');
|
||||
$projectUrlGenerator->generate('test');
|
||||
}
|
||||
|
||||
public function testDumpWithFallbackLocaleLocalizedRoutes()
|
||||
{
|
||||
$this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_canonical_route', 'test'));
|
||||
$this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_canonical_route', 'test'));
|
||||
$this->routeCollection->add('test.fr', (new Route('/tester/est/amusant'))->setDefault('_canonical_route', 'test'));
|
||||
|
||||
$code = $this->generatorDumper->dump();
|
||||
file_put_contents($this->testTmpFilepath, $code);
|
||||
|
||||
$context = new RequestContext('/app.php');
|
||||
$context->setParameter('_locale', 'en_GB');
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, $context, null, null);
|
||||
|
||||
// test with context _locale
|
||||
$this->assertEquals('/app.php/testing/is/fun', $projectUrlGenerator->generate('test'));
|
||||
// test with parameters _locale
|
||||
$this->assertEquals('/app.php/testen/is/leuk', $projectUrlGenerator->generate('test', ['_locale' => 'nl_BE']));
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext('/app.php'), null, 'fr_CA');
|
||||
// test with default locale
|
||||
$this->assertEquals('/app.php/tester/est/amusant', $projectUrlGenerator->generate('test'));
|
||||
}
|
||||
|
||||
public function testDumpWithTooManyRoutes()
|
||||
{
|
||||
$this->routeCollection->add('Test', new Route('/testing/{foo}'));
|
||||
for ($i = 0; $i < 32769; ++$i) {
|
||||
$this->routeCollection->add('route_'.$i, new Route('/route_'.$i));
|
||||
}
|
||||
$this->routeCollection->add('Test2', new Route('/testing2'));
|
||||
|
||||
file_put_contents($this->largeTestTmpFilepath, $this->generatorDumper->dump());
|
||||
$this->routeCollection = $this->generatorDumper = null;
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->largeTestTmpFilepath, new RequestContext('/app.php'));
|
||||
|
||||
$absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$relativeUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
$relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
|
||||
$this->assertEquals('http://localhost/app.php/testing/bar', $absoluteUrlWithParameter);
|
||||
$this->assertEquals('http://localhost/app.php/testing2', $absoluteUrlWithoutParameter);
|
||||
$this->assertEquals('/app.php/testing/bar', $relativeUrlWithParameter);
|
||||
$this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testDumpWithoutRoutes()
|
||||
{
|
||||
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext('/app.php'));
|
||||
|
||||
$projectUrlGenerator->generate('Test', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
|
||||
*/
|
||||
public function testGenerateNonExistingRoute()
|
||||
{
|
||||
$this->routeCollection->add('Test', new Route('/test'));
|
||||
|
||||
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext());
|
||||
$url = $projectUrlGenerator->generate('NonExisting', []);
|
||||
}
|
||||
|
||||
public function testDumpForRouteWithDefaults()
|
||||
{
|
||||
$this->routeCollection->add('Test', new Route('/testing/{foo}', ['foo' => 'bar']));
|
||||
|
||||
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext());
|
||||
$url = $projectUrlGenerator->generate('Test', []);
|
||||
|
||||
$this->assertEquals('/testing', $url);
|
||||
}
|
||||
|
||||
public function testDumpWithSchemeRequirement()
|
||||
{
|
||||
$this->routeCollection->add('Test1', new Route('/testing', [], [], [], '', ['ftp', 'https']));
|
||||
|
||||
file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext('/app.php'));
|
||||
|
||||
$absoluteUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$relativeUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
|
||||
$this->assertEquals('ftp://localhost/app.php/testing', $absoluteUrl);
|
||||
$this->assertEquals('ftp://localhost/app.php/testing', $relativeUrl);
|
||||
|
||||
$projectUrlGenerator = new CompiledUrlGenerator(require $this->testTmpFilepath, new RequestContext('/app.php', 'GET', 'localhost', 'https'));
|
||||
|
||||
$absoluteUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_URL);
|
||||
$relativeUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
|
||||
$this->assertEquals('https://localhost/app.php/testing', $absoluteUrl);
|
||||
$this->assertEquals('/app.php/testing', $relativeUrl);
|
||||
}
|
||||
}
|
@ -18,6 +18,9 @@ use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class PhpGeneratorDumperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
|
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Matcher;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class CompiledRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest
|
||||
{
|
||||
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
||||
{
|
||||
$dumper = new CompiledUrlMatcherDumper($routes);
|
||||
$compiledRoutes = $dumper->getCompiledRoutes();
|
||||
|
||||
return $this->getMockBuilder(TestCompiledRedirectableUrlMatcher::class)
|
||||
->setConstructorArgs([$compiledRoutes, $context ?: new RequestContext()])
|
||||
->setMethods(['redirect'])
|
||||
->getMock();
|
||||
}
|
||||
}
|
||||
|
||||
class TestCompiledRedirectableUrlMatcher extends CompiledUrlMatcher implements RedirectableUrlMatcherInterface
|
||||
{
|
||||
public function redirect($path, $route, $scheme = null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Matcher;
|
||||
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class CompiledUrlMatcherTest extends UrlMatcherTest
|
||||
{
|
||||
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
||||
{
|
||||
$dumper = new CompiledUrlMatcherDumper($routes);
|
||||
|
||||
return new CompiledUrlMatcher($dumper->getCompiledRoutes(), $context ?: new RequestContext());
|
||||
}
|
||||
}
|
@ -17,6 +17,9 @@ use Symfony\Component\Routing\Matcher\UrlMatcher;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class DumpedRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest
|
||||
{
|
||||
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
||||
|
@ -15,6 +15,9 @@ use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class DumpedUrlMatcherTest extends UrlMatcherTest
|
||||
{
|
||||
protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null)
|
||||
|
@ -0,0 +1,496 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Routing\Tests\Matcher\Dumper;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
|
||||
use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
|
||||
use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
|
||||
use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
class CompiledUrlMatcherDumperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $dumpPath;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->dumpPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_matcher.'.uniqid('CompiledUrlMatcher').'.php';
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
@unlink($this->dumpPath);
|
||||
}
|
||||
|
||||
public function testRedirectPreservesUrlEncoding()
|
||||
{
|
||||
$collection = new RouteCollection();
|
||||
$collection->add('foo', new Route('/foo:bar/'));
|
||||
|
||||
$matcher = $this->generateDumpedMatcher($collection);
|
||||
$matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/', 'foo')->willReturn([]);
|
||||
|
||||
$matcher->match('/foo%3Abar');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getRouteCollections
|
||||
*/
|
||||
public function testDump(RouteCollection $collection, $fixture)
|
||||
{
|
||||
$basePath = __DIR__.'/../../Fixtures/dumper/';
|
||||
|
||||
$dumper = new CompiledUrlMatcherDumper($collection);
|
||||
$this->assertStringEqualsFile($basePath.$fixture, $dumper->dump());
|
||||
}
|
||||
|
||||
public function getRouteCollections()
|
||||
{
|
||||
/* test case 1 */
|
||||
|
||||
$collection = new RouteCollection();
|
||||
|
||||
$collection->add('overridden', new Route('/overridden'));
|
||||
|
||||
// defaults and requirements
|
||||
$collection->add('foo', new Route(
|
||||
'/foo/{bar}',
|
||||
['def' => 'test'],
|
||||
['bar' => 'baz|symfony']
|
||||
));
|
||||
// method requirement
|
||||
$collection->add('bar', new Route(
|
||||
'/bar/{foo}',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['GET', 'head']
|
||||
));
|
||||
// GET method requirement automatically adds HEAD as valid
|
||||
$collection->add('barhead', new Route(
|
||||
'/barhead/{foo}',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['GET']
|
||||
));
|
||||
// simple
|
||||
$collection->add('baz', new Route(
|
||||
'/test/baz'
|
||||
));
|
||||
// simple with extension
|
||||
$collection->add('baz2', new Route(
|
||||
'/test/baz.html'
|
||||
));
|
||||
// trailing slash
|
||||
$collection->add('baz3', new Route(
|
||||
'/test/baz3/'
|
||||
));
|
||||
// trailing slash with variable
|
||||
$collection->add('baz4', new Route(
|
||||
'/test/{foo}/'
|
||||
));
|
||||
// trailing slash and method
|
||||
$collection->add('baz5', new Route(
|
||||
'/test/{foo}/',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['post']
|
||||
));
|
||||
// complex name
|
||||
$collection->add('baz.baz6', new Route(
|
||||
'/test/{foo}/',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['put']
|
||||
));
|
||||
// defaults without variable
|
||||
$collection->add('foofoo', new Route(
|
||||
'/foofoo',
|
||||
['def' => 'test']
|
||||
));
|
||||
// pattern with quotes
|
||||
$collection->add('quoter', new Route(
|
||||
'/{quoter}',
|
||||
[],
|
||||
['quoter' => '[\']+']
|
||||
));
|
||||
// space in pattern
|
||||
$collection->add('space', new Route(
|
||||
'/spa ce'
|
||||
));
|
||||
|
||||
// prefixes
|
||||
$collection1 = new RouteCollection();
|
||||
$collection1->add('overridden', new Route('/overridden1'));
|
||||
$collection1->add('foo1', (new Route('/{foo}'))->setMethods('PUT'));
|
||||
$collection1->add('bar1', new Route('/{bar}'));
|
||||
$collection1->addPrefix('/b\'b');
|
||||
$collection2 = new RouteCollection();
|
||||
$collection2->addCollection($collection1);
|
||||
$collection2->add('overridden', new Route('/{var}', [], ['var' => '.*']));
|
||||
$collection1 = new RouteCollection();
|
||||
$collection1->add('foo2', new Route('/{foo1}'));
|
||||
$collection1->add('bar2', new Route('/{bar1}'));
|
||||
$collection1->addPrefix('/b\'b');
|
||||
$collection2->addCollection($collection1);
|
||||
$collection2->addPrefix('/a');
|
||||
$collection->addCollection($collection2);
|
||||
|
||||
// overridden through addCollection() and multiple sub-collections with no own prefix
|
||||
$collection1 = new RouteCollection();
|
||||
$collection1->add('overridden2', new Route('/old'));
|
||||
$collection1->add('helloWorld', new Route('/hello/{who}', ['who' => 'World!']));
|
||||
$collection2 = new RouteCollection();
|
||||
$collection3 = new RouteCollection();
|
||||
$collection3->add('overridden2', new Route('/new'));
|
||||
$collection3->add('hey', new Route('/hey/'));
|
||||
$collection2->addCollection($collection3);
|
||||
$collection1->addCollection($collection2);
|
||||
$collection1->addPrefix('/multi');
|
||||
$collection->addCollection($collection1);
|
||||
|
||||
// "dynamic" prefix
|
||||
$collection1 = new RouteCollection();
|
||||
$collection1->add('foo3', new Route('/{foo}'));
|
||||
$collection1->add('bar3', new Route('/{bar}'));
|
||||
$collection1->addPrefix('/b');
|
||||
$collection1->addPrefix('{_locale}');
|
||||
$collection->addCollection($collection1);
|
||||
|
||||
// route between collections
|
||||
$collection->add('ababa', new Route('/ababa'));
|
||||
|
||||
// collection with static prefix but only one route
|
||||
$collection1 = new RouteCollection();
|
||||
$collection1->add('foo4', new Route('/{foo}'));
|
||||
$collection1->addPrefix('/aba');
|
||||
$collection->addCollection($collection1);
|
||||
|
||||
// prefix and host
|
||||
|
||||
$collection1 = new RouteCollection();
|
||||
|
||||
$route1 = new Route('/route1', [], [], [], 'a.example.com');
|
||||
$collection1->add('route1', $route1);
|
||||
|
||||
$route2 = new Route('/c2/route2', [], [], [], 'a.example.com');
|
||||
$collection1->add('route2', $route2);
|
||||
|
||||
$route3 = new Route('/c2/route3', [], [], [], 'b.example.com');
|
||||
$collection1->add('route3', $route3);
|
||||
|
||||
$route4 = new Route('/route4', [], [], [], 'a.example.com');
|
||||
$collection1->add('route4', $route4);
|
||||
|
||||
$route5 = new Route('/route5', [], [], [], 'c.example.com');
|
||||
$collection1->add('route5', $route5);
|
||||
|
||||
$route6 = new Route('/route6', [], [], [], null);
|
||||
$collection1->add('route6', $route6);
|
||||
|
||||
$collection->addCollection($collection1);
|
||||
|
||||
// host and variables
|
||||
|
||||
$collection1 = new RouteCollection();
|
||||
|
||||
$route11 = new Route('/route11', [], [], [], '{var1}.example.com');
|
||||
$collection1->add('route11', $route11);
|
||||
|
||||
$route12 = new Route('/route12', ['var1' => 'val'], [], [], '{var1}.example.com');
|
||||
$collection1->add('route12', $route12);
|
||||
|
||||
$route13 = new Route('/route13/{name}', [], [], [], '{var1}.example.com');
|
||||
$collection1->add('route13', $route13);
|
||||
|
||||
$route14 = new Route('/route14/{name}', ['var1' => 'val'], [], [], '{var1}.example.com');
|
||||
$collection1->add('route14', $route14);
|
||||
|
||||
$route15 = new Route('/route15/{name}', [], [], [], 'c.example.com');
|
||||
$collection1->add('route15', $route15);
|
||||
|
||||
$route16 = new Route('/route16/{name}', ['var1' => 'val'], [], [], null);
|
||||
$collection1->add('route16', $route16);
|
||||
|
||||
$route17 = new Route('/route17', [], [], [], null);
|
||||
$collection1->add('route17', $route17);
|
||||
|
||||
$collection->addCollection($collection1);
|
||||
|
||||
// multiple sub-collections with a single route and a prefix each
|
||||
$collection1 = new RouteCollection();
|
||||
$collection1->add('a', new Route('/a...'));
|
||||
$collection2 = new RouteCollection();
|
||||
$collection2->add('b', new Route('/{var}'));
|
||||
$collection3 = new RouteCollection();
|
||||
$collection3->add('c', new Route('/{var}'));
|
||||
$collection3->addPrefix('/c');
|
||||
$collection2->addCollection($collection3);
|
||||
$collection2->addPrefix('/b');
|
||||
$collection1->addCollection($collection2);
|
||||
$collection1->addPrefix('/a');
|
||||
$collection->addCollection($collection1);
|
||||
|
||||
/* test case 2 */
|
||||
|
||||
$redirectCollection = clone $collection;
|
||||
|
||||
// force HTTPS redirection
|
||||
$redirectCollection->add('secure', new Route(
|
||||
'/secure',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
['https']
|
||||
));
|
||||
|
||||
// force HTTP redirection
|
||||
$redirectCollection->add('nonsecure', new Route(
|
||||
'/nonsecure',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
['http']
|
||||
));
|
||||
|
||||
/* test case 3 */
|
||||
|
||||
$rootprefixCollection = new RouteCollection();
|
||||
$rootprefixCollection->add('static', new Route('/test'));
|
||||
$rootprefixCollection->add('dynamic', new Route('/{var}'));
|
||||
$rootprefixCollection->addPrefix('rootprefix');
|
||||
$route = new Route('/with-condition');
|
||||
$route->setCondition('context.getMethod() == "GET"');
|
||||
$rootprefixCollection->add('with-condition', $route);
|
||||
|
||||
/* test case 4 */
|
||||
$headMatchCasesCollection = new RouteCollection();
|
||||
$headMatchCasesCollection->add('just_head', new Route(
|
||||
'/just_head',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['HEAD']
|
||||
));
|
||||
$headMatchCasesCollection->add('head_and_get', new Route(
|
||||
'/head_and_get',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['HEAD', 'GET']
|
||||
));
|
||||
$headMatchCasesCollection->add('get_and_head', new Route(
|
||||
'/get_and_head',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['GET', 'HEAD']
|
||||
));
|
||||
$headMatchCasesCollection->add('post_and_head', new Route(
|
||||
'/post_and_head',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['POST', 'HEAD']
|
||||
));
|
||||
$headMatchCasesCollection->add('put_and_post', new Route(
|
||||
'/put_and_post',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['PUT', 'POST']
|
||||
));
|
||||
$headMatchCasesCollection->add('put_and_get_and_head', new Route(
|
||||
'/put_and_post',
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
'',
|
||||
[],
|
||||
['PUT', 'GET', 'HEAD']
|
||||
));
|
||||
|
||||
/* test case 5 */
|
||||
$groupOptimisedCollection = new RouteCollection();
|
||||
$groupOptimisedCollection->add('a_first', new Route('/a/11'));
|
||||
$groupOptimisedCollection->add('a_second', new Route('/a/22'));
|
||||
$groupOptimisedCollection->add('a_third', new Route('/a/333'));
|
||||
$groupOptimisedCollection->add('a_wildcard', new Route('/{param}'));
|
||||
$groupOptimisedCollection->add('a_fourth', new Route('/a/44/'));
|
||||
$groupOptimisedCollection->add('a_fifth', new Route('/a/55/'));
|
||||
$groupOptimisedCollection->add('a_sixth', new Route('/a/66/'));
|
||||
$groupOptimisedCollection->add('nested_wildcard', new Route('/nested/{param}'));
|
||||
$groupOptimisedCollection->add('nested_a', new Route('/nested/group/a/'));
|
||||
$groupOptimisedCollection->add('nested_b', new Route('/nested/group/b/'));
|
||||
$groupOptimisedCollection->add('nested_c', new Route('/nested/group/c/'));
|
||||
|
||||
$groupOptimisedCollection->add('slashed_a', new Route('/slashed/group/'));
|
||||
$groupOptimisedCollection->add('slashed_b', new Route('/slashed/group/b/'));
|
||||
$groupOptimisedCollection->add('slashed_c', new Route('/slashed/group/c/'));
|
||||
|
||||
/* test case 6 & 7 */
|
||||
$trailingSlashCollection = new RouteCollection();
|
||||
$trailingSlashCollection->add('simple_trailing_slash_no_methods', new Route('/trailing/simple/no-methods/', [], [], [], '', [], []));
|
||||
$trailingSlashCollection->add('simple_trailing_slash_GET_method', new Route('/trailing/simple/get-method/', [], [], [], '', [], ['GET']));
|
||||
$trailingSlashCollection->add('simple_trailing_slash_HEAD_method', new Route('/trailing/simple/head-method/', [], [], [], '', [], ['HEAD']));
|
||||
$trailingSlashCollection->add('simple_trailing_slash_POST_method', new Route('/trailing/simple/post-method/', [], [], [], '', [], ['POST']));
|
||||
$trailingSlashCollection->add('regex_trailing_slash_no_methods', new Route('/trailing/regex/no-methods/{param}/', [], [], [], '', [], []));
|
||||
$trailingSlashCollection->add('regex_trailing_slash_GET_method', new Route('/trailing/regex/get-method/{param}/', [], [], [], '', [], ['GET']));
|
||||
$trailingSlashCollection->add('regex_trailing_slash_HEAD_method', new Route('/trailing/regex/head-method/{param}/', [], [], [], '', [], ['HEAD']));
|
||||
$trailingSlashCollection->add('regex_trailing_slash_POST_method', new Route('/trailing/regex/post-method/{param}/', [], [], [], '', [], ['POST']));
|
||||
|
||||
$trailingSlashCollection->add('simple_not_trailing_slash_no_methods', new Route('/not-trailing/simple/no-methods', [], [], [], '', [], []));
|
||||
$trailingSlashCollection->add('simple_not_trailing_slash_GET_method', new Route('/not-trailing/simple/get-method', [], [], [], '', [], ['GET']));
|
||||
$trailingSlashCollection->add('simple_not_trailing_slash_HEAD_method', new Route('/not-trailing/simple/head-method', [], [], [], '', [], ['HEAD']));
|
||||
$trailingSlashCollection->add('simple_not_trailing_slash_POST_method', new Route('/not-trailing/simple/post-method', [], [], [], '', [], ['POST']));
|
||||
$trailingSlashCollection->add('regex_not_trailing_slash_no_methods', new Route('/not-trailing/regex/no-methods/{param}', [], [], [], '', [], []));
|
||||
$trailingSlashCollection->add('regex_not_trailing_slash_GET_method', new Route('/not-trailing/regex/get-method/{param}', [], [], [], '', [], ['GET']));
|
||||
$trailingSlashCollection->add('regex_not_trailing_slash_HEAD_method', new Route('/not-trailing/regex/head-method/{param}', [], [], [], '', [], ['HEAD']));
|
||||
$trailingSlashCollection->add('regex_not_trailing_slash_POST_method', new Route('/not-trailing/regex/post-method/{param}', [], [], [], '', [], ['POST']));
|
||||
|
||||
/* test case 8 */
|
||||
$unicodeCollection = new RouteCollection();
|
||||
$unicodeCollection->add('a', new Route('/{a}', [], ['a' => 'a'], ['utf8' => false]));
|
||||
$unicodeCollection->add('b', new Route('/{a}', [], ['a' => '.'], ['utf8' => true]));
|
||||
$unicodeCollection->add('c', new Route('/{a}', [], ['a' => '.'], ['utf8' => false]));
|
||||
|
||||
/* test case 9 */
|
||||
$hostTreeCollection = new RouteCollection();
|
||||
$hostTreeCollection->add('a', (new Route('/'))->setHost('{d}.e.c.b.a'));
|
||||
$hostTreeCollection->add('b', (new Route('/'))->setHost('d.c.b.a'));
|
||||
$hostTreeCollection->add('c', (new Route('/'))->setHost('{e}.e.c.b.a'));
|
||||
|
||||
/* test case 10 */
|
||||
$chunkedCollection = new RouteCollection();
|
||||
for ($i = 0; $i < 1000; ++$i) {
|
||||
$h = substr(md5($i), 0, 6);
|
||||
$chunkedCollection->add('_'.$i, new Route('/'.$h.'/{a}/{b}/{c}/'.$h));
|
||||
}
|
||||
|
||||
/* test case 11 */
|
||||
$demoCollection = new RouteCollection();
|
||||
$demoCollection->add('a', new Route('/admin/post/'));
|
||||
$demoCollection->add('b', new Route('/admin/post/new'));
|
||||
$demoCollection->add('c', (new Route('/admin/post/{id}'))->setRequirements(['id' => '\d+']));
|
||||
$demoCollection->add('d', (new Route('/admin/post/{id}/edit'))->setRequirements(['id' => '\d+']));
|
||||
$demoCollection->add('e', (new Route('/admin/post/{id}/delete'))->setRequirements(['id' => '\d+']));
|
||||
$demoCollection->add('f', new Route('/blog/'));
|
||||
$demoCollection->add('g', new Route('/blog/rss.xml'));
|
||||
$demoCollection->add('h', (new Route('/blog/page/{page}'))->setRequirements(['id' => '\d+']));
|
||||
$demoCollection->add('i', (new Route('/blog/posts/{page}'))->setRequirements(['id' => '\d+']));
|
||||
$demoCollection->add('j', (new Route('/blog/comments/{id}/new'))->setRequirements(['id' => '\d+']));
|
||||
$demoCollection->add('k', new Route('/blog/search'));
|
||||
$demoCollection->add('l', new Route('/login'));
|
||||
$demoCollection->add('m', new Route('/logout'));
|
||||
$demoCollection->addPrefix('/{_locale}');
|
||||
$demoCollection->add('n', new Route('/{_locale}'));
|
||||
$demoCollection->addRequirements(['_locale' => 'en|fr']);
|
||||
$demoCollection->addDefaults(['_locale' => 'en']);
|
||||
|
||||
/* test case 12 */
|
||||
$suffixCollection = new RouteCollection();
|
||||
$suffixCollection->add('r1', new Route('abc{foo}/1'));
|
||||
$suffixCollection->add('r2', new Route('abc{foo}/2'));
|
||||
$suffixCollection->add('r10', new Route('abc{foo}/10'));
|
||||
$suffixCollection->add('r20', new Route('abc{foo}/20'));
|
||||
$suffixCollection->add('r100', new Route('abc{foo}/100'));
|
||||
$suffixCollection->add('r200', new Route('abc{foo}/200'));
|
||||
|
||||
/* test case 13 */
|
||||
$hostCollection = new RouteCollection();
|
||||
$hostCollection->add('r1', (new Route('abc{foo}'))->setHost('{foo}.exampple.com'));
|
||||
$hostCollection->add('r2', (new Route('abc{foo}'))->setHost('{foo}.exampple.com'));
|
||||
|
||||
return [
|
||||
[new RouteCollection(), 'compiled_url_matcher0.php'],
|
||||
[$collection, 'compiled_url_matcher1.php'],
|
||||
[$redirectCollection, 'compiled_url_matcher2.php'],
|
||||
[$rootprefixCollection, 'compiled_url_matcher3.php'],
|
||||
[$headMatchCasesCollection, 'compiled_url_matcher4.php'],
|
||||
[$groupOptimisedCollection, 'compiled_url_matcher5.php'],
|
||||
[$trailingSlashCollection, 'compiled_url_matcher6.php'],
|
||||
[$trailingSlashCollection, 'compiled_url_matcher7.php'],
|
||||
[$unicodeCollection, 'compiled_url_matcher8.php'],
|
||||
[$hostTreeCollection, 'compiled_url_matcher9.php'],
|
||||
[$chunkedCollection, 'compiled_url_matcher10.php'],
|
||||
[$demoCollection, 'compiled_url_matcher11.php'],
|
||||
[$suffixCollection, 'compiled_url_matcher12.php'],
|
||||
[$hostCollection, 'compiled_url_matcher13.php'],
|
||||
];
|
||||
}
|
||||
|
||||
private function generateDumpedMatcher(RouteCollection $collection)
|
||||
{
|
||||
$dumper = new CompiledUrlMatcherDumper($collection);
|
||||
$code = $dumper->dump();
|
||||
|
||||
file_put_contents($this->dumpPath, $code);
|
||||
$compiledRoutes = require $this->dumpPath;
|
||||
|
||||
return $this->getMockBuilder(TestCompiledUrlMatcher::class)
|
||||
->setConstructorArgs([$compiledRoutes, new RequestContext()])
|
||||
->setMethods(['redirect'])
|
||||
->getMock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
* @expectedExceptionMessage Symfony\Component\Routing\Route cannot contain objects
|
||||
*/
|
||||
public function testGenerateDumperMatcherWithObject()
|
||||
{
|
||||
$routeCollection = new RouteCollection();
|
||||
$routeCollection->add('_', new Route('/', [new \stdClass()]));
|
||||
$dumper = new CompiledUrlMatcherDumper($routeCollection);
|
||||
$dumper->dump();
|
||||
}
|
||||
}
|
||||
|
||||
class TestCompiledUrlMatcher extends CompiledUrlMatcher implements RedirectableUrlMatcherInterface
|
||||
{
|
||||
public function redirect($path, $route, $scheme = null)
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -19,6 +19,9 @@ use Symfony\Component\Routing\RequestContext;
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class PhpMatcherDumperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
|
Reference in New Issue
Block a user