PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

View File

@ -0,0 +1,272 @@
<?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;
@trigger_error('The '.__NAMESPACE__.'\ApacheMatcherDumper class is deprecated since Symfony 2.5 and will be removed in 3.0. It\'s hard to replicate the behaviour of the PHP implementation and the performance gains are minimal.', E_USER_DEPRECATED);
use Symfony\Component\Routing\Route;
/**
* Dumps a set of Apache mod_rewrite rules.
*
* @deprecated since version 2.5, to be removed in 3.0.
* The performance gains are minimal and it's very hard to replicate
* the behavior of PHP implementation.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Kris Wallsmith <kris@symfony.com>
*/
class ApacheMatcherDumper extends MatcherDumper
{
/**
* Dumps a set of Apache mod_rewrite rules.
*
* Available options:
*
* * script_name: The script name (app.php by default)
* * base_uri: The base URI ("" by default)
*
* @return string A string to be used as Apache rewrite rules
*
* @throws \LogicException When the route regex is invalid
*/
public function dump(array $options = array())
{
$options = array_merge(array(
'script_name' => 'app.php',
'base_uri' => '',
), $options);
$options['script_name'] = self::escape($options['script_name'], ' ', '\\');
$rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]");
$methodVars = array();
$hostRegexUnique = 0;
$prevHostRegex = '';
foreach ($this->getRoutes()->all() as $name => $route) {
if ($route->getCondition()) {
throw new \LogicException(sprintf('Unable to dump the routes for Apache as route "%s" has a condition.', $name));
}
$compiledRoute = $route->compile();
$hostRegex = $compiledRoute->getHostRegex();
if (null !== $hostRegex && $prevHostRegex !== $hostRegex) {
$prevHostRegex = $hostRegex;
++$hostRegexUnique;
$rule = array();
$regex = $this->regexToApacheRegex($hostRegex);
$regex = self::escape($regex, ' ', '\\');
$rule[] = sprintf('RewriteCond %%{HTTP:Host} %s', $regex);
$variables = array();
$variables[] = sprintf('E=__ROUTING_host_%s:1', $hostRegexUnique);
foreach ($compiledRoute->getHostVariables() as $i => $variable) {
$variables[] = sprintf('E=__ROUTING_host_%s_%s:%%%d', $hostRegexUnique, $variable, $i + 1);
}
$variables = implode(',', $variables);
$rule[] = sprintf('RewriteRule .? - [%s]', $variables);
$rules[] = implode("\n", $rule);
}
$rules[] = $this->dumpRoute($name, $route, $options, $hostRegexUnique);
$methodVars = array_merge($methodVars, $route->getMethods());
}
if (0 < \count($methodVars)) {
$rule = array('# 405 Method Not Allowed');
$methodVars = array_values(array_unique($methodVars));
if (\in_array('GET', $methodVars) && !\in_array('HEAD', $methodVars)) {
$methodVars[] = 'HEAD';
}
foreach ($methodVars as $i => $methodVar) {
$rule[] = sprintf('RewriteCond %%{ENV:_ROUTING__allow_%s} =1%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : '');
}
$rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']);
$rules[] = implode("\n", $rule);
}
return implode("\n\n", $rules)."\n";
}
/**
* Dumps a single route.
*
* @param string $name Route name
* @param Route $route The route
* @param array $options Options
* @param bool $hostRegexUnique Unique identifier for the host regex
*
* @return string The compiled route
*/
private function dumpRoute($name, $route, array $options, $hostRegexUnique)
{
$compiledRoute = $route->compile();
// prepare the apache regex
$regex = $this->regexToApacheRegex($compiledRoute->getRegex());
$regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\');
$methods = $this->getRouteMethods($route);
$hasTrailingSlash = (!$methods || \in_array('HEAD', $methods)) && '/$' === substr($regex, -2) && '^/$' !== $regex;
$variables = array('E=_ROUTING_route:'.$name);
foreach ($compiledRoute->getHostVariables() as $variable) {
$variables[] = sprintf('E=_ROUTING_param_%s:%%{ENV:__ROUTING_host_%s_%s}', $variable, $hostRegexUnique, $variable);
}
foreach ($compiledRoute->getPathVariables() as $i => $variable) {
$variables[] = 'E=_ROUTING_param_'.$variable.':%'.($i + 1);
}
foreach ($this->normalizeValues($route->getDefaults()) as $key => $value) {
$variables[] = 'E=_ROUTING_default_'.$key.':'.strtr($value, array(
':' => '\\:',
'=' => '\\=',
'\\' => '\\\\',
' ' => '\\ ',
));
}
$variables = implode(',', $variables);
$rule = array("# $name");
// method mismatch
if (0 < \count($methods)) {
$allow = array();
foreach ($methods as $method) {
$allow[] = 'E=_ROUTING_allow_'.$method.':1';
}
if ($compiledRoute->getHostRegex()) {
$rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
}
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
$rule[] = sprintf('RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]', implode('|', $methods));
$rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow));
}
// redirect with trailing slash appended
if ($hasTrailingSlash) {
if ($compiledRoute->getHostRegex()) {
$rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
}
$rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$';
$rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]';
}
// the main rule
if ($compiledRoute->getHostRegex()) {
$rule[] = sprintf('RewriteCond %%{ENV:__ROUTING_host_%s} =1', $hostRegexUnique);
}
$rule[] = "RewriteCond %{REQUEST_URI} $regex";
$rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]";
return implode("\n", $rule);
}
/**
* Returns methods allowed for a route.
*
* @return array The methods
*/
private function getRouteMethods(Route $route)
{
$methods = $route->getMethods();
// GET and HEAD are equivalent
if (\in_array('GET', $methods) && !\in_array('HEAD', $methods)) {
$methods[] = 'HEAD';
}
return $methods;
}
/**
* Converts a regex to make it suitable for mod_rewrite.
*
* @param string $regex The regex
*
* @return string The converted regex
*/
private function regexToApacheRegex($regex)
{
$regexPatternEnd = strrpos($regex, $regex[0]);
return preg_replace('/\?P<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1));
}
/**
* Escapes a string.
*
* @param string $string The string to be escaped
* @param string $char The character to be escaped
* @param string $with The character to be used for escaping
*
* @return string The escaped string
*/
private static function escape($string, $char, $with)
{
$escaped = false;
$output = '';
foreach (str_split($string) as $symbol) {
if ($escaped) {
$output .= $symbol;
$escaped = false;
continue;
}
if ($symbol === $char) {
$output .= $with.$char;
continue;
}
if ($symbol === $with) {
$escaped = true;
}
$output .= $symbol;
}
return $output;
}
/**
* Normalizes an array of values.
*
* @return string[]
*/
private function normalizeValues(array $values)
{
$normalizedValues = array();
foreach ($values as $key => $value) {
if (\is_array($value)) {
foreach ($value as $index => $bit) {
$normalizedValues[sprintf('%s[%s]', $key, $index)] = $bit;
}
} else {
$normalizedValues[$key] = (string) $value;
}
}
return $normalizedValues;
}
}

View File

@ -0,0 +1,159 @@
<?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;
/**
* Collection of routes.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*
* @internal
*/
class DumperCollection implements \IteratorAggregate
{
/**
* @var DumperCollection|null
*/
private $parent;
/**
* @var DumperCollection[]|DumperRoute[]
*/
private $children = array();
/**
* @var array
*/
private $attributes = array();
/**
* Returns the children routes and collections.
*
* @return self[]|DumperRoute[]
*/
public function all()
{
return $this->children;
}
/**
* Adds a route or collection.
*
* @param DumperRoute|DumperCollection The route or collection
*/
public function add($child)
{
if ($child instanceof self) {
$child->setParent($this);
}
$this->children[] = $child;
}
/**
* Sets children.
*
* @param array $children The children
*/
public function setAll(array $children)
{
foreach ($children as $child) {
if ($child instanceof self) {
$child->setParent($this);
}
}
$this->children = $children;
}
/**
* Returns an iterator over the children.
*
* @return \Iterator|DumperCollection[]|DumperRoute[] The iterator
*/
public function getIterator()
{
return new \ArrayIterator($this->children);
}
/**
* Returns the root of the collection.
*
* @return self The root collection
*/
public function getRoot()
{
return (null !== $this->parent) ? $this->parent->getRoot() : $this;
}
/**
* Returns the parent collection.
*
* @return self|null The parent collection or null if the collection has no parent
*/
protected function getParent()
{
return $this->parent;
}
/**
* Sets the parent collection.
*/
protected function setParent(DumperCollection $parent)
{
$this->parent = $parent;
}
/**
* Returns true if the attribute is defined.
*
* @param string $name The attribute name
*
* @return bool true if the attribute is defined, false otherwise
*/
public function hasAttribute($name)
{
return array_key_exists($name, $this->attributes);
}
/**
* Returns an attribute by name.
*
* @param string $name The attribute name
* @param mixed $default Default value is the attribute doesn't exist
*
* @return mixed The attribute value
*/
public function getAttribute($name, $default = null)
{
return $this->hasAttribute($name) ? $this->attributes[$name] : $default;
}
/**
* Sets an attribute by name.
*
* @param string $name The attribute name
* @param mixed $value The attribute value
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* Sets multiple attributes.
*
* @param array $attributes The attributes
*/
public function setAttributes($attributes)
{
$this->attributes = $attributes;
}
}

View File

@ -0,0 +1,105 @@
<?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;
/**
* Prefix tree of routes preserving routes order.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*
* @internal
*/
class DumperPrefixCollection extends DumperCollection
{
/**
* @var string
*/
private $prefix = '';
/**
* Returns the prefix.
*
* @return string The prefix
*/
public function getPrefix()
{
return $this->prefix;
}
/**
* Sets the prefix.
*
* @param string $prefix The prefix
*/
public function setPrefix($prefix)
{
$this->prefix = $prefix;
}
/**
* Adds a route in the tree.
*
* @return self
*
* @throws \LogicException
*/
public function addPrefixRoute(DumperRoute $route)
{
$prefix = $route->getRoute()->compile()->getStaticPrefix();
for ($collection = $this; null !== $collection; $collection = $collection->getParent()) {
// Same prefix, add to current leave
if ($collection->prefix === $prefix) {
$collection->add($route);
return $collection;
}
// Prefix starts with route's prefix
if ('' === $collection->prefix || 0 === strpos($prefix, $collection->prefix)) {
$child = new self();
$child->setPrefix(substr($prefix, 0, \strlen($collection->prefix) + 1));
$collection->add($child);
return $child->addPrefixRoute($route);
}
}
// Reached only if the root has a non empty prefix
throw new \LogicException('The collection root must not have a prefix');
}
/**
* Merges nodes whose prefix ends with a slash.
*
* Children of a node whose prefix ends with a slash are moved to the parent node
*/
public function mergeSlashNodes()
{
$children = array();
foreach ($this as $child) {
if ($child instanceof self) {
$child->mergeSlashNodes();
if ('/' === substr($child->prefix, -1)) {
$children = array_merge($children, $child->all());
} else {
$children[] = $child;
}
} else {
$children[] = $child;
}
}
$this->setAll($children);
}
}

View File

@ -0,0 +1,57 @@
<?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\Routing\Route;
/**
* Container for a Route.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*
* @internal
*/
class DumperRoute
{
private $name;
private $route;
/**
* @param string $name The route name
* @param Route $route The route
*/
public function __construct($name, Route $route)
{
$this->name = $name;
$this->route = $route;
}
/**
* Returns the route name.
*
* @return string The route name
*/
public function getName()
{
return $this->name;
}
/**
* Returns the route.
*
* @return Route The route
*/
public function getRoute()
{
return $this->route;
}
}

View File

@ -0,0 +1,37 @@
<?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\Routing\RouteCollection;
/**
* MatcherDumper is the abstract class for all built-in matcher dumpers.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class MatcherDumper implements MatcherDumperInterface
{
private $routes;
public function __construct(RouteCollection $routes)
{
$this->routes = $routes;
}
/**
* {@inheritdoc}
*/
public function getRoutes()
{
return $this->routes;
}
}

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\Component\Routing\Matcher\Dumper;
use Symfony\Component\Routing\RouteCollection;
/**
* MatcherDumperInterface is the interface that all matcher dumper classes must implement.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
interface MatcherDumperInterface
{
/**
* Dumps a set of routes to a string representation of executable code
* that can then be used to match a request against these routes.
*
* @param array $options An array of options
*
* @return string Executable code
*/
public function dump(array $options = array());
/**
* Gets the routes to dump.
*
* @return RouteCollection A RouteCollection instance
*/
public function getRoutes();
}

View File

@ -0,0 +1,432 @@
<?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;
/**
* PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Tobias Schultze <http://tobion.de>
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
*/
class PhpMatcherDumper extends MatcherDumper
{
private $expressionLanguage;
/**
* @var ExpressionFunctionProviderInterface[]
*/
private $expressionLanguageProviders = array();
/**
* Dumps a set of routes to a PHP class.
*
* Available options:
*
* * class: The class name
* * base_class: The base class name
*
* @param array $options An array of options
*
* @return string A PHP class representing the matcher class
*/
public function dump(array $options = array())
{
$options = array_replace(array(
'class' => 'ProjectUrlMatcher',
'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']);
$supportsRedirections = isset($interfaces['Symfony\\Component\\Routing\\Matcher\\RedirectableUrlMatcherInterface']);
return <<<EOF
<?php
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Symfony\Component\Routing\Exception\ResourceNotFoundException;
use Symfony\Component\Routing\RequestContext;
/**
* This class has been auto-generated
* by the Symfony Routing Component.
*/
class {$options['class']} extends {$options['base_class']}
{
public function __construct(RequestContext \$context)
{
\$this->context = \$context;
}
{$this->generateMatchMethod($supportsRedirections)}
}
EOF;
}
public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider)
{
$this->expressionLanguageProviders[] = $provider;
}
/**
* Generates the code for the match method implementing UrlMatcherInterface.
*
* @param bool $supportsRedirections Whether redirections are supported by the base class
*
* @return string Match method as PHP code
*/
private function generateMatchMethod($supportsRedirections)
{
$code = rtrim($this->compileRoutes($this->getRoutes(), $supportsRedirections), "\n");
return <<<EOF
public function match(\$rawPathinfo)
{
\$allow = array();
\$pathinfo = rawurldecode(\$rawPathinfo);
\$context = \$this->context;
\$request = \$this->request ?: \$this->createRequest(\$pathinfo);
$code
throw 0 < count(\$allow) ? new MethodNotAllowedException(array_unique(\$allow)) : new ResourceNotFoundException();
}
EOF;
}
/**
* Generates PHP code to match a RouteCollection with all its routes.
*
* @param RouteCollection $routes A RouteCollection instance
* @param bool $supportsRedirections Whether redirections are supported by the base class
*
* @return string PHP code
*/
private function compileRoutes(RouteCollection $routes, $supportsRedirections)
{
$fetchedHost = false;
$groups = $this->groupRoutesByHostRegex($routes);
$code = '';
foreach ($groups as $collection) {
if (null !== $regex = $collection->getAttribute('host_regex')) {
if (!$fetchedHost) {
$code .= " \$host = \$this->context->getHost();\n\n";
$fetchedHost = true;
}
$code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true));
}
$tree = $this->buildPrefixTree($collection);
$groupCode = $this->compilePrefixRoutes($tree, $supportsRedirections);
if (null !== $regex) {
// apply extra indention at each line (except empty ones)
$groupCode = preg_replace('/^.{2,}$/m', ' $0', $groupCode);
$code .= $groupCode;
$code .= " }\n\n";
} else {
$code .= $groupCode;
}
}
return $code;
}
/**
* Generates PHP code recursively to match a tree of routes.
*
* @param DumperPrefixCollection $collection A DumperPrefixCollection instance
* @param bool $supportsRedirections Whether redirections are supported by the base class
* @param string $parentPrefix Prefix of the parent collection
*
* @return string PHP code
*/
private function compilePrefixRoutes(DumperPrefixCollection $collection, $supportsRedirections, $parentPrefix = '')
{
$code = '';
$prefix = $collection->getPrefix();
$optimizable = 1 < \strlen($prefix) && 1 < \count($collection->all());
$optimizedPrefix = $parentPrefix;
if ($optimizable) {
$optimizedPrefix = $prefix;
$code .= sprintf(" if (0 === strpos(\$pathinfo, %s)) {\n", var_export($prefix, true));
}
foreach ($collection as $route) {
if ($route instanceof DumperCollection) {
$code .= $this->compilePrefixRoutes($route, $supportsRedirections, $optimizedPrefix);
} else {
$code .= $this->compileRoute($route->getRoute(), $route->getName(), $supportsRedirections, $optimizedPrefix)."\n";
}
}
if ($optimizable) {
$code .= " }\n\n";
// apply extra indention at each line (except empty ones)
$code = preg_replace('/^.{2,}$/m', ' $0', $code);
}
return $code;
}
/**
* Compiles a single Route to PHP code used to match it against the path info.
*
* @param Route $route A Route instance
* @param string $name The name of the Route
* @param bool $supportsRedirections Whether redirections are supported by the base class
* @param string|null $parentPrefix The prefix of the parent collection used to optimize the code
*
* @return string PHP code
*
* @throws \LogicException
*/
private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null)
{
$code = '';
$compiledRoute = $route->compile();
$conditions = array();
$hasTrailingSlash = false;
$matches = false;
$hostMatches = false;
$methods = $route->getMethods();
// GET and HEAD are equivalent
if (\in_array('GET', $methods) && !\in_array('HEAD', $methods)) {
$methods[] = 'HEAD';
}
$supportsTrailingSlash = $supportsRedirections && (!$methods || \in_array('GET', $methods));
if (!\count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P<url>.*?)\$\1#', $compiledRoute->getRegex(), $m)) {
if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) {
$conditions[] = sprintf("%s === rtrim(\$pathinfo, '/')", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true));
$hasTrailingSlash = true;
} else {
$conditions[] = sprintf('%s === $pathinfo', var_export(str_replace('\\', '', $m['url']), true));
}
} else {
if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() !== $parentPrefix) {
$conditions[] = sprintf('0 === strpos($pathinfo, %s)', var_export($compiledRoute->getStaticPrefix(), true));
}
$regex = $compiledRoute->getRegex();
if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) {
$regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
$hasTrailingSlash = true;
}
$conditions[] = sprintf('preg_match(%s, $pathinfo, $matches)', var_export($regex, true));
$matches = true;
}
if ($compiledRoute->getHostVariables()) {
$hostMatches = true;
}
if ($route->getCondition()) {
$conditions[] = $this->getExpressionLanguage()->compile($route->getCondition(), array('context', 'request'));
}
$conditions = implode(' && ', $conditions);
$code .= <<<EOF
// $name
if ($conditions) {
EOF;
$gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name);
if ($hasTrailingSlash) {
$code .= <<<EOF
if ('/' === substr(\$pathinfo, -1)) {
// no-op
} elseif (!in_array(\$this->context->getMethod(), array('HEAD', 'GET'))) {
goto $gotoname;
} else {
return \$this->redirect(\$rawPathinfo.'/', '$name');
}
EOF;
}
if ($schemes = $route->getSchemes()) {
if (!$supportsRedirections) {
throw new \LogicException('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.');
}
$schemes = str_replace("\n", '', var_export(array_flip($schemes), true));
if ($methods) {
$methods = implode("', '", $methods);
$code .= <<<EOF
\$requiredSchemes = $schemes;
\$hasRequiredScheme = isset(\$requiredSchemes[\$this->context->getScheme()]);
if (!in_array(\$this->context->getMethod(), array('$methods'))) {
if (\$hasRequiredScheme) {
\$allow = array_merge(\$allow, array('$methods'));
}
goto $gotoname;
}
if (!\$hasRequiredScheme) {
if (!in_array(\$this->context->getMethod(), array('HEAD', 'GET'))) {
goto $gotoname;
}
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes));
}
EOF;
} else {
$code .= <<<EOF
\$requiredSchemes = $schemes;
if (!isset(\$requiredSchemes[\$this->context->getScheme()])) {
if (!in_array(\$this->context->getMethod(), array('HEAD', 'GET'))) {
goto $gotoname;
}
return \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes));
}
EOF;
}
} elseif ($methods) {
if (1 === \count($methods)) {
$code .= <<<EOF
if (\$this->context->getMethod() != '$methods[0]') {
\$allow[] = '$methods[0]';
goto $gotoname;
}
EOF;
} else {
$methods = implode("', '", $methods);
$code .= <<<EOF
if (!in_array(\$this->context->getMethod(), array('$methods'))) {
\$allow = array_merge(\$allow, array('$methods'));
goto $gotoname;
}
EOF;
}
}
// optimize parameters array
if ($matches || $hostMatches) {
$vars = array();
if ($hostMatches) {
$vars[] = '$hostMatches';
}
if ($matches) {
$vars[] = '$matches';
}
$vars[] = "array('_route' => '$name')";
$code .= sprintf(
" return \$this->mergeDefaults(array_replace(%s), %s);\n",
implode(', ', $vars),
str_replace("\n", '', var_export($route->getDefaults(), true))
);
} elseif ($route->getDefaults()) {
$code .= sprintf(" return %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), array('_route' => $name)), true)));
} else {
$code .= sprintf(" return array('_route' => '%s');\n", $name);
}
$code .= " }\n";
if ($hasTrailingSlash || $schemes || $methods) {
$code .= " $gotoname:\n";
}
return $code;
}
/**
* Groups consecutive routes having the same host regex.
*
* The result is a collection of collections of routes having the same host regex.
*
* @param RouteCollection $routes A flat RouteCollection
*
* @return DumperCollection A collection with routes grouped by host regex in sub-collections
*/
private function groupRoutesByHostRegex(RouteCollection $routes)
{
$groups = new DumperCollection();
$currentGroup = new DumperCollection();
$currentGroup->setAttribute('host_regex', null);
$groups->add($currentGroup);
foreach ($routes as $name => $route) {
$hostRegex = $route->compile()->getHostRegex();
if ($currentGroup->getAttribute('host_regex') !== $hostRegex) {
$currentGroup = new DumperCollection();
$currentGroup->setAttribute('host_regex', $hostRegex);
$groups->add($currentGroup);
}
$currentGroup->add(new DumperRoute($name, $route));
}
return $groups;
}
/**
* Organizes the routes into a prefix tree.
*
* Routes order is preserved such that traversing the tree will traverse the
* routes in the origin order.
*
* @return DumperPrefixCollection
*/
private function buildPrefixTree(DumperCollection $collection)
{
$tree = new DumperPrefixCollection();
$current = $tree;
foreach ($collection as $route) {
$current = $current->addPrefixRoute($route);
}
$tree->mergeSlashNodes();
return $tree;
}
private function getExpressionLanguage()
{
if (null === $this->expressionLanguage) {
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
throw new \RuntimeException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed.');
}
$this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders);
}
return $this->expressionLanguage;
}
}