Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
58.33% covered (warning)
58.33%
7 / 12
CRAP
97.42% covered (success)
97.42%
227 / 233
CompiledUrlMatcherDumper
0.00% covered (danger)
0.00%
0 / 1
58.33% covered (warning)
58.33%
7 / 12
83
97.42% covered (success)
97.42%
227 / 233
 dump
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 addExpressionLanguageProvider
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 getCompiledRoutes
0.00% covered (danger)
0.00%
0 / 1
14
97.22% covered (success)
97.22%
35 / 36
 generateCompiledRoutes
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
19 / 19
 groupStaticRoutes
100.00% covered (success)
100.00%
1 / 1
14
100.00% covered (success)
100.00%
25 / 25
 compileStaticRoutes
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
8 / 8
 compileDynamicRoutes
0.00% covered (danger)
0.00%
0 / 1
19
98.63% covered (success)
98.63%
72 / 73
 compileStaticPrefixCollection
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
29 / 29
 compileRoute
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
15 / 15
 getExpressionLanguage
0.00% covered (danger)
0.00%
0 / 1
3.07
80.00% covered (warning)
80.00%
4 / 5
 indent
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 export
0.00% covered (danger)
0.00%
0 / 1
9.01
94.44% covered (success)
94.44%
17 / 18
<?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('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 = [];