[Routing] allow using compiled matchers and generators without dumping PHP code

This commit is contained in:
Nicolas Grekas 2018-10-08 20:15:25 +02:00
parent a9f8ca57af
commit f0a519ac7d
51 changed files with 5144 additions and 490 deletions

View File

@ -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']);

View File

@ -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" />

View File

@ -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,
];
}
}

View File

@ -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
{

View File

@ -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);
}
}

View File

@ -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()

View File

@ -1,6 +1,13 @@
CHANGELOG
=========
4.3.0
-----
* added `CompiledUrlMatcher` and `CompiledUrlMatcherDumper`
* added `CompiledUrlGenerator` and `CompiledUrlGeneratorDumper`
* deprecated `PhpUrlGeneratorDumped` and `PhpMatcherDumper`
4.2.0
-----

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 .= ' ]';

View 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;
}
}

View File

@ -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);
}
}

View File

@ -21,7 +21,7 @@ use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface;
*
* @internal
*/
trait PhpMatcherTrait
trait CompiledUrlMatcherTrait
{
private $matchHost = false;
private $staticRoutes = [];
@ -126,8 +126,13 @@ 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)) {
continue;
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) {

View File

@ -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);
}
}

View File

@ -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,11 +358,15 @@ class Router implements RouterInterface, RequestMatcherInterface
}
);
if (!class_exists($this->options['generator_cache_class'], false)) {
require_once $cache->getPath();
}
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);
$this->generator = new $this->options['generator_cache_class']($this->context, $this->logger);
}
}
if ($this->generator instanceof ConfigurableRequirementsInterface) {

View File

@ -0,0 +1,17 @@
<?php
/**
* This file has been auto-generated
* by the Symfony Routing Component.
*/
return [
false, // $matchHost
[ // $staticRoutes
],
[ // $regexpList
],
[ // $dynamicRoutes
],
null, // $checkCondition
];

View File

@ -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

View File

@ -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
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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");
}
},
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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
];

View File

@ -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)
{

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}

View File

@ -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],
],
];
}
}

View File

@ -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) {

View File

@ -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)
{

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}
}

View File

@ -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],
],
];
}
}

View File

@ -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)
{

View File

@ -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);
}
}

View File

@ -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
{
/**

View File

@ -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 [];
}
}

View File

@ -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());
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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 [];
}
}

View File

@ -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
{
/**