PK[ZZcppRouteCollection.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; use Symfony\Component\Config\Resource\ResourceInterface; /** * A RouteCollection represents a set of Route instances. * * When adding a route at the end of the collection, an existing route * with the same name is removed first. So there can only be one route * with a given name. * * @author Fabien Potencier * @author Tobias Schultze */ class RouteCollection implements \IteratorAggregate, \Countable { /** * @var Route[] */ private $routes = []; /** * @var array */ private $resources = []; public function __clone() { foreach ($this->routes as $name => $route) { $this->routes[$name] = clone $route; } } /** * Gets the current RouteCollection as an Iterator that includes all routes. * * It implements \IteratorAggregate. * * @see all() * * @return \ArrayIterator|Route[] An \ArrayIterator object for iterating over routes */ public function getIterator() { return new \ArrayIterator($this->routes); } /** * Gets the number of Routes in this collection. * * @return int The number of routes */ public function count() { return \count($this->routes); } /** * Adds a route. * * @param string $name The route name * @param Route $route A Route instance */ public function add($name, Route $route) { unset($this->routes[$name]); $this->routes[$name] = $route; } /** * Returns all routes in this collection. * * @return Route[] An array of routes */ public function all() { return $this->routes; } /** * Gets a route by name. * * @param string $name The route name * * @return Route|null A Route instance or null when not found */ public function get($name) { return isset($this->routes[$name]) ? $this->routes[$name] : null; } /** * Removes a route or an array of routes by name from the collection. * * @param string|string[] $name The route name or an array of route names */ public function remove($name) { foreach ((array) $name as $n) { unset($this->routes[$n]); } } /** * Adds a route collection at the end of the current set by appending all * routes of the added collection. */ public function addCollection(self $collection) { // we need to remove all routes with the same names first because just replacing them // would not place the new route at the end of the merged array foreach ($collection->all() as $name => $route) { unset($this->routes[$name]); $this->routes[$name] = $route; } foreach ($collection->getResources() as $resource) { $this->addResource($resource); } } /** * Adds a prefix to the path of all child routes. * * @param string $prefix An optional prefix to add before each pattern of the route collection * @param array $defaults An array of default values * @param array $requirements An array of requirements */ public function addPrefix($prefix, array $defaults = [], array $requirements = []) { $prefix = trim(trim($prefix), '/'); if ('' === $prefix) { return; } foreach ($this->routes as $route) { $route->setPath('/'.$prefix.$route->getPath()); $route->addDefaults($defaults); $route->addRequirements($requirements); } } /** * Sets the host pattern on all routes. * * @param string $pattern The pattern * @param array $defaults An array of default values * @param array $requirements An array of requirements */ public function setHost($pattern, array $defaults = [], array $requirements = []) { foreach ($this->routes as $route) { $route->setHost($pattern); $route->addDefaults($defaults); $route->addRequirements($requirements); } } /** * Sets a condition on all routes. * * Existing conditions will be overridden. * * @param string $condition The condition */ public function setCondition($condition) { foreach ($this->routes as $route) { $route->setCondition($condition); } } /** * Adds defaults to all routes. * * An existing default value under the same name in a route will be overridden. * * @param array $defaults An array of default values */ public function addDefaults(array $defaults) { if ($defaults) { foreach ($this->routes as $route) { $route->addDefaults($defaults); } } } /** * Adds requirements to all routes. * * An existing requirement under the same name in a route will be overridden. * * @param array $requirements An array of requirements */ public function addRequirements(array $requirements) { if ($requirements) { foreach ($this->routes as $route) { $route->addRequirements($requirements); } } } /** * Adds options to all routes. * * An existing option value under the same name in a route will be overridden. * * @param array $options An array of options */ public function addOptions(array $options) { if ($options) { foreach ($this->routes as $route) { $route->addOptions($options); } } } /** * Sets the schemes (e.g. 'https') all child routes are restricted to. * * @param string|string[] $schemes The scheme or an array of schemes */ public function setSchemes($schemes) { foreach ($this->routes as $route) { $route->setSchemes($schemes); } } /** * Sets the HTTP methods (e.g. 'POST') all child routes are restricted to. * * @param string|string[] $methods The method or an array of methods */ public function setMethods($methods) { foreach ($this->routes as $route) { $route->setMethods($methods); } } /** * Returns an array of resources loaded to build this collection. * * @return ResourceInterface[] An array of resources */ public function getResources() { return array_values($this->resources); } /** * Adds a resource for this collection. If the resource already exists * it is not added. */ public function addResource(ResourceInterface $resource) { $key = (string) $resource; if (!isset($this->resources[$key])) { $this->resources[$key] = $resource; } } } PK[ZZ堻RequestContext.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; use Symfony\Component\HttpFoundation\Request; /** * Holds information about the current request. * * This class implements a fluent interface. * * @author Fabien Potencier * @author Tobias Schultze */ class RequestContext { private $baseUrl; private $pathInfo; private $method; private $host; private $scheme; private $httpPort; private $httpsPort; private $queryString; private $parameters = []; /** * @param string $baseUrl The base URL * @param string $method The HTTP method * @param string $host The HTTP host name * @param string $scheme The HTTP scheme * @param int $httpPort The HTTP port * @param int $httpsPort The HTTPS port * @param string $path The path * @param string $queryString The query string */ public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443, $path = '/', $queryString = '') { $this->setBaseUrl($baseUrl); $this->setMethod($method); $this->setHost($host); $this->setScheme($scheme); $this->setHttpPort($httpPort); $this->setHttpsPort($httpsPort); $this->setPathInfo($path); $this->setQueryString($queryString); } /** * Updates the RequestContext information based on a HttpFoundation Request. * * @return $this */ public function fromRequest(Request $request) { $this->setBaseUrl($request->getBaseUrl()); $this->setPathInfo($request->getPathInfo()); $this->setMethod($request->getMethod()); $this->setHost($request->getHost()); $this->setScheme($request->getScheme()); $this->setHttpPort($request->isSecure() || null === $request->getPort() ? $this->httpPort : $request->getPort()); $this->setHttpsPort($request->isSecure() && null !== $request->getPort() ? $request->getPort() : $this->httpsPort); $this->setQueryString($request->server->get('QUERY_STRING', '')); return $this; } /** * Gets the base URL. * * @return string The base URL */ public function getBaseUrl() { return $this->baseUrl; } /** * Sets the base URL. * * @param string $baseUrl The base URL * * @return $this */ public function setBaseUrl($baseUrl) { $this->baseUrl = $baseUrl; return $this; } /** * Gets the path info. * * @return string The path info */ public function getPathInfo() { return $this->pathInfo; } /** * Sets the path info. * * @param string $pathInfo The path info * * @return $this */ public function setPathInfo($pathInfo) { $this->pathInfo = $pathInfo; return $this; } /** * Gets the HTTP method. * * The method is always an uppercased string. * * @return string The HTTP method */ public function getMethod() { return $this->method; } /** * Sets the HTTP method. * * @param string $method The HTTP method * * @return $this */ public function setMethod($method) { $this->method = strtoupper($method); return $this; } /** * Gets the HTTP host. * * The host is always lowercased because it must be treated case-insensitive. * * @return string The HTTP host */ public function getHost() { return $this->host; } /** * Sets the HTTP host. * * @param string $host The HTTP host * * @return $this */ public function setHost($host) { $this->host = strtolower($host); return $this; } /** * Gets the HTTP scheme. * * @return string The HTTP scheme */ public function getScheme() { return $this->scheme; } /** * Sets the HTTP scheme. * * @param string $scheme The HTTP scheme * * @return $this */ public function setScheme($scheme) { $this->scheme = strtolower($scheme); return $this; } /** * Gets the HTTP port. * * @return int The HTTP port */ public function getHttpPort() { return $this->httpPort; } /** * Sets the HTTP port. * * @param int $httpPort The HTTP port * * @return $this */ public function setHttpPort($httpPort) { $this->httpPort = (int) $httpPort; return $this; } /** * Gets the HTTPS port. * * @return int The HTTPS port */ public function getHttpsPort() { return $this->httpsPort; } /** * Sets the HTTPS port. * * @param int $httpsPort The HTTPS port * * @return $this */ public function setHttpsPort($httpsPort) { $this->httpsPort = (int) $httpsPort; return $this; } /** * Gets the query string. * * @return string The query string without the "?" */ public function getQueryString() { return $this->queryString; } /** * Sets the query string. * * @param string $queryString The query string (after "?") * * @return $this */ public function setQueryString($queryString) { // string cast to be fault-tolerant, accepting null $this->queryString = (string) $queryString; return $this; } /** * Returns the parameters. * * @return array The parameters */ public function getParameters() { return $this->parameters; } /** * Sets the parameters. * * @param array $parameters The parameters * * @return $this */ public function setParameters(array $parameters) { $this->parameters = $parameters; return $this; } /** * Gets a parameter value. * * @param string $name A parameter name * * @return mixed The parameter value or null if nonexistent */ public function getParameter($name) { return isset($this->parameters[$name]) ? $this->parameters[$name] : null; } /** * Checks if a parameter value is set for the given parameter. * * @param string $name A parameter name * * @return bool True if the parameter value is set, false otherwise */ public function hasParameter($name) { return \array_key_exists($name, $this->parameters); } /** * Sets a parameter value. * * @param string $name A parameter name * @param mixed $parameter The parameter value * * @return $this */ public function setParameter($name, $parameter) { $this->parameters[$name] = $parameter; return $this; } } PK[ZZv=R+DependencyInjection/RoutingResolverPass.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** * Adds tagged routing.loader services to routing.resolver service. * * @author Fabien Potencier */ class RoutingResolverPass implements CompilerPassInterface { use PriorityTaggedServiceTrait; private $resolverServiceId; private $loaderTag; public function __construct($resolverServiceId = 'routing.resolver', $loaderTag = 'routing.loader') { $this->resolverServiceId = $resolverServiceId; $this->loaderTag = $loaderTag; } public function process(ContainerBuilder $container) { if (false === $container->hasDefinition($this->resolverServiceId)) { return; } $definition = $container->getDefinition($this->resolverServiceId); foreach ($this->findAndSortTaggedServices($this->loaderTag, $container) as $id) { $definition->addMethodCall('addLoader', [new Reference($id)]); } } } PK[ZZk  RouteCompilerInterface.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; /** * RouteCompilerInterface is the interface that all RouteCompiler classes must implement. * * @author Fabien Potencier */ interface RouteCompilerInterface { /** * Compiles the current route instance. * * @return CompiledRoute A CompiledRoute instance * * @throws \LogicException If the Route cannot be compiled because the * path or host pattern is invalid */ public static function compile(Route $route); } PK[ZZ^ composer.jsonnu[{ "name": "symfony/routing", "type": "library", "description": "Symfony Routing Component", "keywords": ["routing", "router", "URL", "URI"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ { "name": "Fabien Potencier", "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], "require": { "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/config": "^3.3.1|~4.0", "symfony/http-foundation": "~2.8|~3.0|~4.0", "symfony/yaml": "~3.4|~4.0", "symfony/expression-language": "~2.8|~3.0|~4.0", "symfony/dependency-injection": "~3.3|~4.0", "doctrine/annotations": "~1.0", "psr/log": "~1.0" }, "conflict": { "symfony/config": "<3.3.1", "symfony/dependency-injection": "<3.3", "symfony/yaml": "<3.4" }, "suggest": { "symfony/http-foundation": "For using a Symfony Request object", "symfony/config": "For using the all-in-one router or any loader", "symfony/yaml": "For using the YAML loader", "symfony/expression-language": "For using expression matching", "doctrine/annotations": "For using the annotation loader" }, "autoload": { "psr-4": { "Symfony\\Component\\Routing\\": "" }, "exclude-from-classmap": [ "/Tests/" ] }, "minimum-stability": "dev" } PK[ZZx$$ CHANGELOG.mdnu[CHANGELOG ========= 3.4.0 ----- * Added `NoConfigurationException`. * Added the possibility to define a prefix for all routes of a controller via @Route(name="prefix_") * Added support for prioritized routing loaders. * Add matched and default parameters to redirect responses * Added support for a `controller` keyword for configuring route controllers in YAML and XML configurations. 3.3.0 ----- * [DEPRECATION] Class parameters have been deprecated and will be removed in 4.0. * router.options.generator_class * router.options.generator_base_class * router.options.generator_dumper_class * router.options.matcher_class * router.options.matcher_base_class * router.options.matcher_dumper_class * router.options.matcher.cache_class * router.options.generator.cache_class 3.2.0 ----- * Added support for `bool`, `int`, `float`, `string`, `list` and `map` defaults in XML configurations. * Added support for UTF-8 requirements 2.8.0 ----- * allowed specifying a directory to recursively load all routing configuration files it contains * Added ObjectRouteLoader and ServiceRouteLoader that allow routes to be loaded by calling a method on an object/service. * [DEPRECATION] Deprecated the hardcoded value for the `$referenceType` argument of the `UrlGeneratorInterface::generate` method. Use the constants defined in the `UrlGeneratorInterface` instead. Before: ```php $router->generate('blog_show', ['slug' => 'my-blog-post'], true); ``` After: ```php use Symfony\Component\Routing\Generator\UrlGeneratorInterface; $router->generate('blog_show', ['slug' => 'my-blog-post'], UrlGeneratorInterface::ABSOLUTE_URL); ``` 2.5.0 ----- * [DEPRECATION] The `ApacheMatcherDumper` and `ApacheUrlMatcher` were deprecated and will be removed in Symfony 3.0, since the performance gains were minimal and it's hard to replicate the behavior of PHP implementation. 2.3.0 ----- * added RequestContext::getQueryString() 2.2.0 ----- * [DEPRECATION] Several route settings have been renamed (the old ones will be removed in 3.0): * The `pattern` setting for a route has been deprecated in favor of `path` * The `_scheme` and `_method` requirements have been moved to the `schemes` and `methods` settings Before: ```yaml article_edit: pattern: /article/{id} requirements: { '_method': 'POST|PUT', '_scheme': 'https', 'id': '\d+' } ``` ```xml POST|PUT https \d+ ``` ```php $route = new Route(); $route->setPattern('/article/{id}'); $route->setRequirement('_method', 'POST|PUT'); $route->setRequirement('_scheme', 'https'); ``` After: ```yaml article_edit: path: /article/{id} methods: [POST, PUT] schemes: https requirements: { 'id': '\d+' } ``` ```xml \d+ ``` ```php $route = new Route(); $route->setPath('/article/{id}'); $route->setMethods(['POST', 'PUT']); $route->setSchemes('https'); ``` * [BC BREAK] RouteCollection does not behave like a tree structure anymore but as a flat array of Routes. So when using PHP to build the RouteCollection, you must make sure to add routes to the sub-collection before adding it to the parent collection (this is not relevant when using YAML or XML for Route definitions). Before: ```php $rootCollection = new RouteCollection(); $subCollection = new RouteCollection(); $rootCollection->addCollection($subCollection); $subCollection->add('foo', new Route('/foo')); ``` After: ```php $rootCollection = new RouteCollection(); $subCollection = new RouteCollection(); $subCollection->add('foo', new Route('/foo')); $rootCollection->addCollection($subCollection); ``` Also one must call `addCollection` from the bottom to the top hierarchy. So the correct sequence is the following (and not the reverse): ```php $childCollection->addCollection($grandchildCollection); $rootCollection->addCollection($childCollection); ``` * [DEPRECATION] The methods `RouteCollection::getParent()` and `RouteCollection::getRoot()` have been deprecated and will be removed in Symfony 2.3. * [BC BREAK] Misusing the `RouteCollection::addPrefix` method to add defaults, requirements or options without adding a prefix is not supported anymore. So if you called `addPrefix` with an empty prefix or `/` only (both have no relevance), like `addPrefix('', $defaultsArray, $requirementsArray, $optionsArray)` you need to use the new dedicated methods `addDefaults($defaultsArray)`, `addRequirements($requirementsArray)` or `addOptions($optionsArray)` instead. * [DEPRECATION] The `$options` parameter to `RouteCollection::addPrefix()` has been deprecated because adding options has nothing to do with adding a path prefix. If you want to add options to all child routes of a RouteCollection, you can use `addOptions()`. * [DEPRECATION] The method `RouteCollection::getPrefix()` has been deprecated because it suggested that all routes in the collection would have this prefix, which is not necessarily true. On top of that, since there is no tree structure anymore, this method is also useless. Don't worry about performance, prefix optimization for matching is still done in the dumper, which was also improved in 2.2.0 to find even more grouping possibilities. * [DEPRECATION] `RouteCollection::addCollection(RouteCollection $collection)` should now only be used with a single parameter. The other params `$prefix`, `$default`, `$requirements` and `$options` will still work, but have been deprecated. The `addPrefix` method should be used for this use-case instead. Before: `$parentCollection->addCollection($collection, '/prefix', [...], [...])` After: ```php $collection->addPrefix('/prefix', [...], [...]); $parentCollection->addCollection($collection); ``` * added support for the method default argument values when defining a @Route * Adjacent placeholders without separator work now, e.g. `/{x}{y}{z}.{_format}`. * Characters that function as separator between placeholders are now whitelisted to fix routes with normal text around a variable, e.g. `/prefix{var}suffix`. * [BC BREAK] The default requirement of a variable has been changed slightly. Previously it disallowed the previous and the next char around a variable. Now it disallows the slash (`/`) and the next char. Using the previous char added no value and was problematic because the route `/index.{_format}` would be matched by `/index.ht/ml`. * The default requirement now uses possessive quantifiers when possible which improves matching performance by up to 20% because it prevents backtracking when it's not needed. * The ConfigurableRequirementsInterface can now also be used to disable the requirements check on URL generation completely by calling `setStrictRequirements(null)`. It improves performance in production environment as you should know that params always pass the requirements (otherwise it would break your link anyway). * There is no restriction on the route name anymore. So non-alphanumeric characters are now also allowed. * [BC BREAK] `RouteCompilerInterface::compile(Route $route)` was made static (only relevant if you implemented your own RouteCompiler). * Added possibility to generate relative paths and network paths in the UrlGenerator, e.g. "../parent-file" and "//example.com/dir/file". The third parameter in `UrlGeneratorInterface::generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH)` now accepts more values and you should use the constants defined in `UrlGeneratorInterface` for claritiy. The old method calls with a Boolean parameter will continue to work because they equal the signature using the constants. 2.1.0 ----- * added RequestMatcherInterface * added RequestContext::fromRequest() * the UrlMatcher does not throw a \LogicException anymore when the required scheme is not the current one * added TraceableUrlMatcher * added the possibility to define options, default values and requirements for placeholders in prefix, including imported routes * added RouterInterface::getRouteCollection * [BC BREAK] the UrlMatcher urldecodes the route parameters only once, they were decoded twice before. Note that the `urldecode()` calls have been changed for a single `rawurldecode()` in order to support `+` for input paths. * added RouteCollection::getRoot method to retrieve the root of a RouteCollection tree * [BC BREAK] made RouteCollection::setParent private which could not have been used anyway without creating inconsistencies * [BC BREAK] RouteCollection::remove also removes a route from parent collections (not only from its children) * added ConfigurableRequirementsInterface that allows to disable exceptions (and generate empty URLs instead) when generating a route with an invalid parameter value PK[ZZDf..-Exception/RouteCircularReferenceException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; class RouteCircularReferenceException extends RuntimeException { public function __construct(string $routeId, array $path) { parent::__construct(sprintf('Circular reference detected for route "%s", path: "%s".', $routeId, implode(' -> ', $path))); } } PK[ZZ/nE$$'Exception/ResourceNotFoundException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * The resource was not found. * * This exception should trigger an HTTP 404 response in your application code. * * @author Kris Wallsmith */ class ResourceNotFoundException extends \RuntimeException implements ExceptionInterface { } PK[ZZ4221Exception/MissingMandatoryParametersException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * Exception thrown when a route cannot be generated because of missing * mandatory parameters. * * @author Alexandre Salomé */ class MissingMandatoryParametersException extends \InvalidArgumentException implements ExceptionInterface { } PK[ZZ($>'Exception/MethodNotAllowedException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * The resource was found but the request method is not allowed. * * This exception should trigger an HTTP 405 response in your application code. * * @author Kris Wallsmith */ class MethodNotAllowedException extends \RuntimeException implements ExceptionInterface { protected $allowedMethods = []; public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null) { $this->allowedMethods = array_map('strtoupper', $allowedMethods); parent::__construct($message, $code, $previous); } /** * Gets the allowed HTTP methods. * * @return array */ public function getAllowedMethods() { return $this->allowedMethods; } } PK[ZZѺ7$Exception/RouteNotFoundException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * Exception thrown when a route does not exist. * * @author Alexandre Salomé */ class RouteNotFoundException extends \InvalidArgumentException implements ExceptionInterface { } PK[ZZds&Exception/InvalidArgumentException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface { } PK[ZZ";C Exception/ExceptionInterface.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * ExceptionInterface. * * @author Alexandre Salomé */ interface ExceptionInterface { } PK[ZZ&Exception/NoConfigurationException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * Exception thrown when no routes are configured. * * @author Yonel Ceruto */ class NoConfigurationException extends ResourceNotFoundException { } PK[ZZc1'Exception/InvalidParameterException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; /** * Exception thrown when a parameter is not valid. * * @author Alexandre Salomé */ class InvalidParameterException extends \InvalidArgumentException implements ExceptionInterface { } PK[ZZўqqException/RuntimeException.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Exception; class RuntimeException extends \RuntimeException implements ExceptionInterface { } PK[ZZ.. RequestContextAwareInterface.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; interface RequestContextAwareInterface { /** * Sets the request context. */ public function setContext(RequestContext $context); /** * Gets the request context. * * @return RequestContext The context */ public function getContext(); } PK[ZZ,; ; Annotation/Route.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Annotation; /** * Annotation class for @Route(). * * @Annotation * @Target({"CLASS", "METHOD"}) * * @author Fabien Potencier */ class Route { private $path; private $name; private $requirements = []; private $options = []; private $defaults = []; private $host; private $methods = []; private $schemes = []; private $condition; /** * @param array $data An array of key/value parameters * * @throws \BadMethodCallException */ public function __construct(array $data) { if (isset($data['value'])) { $data['path'] = $data['value']; unset($data['value']); } foreach ($data as $key => $value) { $method = 'set'.str_replace('_', '', $key); if (!method_exists($this, $method)) { throw new \BadMethodCallException(sprintf('Unknown property "%s" on annotation "%s".', $key, static::class)); } $this->$method($value); } } public function setPath($path) { $this->path = $path; } public function getPath() { return $this->path; } public function setHost($pattern) { $this->host = $pattern; } public function getHost() { return $this->host; } public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } public function setRequirements($requirements) { $this->requirements = $requirements; } public function getRequirements() { return $this->requirements; } public function setOptions($options) { $this->options = $options; } public function getOptions() { return $this->options; } public function setDefaults($defaults) { $this->defaults = $defaults; } public function getDefaults() { return $this->defaults; } public function setSchemes($schemes) { $this->schemes = \is_array($schemes) ? $schemes : [$schemes]; } public function getSchemes() { return $this->schemes; } public function setMethods($methods) { $this->methods = \is_array($methods) ? $methods : [$methods]; } public function getMethods() { return $this->methods; } public function setCondition($condition) { $this->condition = $condition; } public function getCondition() { return $this->condition; } } PK[ZZ RU  Alias.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; use Symfony\Component\Routing\Exception\InvalidArgumentException; class Alias { private string $id; private array $deprecation = []; public function __construct(string $id) { $this->id = $id; } public function withId(string $id): static { $new = clone $this; $new->id = $id; return $new; } /** * Returns the target name of this alias. * * @return string The target name */ public function getId(): string { return $this->id; } /** * Whether this alias is deprecated, that means it should not be referenced anymore. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The deprecation message to use * * @return $this * * @throws InvalidArgumentException when the message template is invalid */ public function setDeprecated(string $package, string $version, string $message): static { if ('' !== $message) { if (preg_match('#[\r\n]|\*/#', $message)) { throw new InvalidArgumentException('Invalid characters found in deprecation template.'); } if (!str_contains($message, '%alias_id%')) { throw new InvalidArgumentException('The deprecation template must contain the "%alias_id%" placeholder.'); } } $this->deprecation = [ 'package' => $package, 'version' => $version, 'message' => $message ?: 'The "%alias_id%" route alias is deprecated. You should stop using it, as it will be removed in the future.', ]; return $this; } public function isDeprecated(): bool { return (bool) $this->deprecation; } /** * @param string $name Route name relying on this alias */ public function getDeprecation(string $name): array { return [ 'package' => $this->deprecation['package'], 'version' => $this->deprecation['version'], 'message' => str_replace('%alias_id%', $name, $this->deprecation['message']), ]; } } PK[ZZ8W55 Route.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; /** * A Route describes a route and its parameters. * * @author Fabien Potencier * @author Tobias Schultze */ class Route implements \Serializable { private $path = '/'; private $host = ''; private $schemes = []; private $methods = []; private $defaults = []; private $requirements = []; private $options = []; private $condition = ''; /** * @var CompiledRoute|null */ private $compiled; /** * Constructor. * * Available options: * * * compiler_class: A class name able to compile this route instance (RouteCompiler by default) * * utf8: Whether UTF-8 matching is enforced ot not * * @param string $path The path pattern to match * @param array $defaults An array of default parameter values * @param array $requirements An array of requirements for parameters (regexes) * @param array $options An array of options * @param string $host The host pattern to match * @param string|string[] $schemes A required URI scheme or an array of restricted schemes * @param string|string[] $methods A required HTTP method or an array of restricted methods * @param string $condition A condition that should evaluate to true for the route to match */ public function __construct($path, array $defaults = [], array $requirements = [], array $options = [], $host = '', $schemes = [], $methods = [], $condition = '') { $this->setPath($path); $this->setDefaults($defaults); $this->setRequirements($requirements); $this->setOptions($options); $this->setHost($host); $this->setSchemes($schemes); $this->setMethods($methods); $this->setCondition($condition); } /** * {@inheritdoc} */ public function serialize() { return serialize([ 'path' => $this->path, 'host' => $this->host, 'defaults' => $this->defaults, 'requirements' => $this->requirements, 'options' => $this->options, 'schemes' => $this->schemes, 'methods' => $this->methods, 'condition' => $this->condition, 'compiled' => $this->compiled, ]); } /** * {@inheritdoc} */ public function unserialize($serialized) { $data = unserialize($serialized); $this->path = $data['path']; $this->host = $data['host']; $this->defaults = $data['defaults']; $this->requirements = $data['requirements']; $this->options = $data['options']; $this->schemes = $data['schemes']; $this->methods = $data['methods']; if (isset($data['condition'])) { $this->condition = $data['condition']; } if (isset($data['compiled'])) { $this->compiled = $data['compiled']; } } /** * Returns the pattern for the path. * * @return string The path pattern */ public function getPath() { return $this->path; } /** * Sets the pattern for the path. * * This method implements a fluent interface. * * @param string $pattern The path pattern * * @return $this */ public function setPath($pattern) { // A pattern must start with a slash and must not have multiple slashes at the beginning because the // generated path for this route would be confused with a network path, e.g. '//domain.com/path'. $this->path = '/'.ltrim(trim($pattern), '/'); $this->compiled = null; return $this; } /** * Returns the pattern for the host. * * @return string The host pattern */ public function getHost() { return $this->host; } /** * Sets the pattern for the host. * * This method implements a fluent interface. * * @param string $pattern The host pattern * * @return $this */ public function setHost($pattern) { $this->host = (string) $pattern; $this->compiled = null; return $this; } /** * Returns the lowercased schemes this route is restricted to. * So an empty array means that any scheme is allowed. * * @return string[] The schemes */ public function getSchemes() { return $this->schemes; } /** * Sets the schemes (e.g. 'https') this route is restricted to. * So an empty array means that any scheme is allowed. * * This method implements a fluent interface. * * @param string|string[] $schemes The scheme or an array of schemes * * @return $this */ public function setSchemes($schemes) { $this->schemes = array_map('strtolower', (array) $schemes); $this->compiled = null; return $this; } /** * Checks if a scheme requirement has been set. * * @param string $scheme * * @return bool true if the scheme requirement exists, otherwise false */ public function hasScheme($scheme) { return \in_array(strtolower($scheme), $this->schemes, true); } /** * Returns the uppercased HTTP methods this route is restricted to. * So an empty array means that any method is allowed. * * @return string[] The methods */ public function getMethods() { return $this->methods; } /** * Sets the HTTP methods (e.g. 'POST') this route is restricted to. * So an empty array means that any method is allowed. * * This method implements a fluent interface. * * @param string|string[] $methods The method or an array of methods * * @return $this */ public function setMethods($methods) { $this->methods = array_map('strtoupper', (array) $methods); $this->compiled = null; return $this; } /** * Returns the options. * * @return array The options */ public function getOptions() { return $this->options; } /** * Sets the options. * * This method implements a fluent interface. * * @param array $options The options * * @return $this */ public function setOptions(array $options) { $this->options = [ 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler', ]; return $this->addOptions($options); } /** * Adds options. * * This method implements a fluent interface. * * @param array $options The options * * @return $this */ public function addOptions(array $options) { foreach ($options as $name => $option) { $this->options[$name] = $option; } $this->compiled = null; return $this; } /** * Sets an option value. * * This method implements a fluent interface. * * @param string $name An option name * @param mixed $value The option value * * @return $this */ public function setOption($name, $value) { $this->options[$name] = $value; $this->compiled = null; return $this; } /** * Get an option value. * * @param string $name An option name * * @return mixed The option value or null when not given */ public function getOption($name) { return isset($this->options[$name]) ? $this->options[$name] : null; } /** * Checks if an option has been set. * * @param string $name An option name * * @return bool true if the option is set, false otherwise */ public function hasOption($name) { return \array_key_exists($name, $this->options); } /** * Returns the defaults. * * @return array The defaults */ public function getDefaults() { return $this->defaults; } /** * Sets the defaults. * * This method implements a fluent interface. * * @param array $defaults The defaults * * @return $this */ public function setDefaults(array $defaults) { $this->defaults = []; return $this->addDefaults($defaults); } /** * Adds defaults. * * This method implements a fluent interface. * * @param array $defaults The defaults * * @return $this */ public function addDefaults(array $defaults) { foreach ($defaults as $name => $default) { $this->defaults[$name] = $default; } $this->compiled = null; return $this; } /** * Gets a default value. * * @param string $name A variable name * * @return mixed The default value or null when not given */ public function getDefault($name) { return isset($this->defaults[$name]) ? $this->defaults[$name] : null; } /** * Checks if a default value is set for the given variable. * * @param string $name A variable name * * @return bool true if the default value is set, false otherwise */ public function hasDefault($name) { return \array_key_exists($name, $this->defaults); } /** * Sets a default value. * * @param string $name A variable name * @param mixed $default The default value * * @return $this */ public function setDefault($name, $default) { $this->defaults[$name] = $default; $this->compiled = null; return $this; } /** * Returns the requirements. * * @return array The requirements */ public function getRequirements() { return $this->requirements; } /** * Sets the requirements. * * This method implements a fluent interface. * * @param array $requirements The requirements * * @return $this */ public function setRequirements(array $requirements) { $this->requirements = []; return $this->addRequirements($requirements); } /** * Adds requirements. * * This method implements a fluent interface. * * @param array $requirements The requirements * * @return $this */ public function addRequirements(array $requirements) { foreach ($requirements as $key => $regex) { $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); } $this->compiled = null; return $this; } /** * Returns the requirement for the given key. * * @param string $key The key * * @return string|null The regex or null when not given */ public function getRequirement($key) { return isset($this->requirements[$key]) ? $this->requirements[$key] : null; } /** * Checks if a requirement is set for the given key. * * @param string $key A variable name * * @return bool true if a requirement is specified, false otherwise */ public function hasRequirement($key) { return \array_key_exists($key, $this->requirements); } /** * Sets a requirement for the given key. * * @param string $key The key * @param string $regex The regex * * @return $this */ public function setRequirement($key, $regex) { $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); $this->compiled = null; return $this; } /** * Returns the condition. * * @return string The condition */ public function getCondition() { return $this->condition; } /** * Sets the condition. * * This method implements a fluent interface. * * @param string $condition The condition * * @return $this */ public function setCondition($condition) { $this->condition = (string) $condition; $this->compiled = null; return $this; } /** * Compiles the route. * * @return CompiledRoute A CompiledRoute instance * * @throws \LogicException If the Route cannot be compiled because the * path or host pattern is invalid * * @see RouteCompiler which is responsible for the compilation process */ public function compile() { if (null !== $this->compiled) { return $this->compiled; } $class = $this->getOption('compiler_class'); return $this->compiled = $class::compile($this); } private function sanitizeRequirement($key, $regex) { if (!\is_string($regex)) { throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" must be a string.', $key)); } if ('' !== $regex && '^' === $regex[0]) { $regex = (string) substr($regex, 1); // returns false for a single character } if ('$' === substr($regex, -1)) { $regex = substr($regex, 0, -1); } if ('' === $regex) { throw new \InvalidArgumentException(sprintf('Routing requirement for "%s" cannot be empty.', $key)); } return $regex; } } PK[ZZ. README.mdnu[Routing Component ================= The Routing component maps an HTTP request to a set of configuration variables. Getting Started --------------- ``` $ composer require symfony/routing ``` ```php use App\Controller\BlogController; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; $route = new Route('/blog/{slug}', ['_controller' => BlogController::class]); $routes = new RouteCollection(); $routes->add('blog_show', $route); $context = new RequestContext(); // Routing can match routes with incoming requests $matcher = new UrlMatcher($routes, $context); $parameters = $matcher->match('/blog/lorem-ipsum'); // $parameters = [ // '_controller' => 'App\Controller\BlogController', // 'slug' => 'lorem-ipsum', // '_route' => 'blog_show' // ] // Routing can also generate URLs for a given route $generator = new UrlGenerator($routes, $context); $url = $generator->generate('blog_show', [ 'slug' => 'my-blog-post', ]); // $url = '/blog/my-blog-post' ``` Resources --------- * [Documentation](https://symfony.com/doc/current/routing.html) * [Contributing](https://symfony.com/doc/current/contributing/index.html) * [Report issues](https://github.com/symfony/symfony/issues) and [send Pull Requests](https://github.com/symfony/symfony/pulls) in the [main Symfony repository](https://github.com/symfony/symfony) PK[ZZ{K}66RouteCompiler.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; /** * RouteCompiler compiles Route instances to CompiledRoute instances. * * @author Fabien Potencier * @author Tobias Schultze */ class RouteCompiler implements RouteCompilerInterface { const REGEX_DELIMITER = '#'; /** * This string defines the characters that are automatically considered separators in front of * optional placeholders (with default and no static text following). Such a single separator * can be left out together with the optional placeholder from matching and generating URLs. */ const SEPARATORS = '/,;.:-_~+*=@|'; /** * The maximum supported length of a PCRE subpattern name * http://pcre.org/current/doc/html/pcre2pattern.html#SEC16. * * @internal */ const VARIABLE_MAXIMUM_LENGTH = 32; /** * {@inheritdoc} * * @throws \InvalidArgumentException if a path variable is named _fragment * @throws \LogicException if a variable is referenced more than once * @throws \DomainException if a variable name starts with a digit or if it is too long to be successfully used as * a PCRE subpattern */ public static function compile(Route $route) { $hostVariables = []; $variables = []; $hostRegex = null; $hostTokens = []; if ('' !== $host = $route->getHost()) { $result = self::compilePattern($route, $host, true); $hostVariables = $result['variables']; $variables = $hostVariables; $hostTokens = $result['tokens']; $hostRegex = $result['regex']; } $path = $route->getPath(); $result = self::compilePattern($route, $path, false); $staticPrefix = $result['staticPrefix']; $pathVariables = $result['variables']; foreach ($pathVariables as $pathParam) { if ('_fragment' === $pathParam) { throw new \InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath())); } } $variables = array_merge($variables, $pathVariables); $tokens = $result['tokens']; $regex = $result['regex']; return new CompiledRoute( $staticPrefix, $regex, $tokens, $pathVariables, $hostRegex, $hostTokens, $hostVariables, array_unique($variables) ); } private static function compilePattern(Route $route, $pattern, $isHost) { $tokens = []; $variables = []; $matches = []; $pos = 0; $defaultSeparator = $isHost ? '.' : '/'; $useUtf8 = preg_match('//u', $pattern); $needsUtf8 = $route->getOption('utf8'); if (!$needsUtf8 && $useUtf8 && preg_match('/[\x80-\xFF]/', $pattern)) { $needsUtf8 = true; @trigger_error(sprintf('Using UTF-8 route patterns without setting the "utf8" option is deprecated since Symfony 3.2 and will throw a LogicException in 4.0. Turn on the "utf8" route option for pattern "%s".', $pattern), \E_USER_DEPRECATED); } if (!$useUtf8 && $needsUtf8) { throw new \LogicException(sprintf('Cannot mix UTF-8 requirements with non-UTF-8 pattern "%s".', $pattern)); } // Match all variables enclosed in "{}" and iterate over them. But we only want to match the innermost variable // in case of nested "{}", e.g. {foo{bar}}. This in ensured because \w does not match "{" or "}" itself. preg_match_all('#\{\w+\}#', $pattern, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER); foreach ($matches as $match) { $varName = substr($match[0][0], 1, -1); // get all static text preceding the current variable $precedingText = substr($pattern, $pos, $match[0][1] - $pos); $pos = $match[0][1] + \strlen($match[0][0]); if (!\strlen($precedingText)) { $precedingChar = ''; } elseif ($useUtf8) { preg_match('/.$/u', $precedingText, $precedingChar); $precedingChar = $precedingChar[0]; } else { $precedingChar = substr($precedingText, -1); } $isSeparator = '' !== $precedingChar && false !== strpos(static::SEPARATORS, $precedingChar); // A PCRE subpattern name must start with a non-digit. Also a PHP variable cannot start with a digit so the // variable would not be usable as a Controller action argument. if (preg_match('/^\d/', $varName)) { throw new \DomainException(sprintf('Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.', $varName, $pattern)); } if (\in_array($varName, $variables)) { throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName)); } if (\strlen($varName) > self::VARIABLE_MAXIMUM_LENGTH) { throw new \DomainException(sprintf('Variable name "%s" cannot be longer than %d characters in route pattern "%s". Please use a shorter name.', $varName, self::VARIABLE_MAXIMUM_LENGTH, $pattern)); } if ($isSeparator && $precedingText !== $precedingChar) { $tokens[] = ['text', substr($precedingText, 0, -\strlen($precedingChar))]; } elseif (!$isSeparator && \strlen($precedingText) > 0) { $tokens[] = ['text', $precedingText]; } $regexp = $route->getRequirement($varName); if (null === $regexp) { $followingPattern = (string) substr($pattern, $pos); // Find the next static character after the variable that functions as a separator. By default, this separator and '/' // are disallowed for the variable. This default requirement makes sure that optional variables can be matched at all // and that the generating-matching-combination of URLs unambiguous, i.e. the params used for generating the URL are // the same that will be matched. Example: new Route('/{page}.{_format}', ['_format' => 'html']) // If {page} would also match the separating dot, {_format} would never match as {page} will eagerly consume everything. // Also even if {_format} was not optional the requirement prevents that {page} matches something that was originally // part of {_format} when generating the URL, e.g. _format = 'mobile.html'. $nextSeparator = self::findNextSeparator($followingPattern, $useUtf8); $regexp = sprintf( '[^%s%s]+', preg_quote($defaultSeparator, self::REGEX_DELIMITER), $defaultSeparator !== $nextSeparator && '' !== $nextSeparator ? preg_quote($nextSeparator, self::REGEX_DELIMITER) : '' ); if (('' !== $nextSeparator && !preg_match('#^\{\w+\}#', $followingPattern)) || '' === $followingPattern) { // When we have a separator, which is disallowed for the variable, we can optimize the regex with a possessive // quantifier. This prevents useless backtracking of PCRE and improves performance by 20% for matching those patterns. // Given the above example, there is no point in backtracking into {page} (that forbids the dot) when a dot must follow // after it. This optimization cannot be applied when the next char is no real separator or when the next variable is // directly adjacent, e.g. '/{x}{y}'. $regexp .= '+'; } } else { if (!preg_match('//u', $regexp)) { $useUtf8 = false; } elseif (!$needsUtf8 && preg_match('/[\x80-\xFF]|(?= 0; --$i) { $token = $tokens[$i]; if ('variable' === $token[0] && $route->hasDefault($token[3])) { $firstOptional = $i; } else { break; } } } // compute the matching regexp $regexp = ''; for ($i = 0, $nbToken = \count($tokens); $i < $nbToken; ++$i) { $regexp .= self::computeRegexp($tokens, $i, $firstOptional); } $regexp = self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'sD'.($isHost ? 'i' : ''); // enable Utf8 matching if really required if ($needsUtf8) { $regexp .= 'u'; for ($i = 0, $nbToken = \count($tokens); $i < $nbToken; ++$i) { if ('variable' === $tokens[$i][0]) { $tokens[$i][] = true; } } } return [ 'staticPrefix' => self::determineStaticPrefix($route, $tokens), 'regex' => $regexp, 'tokens' => array_reverse($tokens), 'variables' => $variables, ]; } /** * Determines the longest static prefix possible for a route. * * @return string The leading static part of a route's path */ private static function determineStaticPrefix(Route $route, array $tokens) { if ('text' !== $tokens[0][0]) { return ($route->hasDefault($tokens[0][3]) || '/' === $tokens[0][1]) ? '' : $tokens[0][1]; } $prefix = $tokens[0][1]; if (isset($tokens[1][1]) && '/' !== $tokens[1][1] && false === $route->hasDefault($tokens[1][3])) { $prefix .= $tokens[1][1]; } return $prefix; } /** * Returns the next static character in the Route pattern that will serve as a separator. * * @param string $pattern The route pattern * @param bool $useUtf8 Whether the character is encoded in UTF-8 or not * * @return string The next static character that functions as separator (or empty string when none available) */ private static function findNextSeparator($pattern, $useUtf8) { if ('' == $pattern) { // return empty string if pattern is empty or false (false which can be returned by substr) return ''; } // first remove all placeholders from the pattern so we can find the next real static character if ('' === $pattern = preg_replace('#\{\w+\}#', '', $pattern)) { return ''; } if ($useUtf8) { preg_match('/^./u', $pattern, $pattern); } return false !== strpos(static::SEPARATORS, $pattern[0]) ? $pattern[0] : ''; } /** * Computes the regexp used to match a specific token. It can be static text or a subpattern. * * @param array $tokens The route tokens * @param int $index The index of the current token * @param int $firstOptional The index of the first optional token * * @return string The regexp pattern for a single token */ private static function computeRegexp(array $tokens, $index, $firstOptional) { $token = $tokens[$index]; if ('text' === $token[0]) { // Text tokens return preg_quote($token[1], self::REGEX_DELIMITER); } else { // Variable tokens if (0 === $index && 0 === $firstOptional) { // When the only token is an optional variable token, the separator is required return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); } else { $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); if ($index >= $firstOptional) { // Enclose each optional token in a subpattern to make it optional. // "?:" means it is non-capturing, i.e. the portion of the subject string that // matched the optional subpattern is not passed back. $regexp = "(?:$regexp"; $nbTokens = \count($tokens); if ($nbTokens - 1 == $index) { // Close the optional subpatterns $regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0)); } } return $regexp; } } } } PK[ZZERouterInterface.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; /** * RouterInterface is the interface that all Router classes must implement. * * This interface is the concatenation of UrlMatcherInterface and UrlGeneratorInterface. * * @author Fabien Potencier */ interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface { /** * Gets the RouteCollection instance associated with this Router. * * WARNING: This method should never be used at runtime as it is SLOW. * You might use it in a cache warmer though. * * @return RouteCollection A RouteCollection instance */ public function getRouteCollection(); } PK[ZZ*M Loader/ContainerLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Psr\Container\ContainerInterface; /** * A route loader that executes a service from a PSR-11 container to load the routes. * * @author Ryan Weaver */ class ContainerLoader extends ObjectLoader { private $container; public function __construct(ContainerInterface $container, string $env = null) { $this->container = $container; parent::__construct($env); } /** * {@inheritdoc} */ public function supports(mixed $resource, string $type = null): bool { return 'service' === $type && \is_string($resource); } /** * {@inheritdoc} */ protected function getObject(string $id): object { return $this->container->get($id); } } PK[ZZ  $Loader/AnnotationDirectoryLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Routing\RouteCollection; /** * AnnotationDirectoryLoader loads routing information from annotations set * on PHP classes and methods. * * @author Fabien Potencier */ class AnnotationDirectoryLoader extends AnnotationFileLoader { /** * Loads from annotations from a directory. * * @param string $path A directory path * @param string|null $type The resource type * * @return RouteCollection A RouteCollection instance * * @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed */ public function load($path, $type = null) { if (!is_dir($dir = $this->locator->locate($path))) { return parent::supports($path, $type) ? parent::load($path, $type) : new RouteCollection(); } $collection = new RouteCollection(); $collection->addResource(new DirectoryResource($dir, '/\.php$/')); $files = iterator_to_array(new \RecursiveIteratorIterator( new \RecursiveCallbackFilterIterator( new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS), function (\SplFileInfo $current) { return '.' !== substr($current->getBasename(), 0, 1); } ), \RecursiveIteratorIterator::LEAVES_ONLY )); usort($files, function (\SplFileInfo $a, \SplFileInfo $b) { return (string) $a > (string) $b ? 1 : -1; }); foreach ($files as $file) { if (!$file->isFile() || '.php' !== substr($file->getFilename(), -4)) { continue; } if ($class = $this->findClass($file)) { $refl = new \ReflectionClass($class); if ($refl->isAbstract()) { continue; } $collection->addCollection($this->loader->load($class, $type)); } } return $collection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { if ('annotation' === $type) { return true; } if ($type || !\is_string($resource)) { return false; } try { return is_dir($this->locator->locate($resource)); } catch (\Exception $e) { return false; } } } PK[ZZ`% % Loader/ObjectLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\RouteCollection; /** * A route loader that calls a method on an object to load the routes. * * @author Ryan Weaver */ abstract class ObjectLoader extends Loader { /** * Returns the object that the method will be called on to load routes. * * For example, if your application uses a service container, * the $id may be a service id. */ abstract protected function getObject(string $id): object; /** * Calls the object method that will load the routes. */ public function load(mixed $resource, string $type = null): RouteCollection { if (!preg_match('/^[^\:]+(?:::(?:[^\:]+))?$/', $resource)) { throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the %s route loader: use the format "object_id::method" or "object_id" if your object class has an "__invoke" method.', $resource, \is_string($type) ? '"'.$type.'"' : 'object')); } $parts = explode('::', $resource); $method = $parts[1] ?? '__invoke'; $loaderObject = $this->getObject($parts[0]); if (!\is_object($loaderObject)) { throw new \TypeError(sprintf('"%s:getObject()" must return an object: "%s" returned.', static::class, get_debug_type($loaderObject))); } if (!\is_callable([$loaderObject, $method])) { throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s".', $method, get_debug_type($loaderObject), $resource)); } $routeCollection = $loaderObject->$method($this, $this->env); if (!$routeCollection instanceof RouteCollection) { $type = get_debug_type($routeCollection); throw new \LogicException(sprintf('The "%s::%s()" method must return a RouteCollection: "%s" returned.', get_debug_type($loaderObject), $method, $type)); } // make the object file tracked so that if it changes, the cache rebuilds $this->addClassResource(new \ReflectionClass($loaderObject), $routeCollection); return $routeCollection; } private function addClassResource(\ReflectionClass $class, RouteCollection $collection) { do { if (is_file($class->getFileName())) { $collection->addResource(new FileResource($class->getFileName())); } } while ($class = $class->getParentClass()); } } PK[ZZva]]Loader/AnnotationFileLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\FileLocatorInterface; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\RouteCollection; /** * AnnotationFileLoader loads routing information from annotations set * on a PHP class and its methods. * * @author Fabien Potencier */ class AnnotationFileLoader extends FileLoader { protected $loader; /** * @throws \RuntimeException */ public function __construct(FileLocatorInterface $locator, AnnotationClassLoader $loader) { if (!\function_exists('token_get_all')) { throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.'); } parent::__construct($locator); $this->loader = $loader; } /** * Loads from annotations from a file. * * @param string $file A PHP file path * @param string|null $type The resource type * * @return RouteCollection|null A RouteCollection instance * * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed */ public function load($file, $type = null) { $path = $this->locator->locate($file); $collection = new RouteCollection(); if ($class = $this->findClass($path)) { $refl = new \ReflectionClass($class); if ($refl->isAbstract()) { return null; } $collection->addResource(new FileResource($path)); $collection->addCollection($this->loader->load($class, $type)); } if (\PHP_VERSION_ID >= 70000) { // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 gc_mem_caches(); } return $collection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'annotation' === $type); } /** * Returns the full class name for the first class in the file. * * @param string $file A PHP file path * * @return string|false Full class name if found, false otherwise */ protected function findClass($file) { $class = false; $namespace = false; $tokens = token_get_all(file_get_contents($file)); if (1 === \count($tokens) && \T_INLINE_HTML === $tokens[0][0]) { throw new \InvalidArgumentException(sprintf('The file "%s" does not contain PHP code. Did you forgot to add the " true, \T_STRING => true]; if (\defined('T_NAME_QUALIFIED')) { $nsTokens[T_NAME_QUALIFIED] = true; } for ($i = 0; isset($tokens[$i]); ++$i) { $token = $tokens[$i]; if (!isset($token[1])) { continue; } if (true === $class && \T_STRING === $token[0]) { return $namespace.'\\'.$token[1]; } if (true === $namespace && isset($nsTokens[$token[0]])) { $namespace = $token[1]; while (isset($tokens[++$i][1], $nsTokens[$tokens[$i][0]])) { $namespace .= $tokens[$i][1]; } $token = $tokens[$i]; } if (\T_CLASS === $token[0]) { // Skip usage of ::class constant and anonymous classes $skipClassToken = false; for ($j = $i - 1; $j > 0; --$j) { if (!isset($tokens[$j][1])) { break; } if (\T_DOUBLE_COLON === $tokens[$j][0] || \T_NEW === $tokens[$j][0]) { $skipClassToken = true; break; } elseif (!\in_array($tokens[$j][0], [\T_WHITESPACE, \T_DOC_COMMENT, \T_COMMENT])) { break; } } if (!$skipClassToken) { $class = true; } } if (\T_NAMESPACE === $token[0]) { $namespace = true; } } return false; } } PK[ZZ XLoader/DirectoryLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Routing\RouteCollection; class DirectoryLoader extends FileLoader { /** * {@inheritdoc} */ public function load($file, $type = null) { $path = $this->locator->locate($file); $collection = new RouteCollection(); $collection->addResource(new DirectoryResource($path)); foreach (scandir($path) as $dir) { if ('.' !== $dir[0]) { $this->setCurrentDir($path); $subPath = $path.'/'.$dir; $subType = null; if (is_dir($subPath)) { $subPath .= '/'; $subType = 'directory'; } $subCollection = $this->import($subPath, $subType, false, $path); $collection->addCollection($subCollection); } } return $collection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { // only when type is forced to directory, not to conflict with AnnotationLoader return 'directory' === $type; } } PK[ZZU[ -))Loader/ClosureLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Routing\RouteCollection; /** * ClosureLoader loads routes from a PHP closure. * * The Closure must return a RouteCollection instance. * * @author Fabien Potencier */ class ClosureLoader extends Loader { /** * Loads a Closure. * * @param \Closure $closure A Closure * @param string|null $type The resource type * * @return RouteCollection A RouteCollection instance */ public function load($closure, $type = null) { return $closure(); } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return $resource instanceof \Closure && (!$type || 'closure' === $type); } } PK[ZZ(Ck#k#Loader/YamlFileLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Parser as YamlParser; /** * YamlFileLoader loads Yaml routing files. * * @author Fabien Potencier * @author Tobias Schultze */ class YamlFileLoader extends FileLoader { private static $availableKeys = [ 'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', ]; private $yamlParser; /** * Loads a Yaml file. * * @param string $file A Yaml file path * @param string|null $type The resource type * * @return RouteCollection A RouteCollection instance * * @throws \InvalidArgumentException When a route can't be parsed because YAML is invalid */ public function load($file, $type = null) { $path = $this->locator->locate($file); if (!stream_is_local($path)) { throw new \InvalidArgumentException(sprintf('This is not a local file "%s".', $path)); } if (!file_exists($path)) { throw new \InvalidArgumentException(sprintf('File "%s" not found.', $path)); } if (null === $this->yamlParser) { $this->yamlParser = new YamlParser(); } $prevErrorHandler = set_error_handler(function ($level, $message, $script, $line) use ($file, &$prevErrorHandler) { $message = \E_USER_DEPRECATED === $level ? preg_replace('/ on line \d+/', ' in "'.$file.'"$0', $message) : $message; return $prevErrorHandler ? $prevErrorHandler($level, $message, $script, $line) : false; }); try { $parsedConfig = $this->yamlParser->parseFile($path); } catch (ParseException $e) { throw new \InvalidArgumentException(sprintf('The file "%s" does not contain valid YAML: ', $path).$e->getMessage(), 0, $e); } finally { restore_error_handler(); } $collection = new RouteCollection(); $collection->addResource(new FileResource($path)); // empty file if (null === $parsedConfig) { return $collection; } // not an array if (!\is_array($parsedConfig)) { throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $path)); } foreach ($parsedConfig as $name => $config) { $this->validate($config, $name, $path); if (isset($config['resource'])) { $this->parseImport($collection, $config, $path, $file); } else { $this->parseRoute($collection, $name, $config, $path); } } return $collection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return \is_string($resource) && \in_array(pathinfo($resource, \PATHINFO_EXTENSION), ['yml', 'yaml'], true) && (!$type || 'yaml' === $type); } /** * Parses a route and adds it to the RouteCollection. * * @param RouteCollection $collection A RouteCollection instance * @param string $name Route name * @param array $config Route definition * @param string $path Full path of the YAML file being processed */ protected function parseRoute(RouteCollection $collection, $name, array $config, $path) { $defaults = isset($config['defaults']) ? $config['defaults'] : []; $requirements = isset($config['requirements']) ? $config['requirements'] : []; $options = isset($config['options']) ? $config['options'] : []; $host = isset($config['host']) ? $config['host'] : ''; $schemes = isset($config['schemes']) ? $config['schemes'] : []; $methods = isset($config['methods']) ? $config['methods'] : []; $condition = isset($config['condition']) ? $config['condition'] : null; if (isset($config['controller'])) { $defaults['_controller'] = $config['controller']; } $route = new Route($config['path'], $defaults, $requirements, $options, $host, $schemes, $methods, $condition); $collection->add($name, $route); } /** * Parses an import and adds the routes in the resource to the RouteCollection. * * @param RouteCollection $collection A RouteCollection instance * @param array $config Route definition * @param string $path Full path of the YAML file being processed * @param string $file Loaded file name */ protected function parseImport(RouteCollection $collection, array $config, $path, $file) { $type = isset($config['type']) ? $config['type'] : null; $prefix = isset($config['prefix']) ? $config['prefix'] : ''; $defaults = isset($config['defaults']) ? $config['defaults'] : []; $requirements = isset($config['requirements']) ? $config['requirements'] : []; $options = isset($config['options']) ? $config['options'] : []; $host = isset($config['host']) ? $config['host'] : null; $condition = isset($config['condition']) ? $config['condition'] : null; $schemes = isset($config['schemes']) ? $config['schemes'] : null; $methods = isset($config['methods']) ? $config['methods'] : null; if (isset($config['controller'])) { $defaults['_controller'] = $config['controller']; } $this->setCurrentDir(\dirname($path)); $imported = $this->import($config['resource'], $type, false, $file) ?: []; if (!\is_array($imported)) { $imported = [$imported]; } foreach ($imported as $subCollection) { /* @var $subCollection RouteCollection */ $subCollection->addPrefix($prefix); if (null !== $host) { $subCollection->setHost($host); } if (null !== $condition) { $subCollection->setCondition($condition); } if (null !== $schemes) { $subCollection->setSchemes($schemes); } if (null !== $methods) { $subCollection->setMethods($methods); } $subCollection->addDefaults($defaults); $subCollection->addRequirements($requirements); $subCollection->addOptions($options); $collection->addCollection($subCollection); } } /** * Validates the route configuration. * * @param array $config A resource config * @param string $name The config key * @param string $path The loaded file path * * @throws \InvalidArgumentException If one of the provided config keys is not supported, * something is missing or the combination is nonsense */ protected function validate($config, $name, $path) { if (!\is_array($config)) { throw new \InvalidArgumentException(sprintf('The definition of "%s" in "%s" must be a YAML array.', $name, $path)); } if ($extraKeys = array_diff(array_keys($config), self::$availableKeys)) { throw new \InvalidArgumentException(sprintf('The routing file "%s" contains unsupported keys for "%s": "%s". Expected one of: "%s".', $path, $name, implode('", "', $extraKeys), implode('", "', self::$availableKeys))); } if (isset($config['resource']) && isset($config['path'])) { throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "resource" key and the "path" key for "%s". Choose between an import and a route definition.', $path, $name)); } if (!isset($config['resource']) && isset($config['type'])) { throw new \InvalidArgumentException(sprintf('The "type" key for the route definition "%s" in "%s" is unsupported. It is only available for imports in combination with the "resource" key.', $name, $path)); } if (!isset($config['resource']) && !isset($config['path'])) { throw new \InvalidArgumentException(sprintf('You must define a "path" for the route "%s" in file "%s".', $name, $path)); } if (isset($config['controller']) && isset($config['defaults']['_controller'])) { throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" key and the defaults key "_controller" for "%s".', $path, $name)); } } } PK[ZZ%Loader/PhpFileLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator; use Symfony\Component\Routing\RouteCollection; /** * PhpFileLoader loads routes from a PHP file. * * The file must return a RouteCollection instance. * * @author Fabien Potencier */ class PhpFileLoader extends FileLoader { /** * Loads a PHP file. * * @param string $file A PHP file path * @param string|null $type The resource type * * @return RouteCollection A RouteCollection instance */ public function load($file, $type = null) { $path = $this->locator->locate($file); $this->setCurrentDir(\dirname($path)); // the closure forbids access to the private scope in the included file $loader = $this; $load = \Closure::bind(static function ($file) use ($loader) { return include $file; }, null, ProtectedPhpFileLoader::class); $result = $load($path); if ($result instanceof \Closure) { $collection = new RouteCollection(); $result(new RoutingConfigurator($collection, $this, $path, $file)); } else { $collection = $result; } $collection->addResource(new FileResource($path)); return $collection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return \is_string($resource) && 'php' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'php' === $type); } } /** * @internal */ final class ProtectedPhpFileLoader extends PhpFileLoader { } PK[ZZȒ4s4s4Loader/XmlFileLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * XmlFileLoader loads XML routing files. * * @author Fabien Potencier * @author Tobias Schultze */ class XmlFileLoader extends FileLoader { const NAMESPACE_URI = 'http://symfony.com/schema/routing'; const SCHEME_PATH = '/schema/routing/routing-1.0.xsd'; /** * Loads an XML file. * * @param string $file An XML file path * @param string|null $type The resource type * * @return RouteCollection A RouteCollection instance * * @throws \InvalidArgumentException when the file cannot be loaded or when the XML cannot be * parsed because it does not validate against the scheme */ public function load($file, $type = null) { $path = $this->locator->locate($file); $xml = $this->loadFile($path); $collection = new RouteCollection(); $collection->addResource(new FileResource($path)); // process routes and imports foreach ($xml->documentElement->childNodes as $node) { if (!$node instanceof \DOMElement) { continue; } $this->parseNode($collection, $node, $path, $file); } return $collection; } /** * Parses a node from a loaded XML file. * * @param RouteCollection $collection Collection to associate with the node * @param \DOMElement $node Element to parse * @param string $path Full path of the XML file being processed * @param string $file Loaded file name * * @throws \InvalidArgumentException When the XML is invalid */ protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file) { if (self::NAMESPACE_URI !== $node->namespaceURI) { return; } switch ($node->localName) { case 'route': $this->parseRoute($collection, $node, $path); break; case 'import': $this->parseImport($collection, $node, $path, $file); break; default: throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "route" or "import".', $node->localName, $path)); } } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return \is_string($resource) && 'xml' === pathinfo($resource, \PATHINFO_EXTENSION) && (!$type || 'xml' === $type); } /** * Parses a route and adds it to the RouteCollection. * * @param RouteCollection $collection RouteCollection instance * @param \DOMElement $node Element to parse that represents a Route * @param string $path Full path of the XML file being processed * * @throws \InvalidArgumentException When the XML is invalid */ protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path) { if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) { throw new \InvalidArgumentException(sprintf('The element in file "%s" must have an "id" and a "path" attribute.', $path)); } $schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, \PREG_SPLIT_NO_EMPTY); $methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, \PREG_SPLIT_NO_EMPTY); list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path); $route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); $collection->add($id, $route); } /** * Parses an import and adds the routes in the resource to the RouteCollection. * * @param RouteCollection $collection RouteCollection instance * @param \DOMElement $node Element to parse that represents a Route * @param string $path Full path of the XML file being processed * @param string $file Loaded file name * * @throws \InvalidArgumentException When the XML is invalid */ protected function parseImport(RouteCollection $collection, \DOMElement $node, $path, $file) { if ('' === $resource = $node->getAttribute('resource')) { throw new \InvalidArgumentException(sprintf('The element in file "%s" must have a "resource" attribute.', $path)); } $type = $node->getAttribute('type'); $prefix = $node->getAttribute('prefix'); $host = $node->hasAttribute('host') ? $node->getAttribute('host') : null; $schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, \PREG_SPLIT_NO_EMPTY) : null; $methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, \PREG_SPLIT_NO_EMPTY) : null; list($defaults, $requirements, $options, $condition) = $this->parseConfigs($node, $path); $this->setCurrentDir(\dirname($path)); /** @var RouteCollection[] $imported */ $imported = $this->import($resource, ('' !== $type ? $type : null), false, $file) ?: []; if (!\is_array($imported)) { $imported = [$imported]; } foreach ($imported as $subCollection) { /* @var $subCollection RouteCollection */ $subCollection->addPrefix($prefix); if (null !== $host) { $subCollection->setHost($host); } if (null !== $condition) { $subCollection->setCondition($condition); } if (null !== $schemes) { $subCollection->setSchemes($schemes); } if (null !== $methods) { $subCollection->setMethods($methods); } $subCollection->addDefaults($defaults); $subCollection->addRequirements($requirements); $subCollection->addOptions($options); $collection->addCollection($subCollection); } } /** * Loads an XML file. * * @param string $file An XML file path * * @return \DOMDocument * * @throws \InvalidArgumentException When loading of XML file fails because of syntax errors * or when the XML structure is not as expected by the scheme - * see validate() */ protected function loadFile($file) { return XmlUtils::loadFile($file, __DIR__.static::SCHEME_PATH); } /** * Parses the config elements (default, requirement, option). * * @param \DOMElement $node Element to parse that contains the configs * @param string $path Full path of the XML file being processed * * @return array An array with the defaults as first item, requirements as second and options as third * * @throws \InvalidArgumentException When the XML is invalid */ private function parseConfigs(\DOMElement $node, $path) { $defaults = []; $requirements = []; $options = []; $condition = null; /** @var \DOMElement $n */ foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) { if ($node !== $n->parentNode) { continue; } switch ($n->localName) { case 'default': if ($this->isElementValueNull($n)) { $defaults[$n->getAttribute('key')] = null; } else { $defaults[$n->getAttribute('key')] = $this->parseDefaultsConfig($n, $path); } break; case 'requirement': $requirements[$n->getAttribute('key')] = trim($n->textContent); break; case 'option': $options[$n->getAttribute('key')] = XmlUtils::phpize(trim($n->textContent)); break; case 'condition': $condition = trim($n->textContent); break; default: throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "default", "requirement", "option" or "condition".', $n->localName, $path)); } } if ($controller = $node->getAttribute('controller')) { if (isset($defaults['_controller'])) { $name = $node->hasAttribute('id') ? sprintf('"%s".', $node->getAttribute('id')) : sprintf('the "%s" tag.', $node->tagName); throw new \InvalidArgumentException(sprintf('The routing file "%s" must not specify both the "controller" attribute and the defaults key "_controller" for ', $path).$name); } $defaults['_controller'] = $controller; } return [$defaults, $requirements, $options, $condition]; } /** * Parses the "default" elements. * * @param \DOMElement $element The "default" element to parse * @param string $path Full path of the XML file being processed * * @return array|bool|float|int|string|null The parsed value of the "default" element */ private function parseDefaultsConfig(\DOMElement $element, $path) { if ($this->isElementValueNull($element)) { return null; } // Check for existing element nodes in the default element. There can // only be a single element inside a default element. So this element // (if one was found) can safely be returned. foreach ($element->childNodes as $child) { if (!$child instanceof \DOMElement) { continue; } if (self::NAMESPACE_URI !== $child->namespaceURI) { continue; } return $this->parseDefaultNode($child, $path); } // If the default element doesn't contain a nested "bool", "int", "float", // "string", "list", or "map" element, the element contents will be treated // as the string value of the associated default option. return trim($element->textContent); } /** * Recursively parses the value of a "default" element. * * @param \DOMElement $node The node value * @param string $path Full path of the XML file being processed * * @return array|bool|float|int|string The parsed value * * @throws \InvalidArgumentException when the XML is invalid */ private function parseDefaultNode(\DOMElement $node, $path) { if ($this->isElementValueNull($node)) { return null; } switch ($node->localName) { case 'bool': return 'true' === trim($node->nodeValue) || '1' === trim($node->nodeValue); case 'int': return (int) trim($node->nodeValue); case 'float': return (float) trim($node->nodeValue); case 'string': return trim($node->nodeValue); case 'list': $list = []; foreach ($node->childNodes as $element) { if (!$element instanceof \DOMElement) { continue; } if (self::NAMESPACE_URI !== $element->namespaceURI) { continue; } $list[] = $this->parseDefaultNode($element, $path); } return $list; case 'map': $map = []; foreach ($node->childNodes as $element) { if (!$element instanceof \DOMElement) { continue; } if (self::NAMESPACE_URI !== $element->namespaceURI) { continue; } $map[$element->getAttribute('key')] = $this->parseDefaultNode($element, $path); } return $map; default: throw new \InvalidArgumentException(sprintf('Unknown tag "%s" used in file "%s". Expected "bool", "int", "float", "string", "list", or "map".', $node->localName, $path)); } } private function isElementValueNull(\DOMElement $element) { $namespaceUri = 'http://www.w3.org/2001/XMLSchema-instance'; if (!$element->hasAttributeNS($namespaceUri, 'nil')) { return false; } return 'true' === $element->getAttributeNS($namespaceUri, 'nil') || '1' === $element->getAttributeNS($namespaceUri, 'nil'); } } PK[ZZ'@i"" Loader/AnnotationClassLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Doctrine\Common\Annotations\Reader; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Loader\LoaderResolverInterface; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Annotation\Route as RouteAnnotation; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * AnnotationClassLoader loads routing information from a PHP class and its methods. * * You need to define an implementation for the configureRoute() method. Most of the * time, this method should define some PHP callable to be called for the route * (a controller in MVC speak). * * The @Route annotation can be set on the class (for global parameters), * and on each method. * * The @Route annotation main value is the route path. The annotation also * recognizes several parameters: requirements, options, defaults, schemes, * methods, host, and name. The name parameter is mandatory. * Here is an example of how you should be able to use it: * /** * * @Route("/Blog") * * / * class Blog * { * /** * * @Route("/", name="blog_index") * * / * public function index() * { * } * /** * * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"}) * * / * public function show() * { * } * } * * @author Fabien Potencier */ abstract class AnnotationClassLoader implements LoaderInterface { protected $reader; /** * @var string */ protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route'; /** * @var int */ protected $defaultRouteIndex = 0; public function __construct(Reader $reader) { $this->reader = $reader; } /** * Sets the annotation class to read route properties from. * * @param string $class A fully-qualified class name */ public function setRouteAnnotationClass($class) { $this->routeAnnotationClass = $class; } /** * Loads from annotations from a class. * * @param string $class A class name * @param string|null $type The resource type * * @return RouteCollection A RouteCollection instance * * @throws \InvalidArgumentException When route can't be parsed */ public function load($class, $type = null) { if (!class_exists($class)) { throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); } $class = new \ReflectionClass($class); if ($class->isAbstract()) { throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class->getName())); } $globals = $this->getGlobals($class); $collection = new RouteCollection(); $collection->addResource(new FileResource($class->getFileName())); foreach ($class->getMethods() as $method) { $this->defaultRouteIndex = 0; foreach ($this->reader->getMethodAnnotations($method) as $annot) { if ($annot instanceof $this->routeAnnotationClass) { $this->addRoute($collection, $annot, $globals, $class, $method); } } } if (0 === $collection->count() && $class->hasMethod('__invoke')) { $globals = $this->resetGlobals(); foreach ($this->reader->getClassAnnotations($class) as $annot) { if ($annot instanceof $this->routeAnnotationClass) { $this->addRoute($collection, $annot, $globals, $class, $class->getMethod('__invoke')); } } } return $collection; } /** * @param RouteAnnotation $annot or an object that exposes a similar interface * @param array $globals */ protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method) { $name = $annot->getName(); if (null === $name) { $name = $this->getDefaultRouteName($class, $method); } $name = $globals['name'].$name; $defaults = array_replace($globals['defaults'], $annot->getDefaults()); foreach ($method->getParameters() as $param) { if (false !== strpos($globals['path'].$annot->getPath(), sprintf('{%s}', $param->getName())) && !isset($defaults[$param->getName()]) && $param->isDefaultValueAvailable()) { $defaults[$param->getName()] = $param->getDefaultValue(); } } $requirements = array_replace($globals['requirements'], $annot->getRequirements()); $options = array_replace($globals['options'], $annot->getOptions()); $schemes = array_merge($globals['schemes'], $annot->getSchemes()); $methods = array_merge($globals['methods'], $annot->getMethods()); $host = $annot->getHost(); if (null === $host) { $host = $globals['host']; } $condition = $annot->getCondition(); if (null === $condition) { $condition = $globals['condition']; } $route = $this->createRoute($globals['path'].$annot->getPath(), $defaults, $requirements, $options, $host, $schemes, $methods, $condition); $this->configureRoute($route, $class, $method, $annot); $collection->add($name, $route); } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return \is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type); } /** * {@inheritdoc} */ public function setResolver(LoaderResolverInterface $resolver) { } /** * {@inheritdoc} */ public function getResolver() { } /** * Gets the default route name for a class method. * * @return string */ protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) { $name = str_replace('\\', '_', $class->name).'_'.$method->name; $name = \function_exists('mb_strtolower') && preg_match('//u', $name) ? mb_strtolower($name, 'UTF-8') : strtolower($name); if ($this->defaultRouteIndex > 0) { $name .= '_'.$this->defaultRouteIndex; } ++$this->defaultRouteIndex; return $name; } protected function getGlobals(\ReflectionClass $class) { $globals = $this->resetGlobals(); if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) { if (null !== $annot->getName()) { $globals['name'] = $annot->getName(); } if (null !== $annot->getPath()) { $globals['path'] = $annot->getPath(); } if (null !== $annot->getRequirements()) { $globals['requirements'] = $annot->getRequirements(); } if (null !== $annot->getOptions()) { $globals['options'] = $annot->getOptions(); } if (null !== $annot->getDefaults()) { $globals['defaults'] = $annot->getDefaults(); } if (null !== $annot->getSchemes()) { $globals['schemes'] = $annot->getSchemes(); } if (null !== $annot->getMethods()) { $globals['methods'] = $annot->getMethods(); } if (null !== $annot->getHost()) { $globals['host'] = $annot->getHost(); } if (null !== $annot->getCondition()) { $globals['condition'] = $annot->getCondition(); } } return $globals; } private function resetGlobals() { return [ 'path' => '', 'requirements' => [], 'options' => [], 'defaults' => [], 'schemes' => [], 'methods' => [], 'host' => '', 'condition' => '', 'name' => '', ]; } protected function createRoute($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition) { return new Route($path, $defaults, $requirements, $options, $host, $schemes, $methods, $condition); } abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot); } PK[ZZfL%Loader/schema/routing/routing-1.0.xsdnu[ PK[ZZMn.Loader/Configurator/CollectionConfigurator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * @author Nicolas Grekas */ class CollectionConfigurator { use Traits\AddTrait; use Traits\RouteTrait; private $parent; private $parentConfigurator; public function __construct(RouteCollection $parent, $name, self $parentConfigurator = null) { $this->parent = $parent; $this->name = $name; $this->collection = new RouteCollection(); $this->route = new Route(''); $this->parentConfigurator = $parentConfigurator; // for GC control } public function __destruct() { $this->collection->addPrefix(rtrim($this->route->getPath(), '/')); $this->parent->addCollection($this->collection); } /** * Adds a route. * * @param string $name * @param string $path * * @return RouteConfigurator */ final public function add($name, $path) { $this->collection->add($this->name.$name, $route = clone $this->route); return new RouteConfigurator($this->collection, $route->setPath($path), $this->name, $this); } /** * Creates a sub-collection. * * @return self */ final public function collection($name = '') { return new self($this->collection, $this->name.$name, $this); } /** * Sets the prefix to add to the path of all child routes. * * @param string $prefix * * @return $this */ final public function prefix($prefix) { $this->route->setPath($prefix); return $this; } } PK[ZZ hh)Loader/Configurator/RouteConfigurator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * @author Nicolas Grekas */ class RouteConfigurator { use Traits\AddTrait; use Traits\RouteTrait; private $parentConfigurator; public function __construct(RouteCollection $collection, Route $route, $name = '', CollectionConfigurator $parentConfigurator = null) { $this->collection = $collection; $this->route = $route; $this->name = $name; $this->parentConfigurator = $parentConfigurator; // for GC control } } PK[ZZ iG*Loader/Configurator/ImportConfigurator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator; use Symfony\Component\Routing\RouteCollection; /** * @author Nicolas Grekas */ class ImportConfigurator { use Traits\RouteTrait; private $parent; public function __construct(RouteCollection $parent, RouteCollection $route) { $this->parent = $parent; $this->route = $route; } public function __destruct() { $this->parent->addCollection($this->route); } /** * Sets the prefix to add to the path of all child routes. * * @param string $prefix * * @return $this */ final public function prefix($prefix) { $this->route->addPrefix($prefix); return $this; } } PK[ZZ{||+Loader/Configurator/RoutingConfigurator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator; use Symfony\Component\Routing\Loader\PhpFileLoader; use Symfony\Component\Routing\RouteCollection; /** * @author Nicolas Grekas */ class RoutingConfigurator { use Traits\AddTrait; private $loader; private $path; private $file; public function __construct(RouteCollection $collection, PhpFileLoader $loader, $path, $file) { $this->collection = $collection; $this->loader = $loader; $this->path = $path; $this->file = $file; } /** * @return ImportConfigurator */ final public function import($resource, $type = null, $ignoreErrors = false) { $this->loader->setCurrentDir(\dirname($this->path)); $imported = $this->loader->import($resource, $type, $ignoreErrors, $this->file) ?: []; if (!\is_array($imported)) { return new ImportConfigurator($this->collection, $imported); } $mergedCollection = new RouteCollection(); foreach ($imported as $subCollection) { $mergedCollection->addCollection($subCollection); } return new ImportConfigurator($this->collection, $mergedCollection); } /** * @return CollectionConfigurator */ final public function collection($name = '') { return new CollectionConfigurator($this->collection, $name); } } PK[ZZA(Loader/Configurator/Traits/HostTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator\Traits; use Symfony\Component\Routing\RouteCollection; /** * @internal */ trait HostTrait { final protected function addHost(RouteCollection $routes, string|array $hosts) { if (!$hosts || !\is_array($hosts)) { $routes->setHost($hosts ?: ''); return; } foreach ($routes->all() as $name => $route) { if (null === $locale = $route->getDefault('_locale')) { $routes->remove($name); foreach ($hosts as $locale => $host) { $localizedRoute = clone $route; $localizedRoute->setDefault('_locale', $locale); $localizedRoute->setRequirement('_locale', preg_quote($locale)); $localizedRoute->setDefault('_canonical_route', $name); $localizedRoute->setHost($host); $routes->add($name.'.'.$locale, $localizedRoute); } } elseif (!isset($hosts[$locale])) { throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding host in its parent collection.', $name, $locale)); } else { $route->setHost($hosts[$locale]); $route->setRequirement('_locale', preg_quote($locale)); $routes->add($name, $route); } } } } PK[ZZk k *Loader/Configurator/Traits/PrefixTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator\Traits; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * @internal * * @author Nicolas Grekas */ trait PrefixTrait { final protected function addPrefix(RouteCollection $routes, string|array $prefix, bool $trailingSlashOnRoot) { if (\is_array($prefix)) { foreach ($prefix as $locale => $localePrefix) { $prefix[$locale] = trim(trim($localePrefix), '/'); } foreach ($routes->all() as $name => $route) { if (null === $locale = $route->getDefault('_locale')) { $routes->remove($name); foreach ($prefix as $locale => $localePrefix) { $localizedRoute = clone $route; $localizedRoute->setDefault('_locale', $locale); $localizedRoute->setRequirement('_locale', preg_quote($locale)); $localizedRoute->setDefault('_canonical_route', $name); $localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); $routes->add($name.'.'.$locale, $localizedRoute); } } elseif (!isset($prefix[$locale])) { throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale)); } else { $route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath())); $routes->add($name, $route); } } return; } $routes->addPrefix($prefix); if (!$trailingSlashOnRoot) { $rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath(); foreach ($routes->all() as $route) { if ($route->getPath() === $rootPath) { $route->setPath(rtrim($rootPath, '/')); } } } } } PK[ZZ}b )Loader/Configurator/Traits/RouteTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator\Traits; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; trait RouteTrait { /** * @var RouteCollection|Route */ private $route; /** * Adds defaults. * * @return $this */ final public function defaults(array $defaults) { $this->route->addDefaults($defaults); return $this; } /** * Adds requirements. * * @return $this */ final public function requirements(array $requirements) { $this->route->addRequirements($requirements); return $this; } /** * Adds options. * * @return $this */ final public function options(array $options) { $this->route->addOptions($options); return $this; } /** * Sets the condition. * * @param string $condition * * @return $this */ final public function condition($condition) { $this->route->setCondition($condition); return $this; } /** * Sets the pattern for the host. * * @param string $pattern * * @return $this */ final public function host($pattern) { $this->route->setHost($pattern); return $this; } /** * Sets the schemes (e.g. 'https') this route is restricted to. * So an empty array means that any scheme is allowed. * * @param string[] $schemes * * @return $this */ final public function schemes(array $schemes) { $this->route->setSchemes($schemes); return $this; } /** * Sets the HTTP methods (e.g. 'POST') this route is restricted to. * So an empty array means that any method is allowed. * * @param string[] $methods * * @return $this */ final public function methods(array $methods) { $this->route->setMethods($methods); return $this; } /** * Adds the "_controller" entry to defaults. * * @param callable|string $controller a callable or parseable pseudo-callable * * @return $this */ final public function controller($controller) { $this->route->addDefaults(['_controller' => $controller]); return $this; } } PK[ZZYW\ 2Loader/Configurator/Traits/LocalizedRouteTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator\Traits; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * @internal * * @author Nicolas Grekas * @author Jules Pietri */ trait LocalizedRouteTrait { /** * Creates one or many routes. * * @param string|array $path the path, or the localized paths of the route */ final protected function createLocalizedRoute(RouteCollection $collection, string $name, string|array $path, string $namePrefix = '', array $prefixes = null): RouteCollection { $paths = []; $routes = new RouteCollection(); if (\is_array($path)) { if (null === $prefixes) { $paths = $path; } elseif ($missing = array_diff_key($prefixes, $path)) { throw new \LogicException(sprintf('Route "%s" is missing routes for locale(s) "%s".', $name, implode('", "', array_keys($missing)))); } else { foreach ($path as $locale => $localePath) { if (!isset($prefixes[$locale])) { throw new \LogicException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale)); } $paths[$locale] = $prefixes[$locale].$localePath; } } } elseif (null !== $prefixes) { foreach ($prefixes as $locale => $prefix) { $paths[$locale] = $prefix.$path; } } else { $routes->add($namePrefix.$name, $route = $this->createRoute($path)); $collection->add($namePrefix.$name, $route); return $routes; } foreach ($paths as $locale => $path) { $routes->add($name.'.'.$locale, $route = $this->createRoute($path)); $collection->add($namePrefix.$name.'.'.$locale, $route); $route->setDefault('_locale', $locale); $route->setRequirement('_locale', preg_quote($locale)); $route->setDefault('_canonical_route', $namePrefix.$name); } return $routes; } private function createRoute(string $path): Route { return new Route($path); } } PK[ZZ^x'Loader/Configurator/Traits/AddTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator\Traits; use Symfony\Component\Routing\Loader\Configurator\RouteConfigurator; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; trait AddTrait { /** * @var RouteCollection */ private $collection; private $name = ''; /** * Adds a route. * * @param string $name * @param string $path * * @return RouteConfigurator */ final public function add($name, $path) { $parentConfigurator = $this instanceof RouteConfigurator ? $this->parentConfigurator : null; $this->collection->add($this->name.$name, $route = new Route($path)); return new RouteConfigurator($this->collection, $route, '', $parentConfigurator); } /** * Adds a route. * * @param string $name * @param string $path * * @return RouteConfigurator */ final public function __invoke($name, $path) { return $this->add($name, $path); } } PK[ZZ l)Loader/Configurator/AliasConfigurator.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\Configurator; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use Symfony\Component\Routing\Alias; class AliasConfigurator { private $alias; public function __construct(Alias $alias) { $this->alias = $alias; } /** * Whether this alias is deprecated, that means it should not be called anymore. * * @param string $package The name of the composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The deprecation message to use * * @return $this * * @throws InvalidArgumentException when the message template is invalid */ public function deprecate(string $package, string $version, string $message): static { $this->alias->setDeprecated($package, $version, $message); return $this; } } PK[ZZOLoader/GlobFileLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\FileLoader; use Symfony\Component\Routing\RouteCollection; /** * GlobFileLoader loads files from a glob pattern. * * @author Nicolas Grekas */ class GlobFileLoader extends FileLoader { /** * {@inheritdoc} */ public function load($resource, $type = null) { $collection = new RouteCollection(); foreach ($this->glob($resource, false, $globResource) as $path => $info) { $collection->addCollection($this->import($path)); } $collection->addResource($globResource); return $collection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return 'glob' === $type; } } PK[ZZ=))LICENSEnu[Copyright (c) 2004-2020 Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PK[ZZ CompiledRoute.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; /** * CompiledRoutes are returned by the RouteCompiler class. * * @author Fabien Potencier */ class CompiledRoute implements \Serializable { private $variables; private $tokens; private $staticPrefix; private $regex; private $pathVariables; private $hostVariables; private $hostRegex; private $hostTokens; /** * @param string $staticPrefix The static prefix of the compiled route * @param string $regex The regular expression to use to match this route * @param array $tokens An array of tokens to use to generate URL for this route * @param array $pathVariables An array of path variables * @param string|null $hostRegex Host regex * @param array $hostTokens Host tokens * @param array $hostVariables An array of host variables * @param array $variables An array of variables (variables defined in the path and in the host patterns) */ public function __construct($staticPrefix, $regex, array $tokens, array $pathVariables, $hostRegex = null, array $hostTokens = [], array $hostVariables = [], array $variables = []) { $this->staticPrefix = (string) $staticPrefix; $this->regex = $regex; $this->tokens = $tokens; $this->pathVariables = $pathVariables; $this->hostRegex = $hostRegex; $this->hostTokens = $hostTokens; $this->hostVariables = $hostVariables; $this->variables = $variables; } /** * {@inheritdoc} */ public function serialize() { return serialize([ 'vars' => $this->variables, 'path_prefix' => $this->staticPrefix, 'path_regex' => $this->regex, 'path_tokens' => $this->tokens, 'path_vars' => $this->pathVariables, 'host_regex' => $this->hostRegex, 'host_tokens' => $this->hostTokens, 'host_vars' => $this->hostVariables, ]); } /** * {@inheritdoc} */ public function unserialize($serialized) { if (\PHP_VERSION_ID >= 70000) { $data = unserialize($serialized, ['allowed_classes' => false]); } else { $data = unserialize($serialized); } $this->variables = $data['vars']; $this->staticPrefix = $data['path_prefix']; $this->regex = $data['path_regex']; $this->tokens = $data['path_tokens']; $this->pathVariables = $data['path_vars']; $this->hostRegex = $data['host_regex']; $this->hostTokens = $data['host_tokens']; $this->hostVariables = $data['host_vars']; } /** * Returns the static prefix. * * @return string The static prefix */ public function getStaticPrefix() { return $this->staticPrefix; } /** * Returns the regex. * * @return string The regex */ public function getRegex() { return $this->regex; } /** * Returns the host regex. * * @return string|null The host regex or null */ public function getHostRegex() { return $this->hostRegex; } /** * Returns the tokens. * * @return array The tokens */ public function getTokens() { return $this->tokens; } /** * Returns the host tokens. * * @return array The tokens */ public function getHostTokens() { return $this->hostTokens; } /** * Returns the variables. * * @return array The variables */ public function getVariables() { return $this->variables; } /** * Returns the path variables. * * @return array The variables */ public function getPathVariables() { return $this->pathVariables; } /** * Returns the host variables. * * @return array The variables */ public function getHostVariables() { return $this->hostVariables; } } PK[ZZڙi]]Matcher/CompiledUrlMatcher.phpnu[ * * 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 */ class CompiledUrlMatcher extends UrlMatcher { use CompiledUrlMatcherTrait; public function __construct(array $compiledRoutes, RequestContext $context) { $this->context = $context; [$this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition] = $compiledRoutes; } } PK[ZZGG+Matcher/RedirectableUrlMatcherInterface.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; /** * RedirectableUrlMatcherInterface knows how to redirect the user. * * @author Fabien Potencier */ interface RedirectableUrlMatcherInterface { /** * Redirects the user to another URL. * * @param string $path The path info to redirect to * @param string $route The route name that matched * @param string|null $scheme The URL scheme (null to keep the current one) * * @return array An array of parameters */ public function redirect($path, $route, $scheme = null); } PK[ZZ璄##Matcher/UrlMatcher.phpnu[ * * 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\ExpressionLanguage\ExpressionFunctionProviderInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\NoConfigurationException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * UrlMatcher matches URL based on a set of routes. * * @author Fabien Potencier */ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface { const REQUIREMENT_MATCH = 0; const REQUIREMENT_MISMATCH = 1; const ROUTE_MATCH = 2; protected $context; protected $allow = []; protected $routes; protected $request; protected $expressionLanguage; /** * @var ExpressionFunctionProviderInterface[] */ protected $expressionLanguageProviders = []; public function __construct(RouteCollection $routes, RequestContext $context) { $this->routes = $routes; $this->context = $context; } /** * {@inheritdoc} */ public function setContext(RequestContext $context) { $this->context = $context; } /** * {@inheritdoc} */ public function getContext() { return $this->context; } /** * {@inheritdoc} */ public function match($pathinfo) { $this->allow = []; if ($ret = $this->matchCollection(rawurldecode($pathinfo), $this->routes)) { return $ret; } if ('/' === $pathinfo && !$this->allow) { throw new NoConfigurationException(); } throw 0 < \count($this->allow) ? new MethodNotAllowedException(array_unique($this->allow)) : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); } /** * {@inheritdoc} */ public function matchRequest(Request $request) { $this->request = $request; $ret = $this->match($request->getPathInfo()); $this->request = null; return $ret; } public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) { $this->expressionLanguageProviders[] = $provider; } /** * Tries to match a URL with a set of routes. * * @param string $pathinfo The path info to be parsed * @param RouteCollection $routes The set of routes * * @return array An array of parameters * * @throws NoConfigurationException If no routing configuration could be found * @throws ResourceNotFoundException If the resource could not be found * @throws MethodNotAllowedException If the resource was found but the request method is not allowed */ protected function matchCollection($pathinfo, RouteCollection $routes) { // HEAD and GET are equivalent as per RFC if ('HEAD' === $method = $this->context->getMethod()) { $method = 'GET'; } $supportsTrailingSlash = '/' !== $pathinfo && '' !== $pathinfo && $this instanceof RedirectableUrlMatcherInterface; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); $staticPrefix = $compiledRoute->getStaticPrefix(); $requiredMethods = $route->getMethods(); // check the static prefix of the URL first. Only use the more expensive preg_match when it matches if ('' === $staticPrefix || 0 === strpos($pathinfo, $staticPrefix)) { // no-op } elseif (!$supportsTrailingSlash || ($requiredMethods && !\in_array('GET', $requiredMethods)) || 'GET' !== $method) { continue; } elseif ('/' === substr($staticPrefix, -1) && substr($staticPrefix, 0, -1) === $pathinfo) { return $this->allow = []; } else { continue; } $regex = $compiledRoute->getRegex(); if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) { $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2); $hasTrailingSlash = true; } else { $hasTrailingSlash = false; } if (!preg_match($regex, $pathinfo, $matches)) { continue; } if ($hasTrailingSlash && '/' !== substr($pathinfo, -1)) { if ((!$requiredMethods || \in_array('GET', $requiredMethods)) && 'GET' === $method) { return $this->allow = []; } continue; } $hostMatches = []; if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { continue; } $status = $this->handleRouteRequirements($pathinfo, $name, $route); if (self::REQUIREMENT_MISMATCH === $status[0]) { continue; } // check HTTP method requirement if ($requiredMethods) { if (!\in_array($method, $requiredMethods)) { if (self::REQUIREMENT_MATCH === $status[0]) { $this->allow = array_merge($this->allow, $requiredMethods); } continue; } } return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : [])); } return []; } /** * Returns an array of values to use as request attributes. * * As this method requires the Route object, it is not available * in matchers that do not have access to the matched Route instance * (like the PHP and Apache matcher dumpers). * * @param Route $route The route we are matching against * @param string $name The name of the route * @param array $attributes An array of attributes from the matcher * * @return array An array of parameters */ protected function getAttributes(Route $route, $name, array $attributes) { $attributes['_route'] = $name; return $this->mergeDefaults($attributes, $route->getDefaults()); } /** * Handles specific route requirements. * * @param string $pathinfo The path * @param string $name The route name * @param Route $route The route * * @return array The first element represents the status, the second contains additional information */ protected function handleRouteRequirements($pathinfo, $name, Route $route) { // expression condition if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), ['context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)])) { return [self::REQUIREMENT_MISMATCH, null]; } // check HTTP scheme requirement $scheme = $this->context->getScheme(); $status = $route->getSchemes() && !$route->hasScheme($scheme) ? self::REQUIREMENT_MISMATCH : self::REQUIREMENT_MATCH; return [$status, null]; } /** * Get merged default parameters. * * @param array $params The parameters * @param array $defaults The defaults * * @return array Merged default parameters */ protected function mergeDefaults($params, $defaults) { foreach ($params as $key => $value) { if (!\is_int($key)) { $defaults[$key] = $value; } } return $defaults; } protected 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; } /** * @internal */ protected function createRequest($pathinfo) { if (!class_exists('Symfony\Component\HttpFoundation\Request')) { return null; } return Request::create($this->context->getScheme().'://'.$this->context->getHost().$this->context->getBaseUrl().$pathinfo, $this->context->getMethod(), $this->context->getParameters(), [], [], [ 'SCRIPT_FILENAME' => $this->context->getBaseUrl(), 'SCRIPT_NAME' => $this->context->getBaseUrl(), ]); } } PK[ZZ%&Matcher/ExpressionLanguageProvider.phpnu[ * * 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\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; use Symfony\Contracts\Service\ServiceProviderInterface; /** * Exposes functions defined in the request context to route conditions. * * @author Ahmed TAILOULOUTE */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface { private $functions; public function __construct(ServiceProviderInterface $functions) { $this->functions = $functions; } /** * {@inheritdoc} */ public function getFunctions(): array { $functions = []; foreach ($this->functions->getProvidedServices() as $function => $type) { $functions[] = new ExpressionFunction( $function, static function (...$args) use ($function) { return sprintf('($context->getParameter(\'_functions\')->get(%s)(%s))', var_export($function, true), implode(', ', $args)); }, function ($values, ...$args) use ($function) { return $values['context']->getParameter('_functions')->get($function)(...$args); } ); } return $functions; } public function get(string $function): callable { return $this->functions->get($function); } } PK[ZZh#Matcher/RequestMatcherInterface.phpnu[ * * 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\HttpFoundation\Request; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\NoConfigurationException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; /** * RequestMatcherInterface is the interface that all request matcher classes must implement. * * @author Fabien Potencier */ interface RequestMatcherInterface { /** * Tries to match a request with a set of routes. * * If the matcher can not find information, it must throw one of the exceptions documented * below. * * @return array An array of parameters * * @throws NoConfigurationException If no routing configuration could be found * @throws ResourceNotFoundException If no matching resource could be found * @throws MethodNotAllowedException If a matching resource was found but the request method is not allowed */ public function matchRequest(Request $request); } PK[ZZ Matcher/UrlMatcherInterface.phpnu[ * * 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\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\NoConfigurationException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\RequestContextAwareInterface; /** * UrlMatcherInterface is the interface that all URL matcher classes must implement. * * @author Fabien Potencier */ interface UrlMatcherInterface extends RequestContextAwareInterface { /** * Tries to match a URL path with a set of routes. * * If the matcher can not find information, it must throw one of the exceptions documented * below. * * @param string $pathinfo The path info to be parsed (raw format, i.e. not urldecoded) * * @return array An array of parameters * * @throws NoConfigurationException If no routing configuration could be found * @throws ResourceNotFoundException If the resource could not be found * @throws MethodNotAllowedException If the resource was found but the request method is not allowed */ public function match($pathinfo); } PK[ZZ[}"Matcher/RedirectableUrlMatcher.phpnu[ * * 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\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Route; /** * @author Fabien Potencier */ abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface { /** * {@inheritdoc} */ public function match($pathinfo) { try { $parameters = parent::match($pathinfo); } catch (ResourceNotFoundException $e) { if ('/' === substr($pathinfo, -1) || !\in_array($this->context->getMethod(), ['HEAD', 'GET'])) { throw $e; } try { $parameters = parent::match($pathinfo.'/'); return array_replace($parameters, $this->redirect($pathinfo.'/', isset($parameters['_route']) ? $parameters['_route'] : null)); } catch (ResourceNotFoundException $e2) { throw $e; } } return $parameters; } /** * {@inheritdoc} */ protected function handleRouteRequirements($pathinfo, $name, Route $route) { // expression condition if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), ['context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)])) { return [self::REQUIREMENT_MISMATCH, null]; } // check HTTP scheme requirement $scheme = $this->context->getScheme(); $schemes = $route->getSchemes(); if ($schemes && !$route->hasScheme($scheme)) { return [self::ROUTE_MATCH, $this->redirect($pathinfo, $name, current($schemes))]; } return [self::REQUIREMENT_MATCH, null]; } } PK[ZZ6)Matcher/Dumper/StaticPrefixCollection.phpnu[ * * 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 Frank de Jonge * * @internal */ class StaticPrefixCollection { /** * @var string */ private $prefix; /** * @var array[]|StaticPrefixCollection[] */ private $items = []; /** * @var int */ private $matchStart = 0; public function __construct($prefix = '') { $this->prefix = $prefix; } public function getPrefix() { return $this->prefix; } /** * @return mixed[]|StaticPrefixCollection[] */ public function getItems() { return $this->items; } /** * Adds a route to a group. * * @param string $prefix * @param mixed $route */ public function addRoute($prefix, $route) { $prefix = '/' === $prefix ? $prefix : rtrim($prefix, '/'); $this->guardAgainstAddingNotAcceptedRoutes($prefix); if ($this->prefix === $prefix) { // When a prefix is exactly the same as the base we move up the match start position. // This is needed because otherwise routes that come afterwards have higher precedence // than a possible regular expression, which goes against the input order sorting. $this->items[] = [$prefix, $route]; $this->matchStart = \count($this->items); return; } foreach ($this->items as $i => $item) { if ($i < $this->matchStart) { continue; } if ($item instanceof self && $item->accepts($prefix)) { $item->addRoute($prefix, $route); return; } $group = $this->groupWithItem($item, $prefix, $route); if ($group instanceof self) { $this->items[$i] = $group; return; } } // No optimised case was found, in this case we simple add the route for possible // grouping when new routes are added. $this->items[] = [$prefix, $route]; } /** * Tries to combine a route with another route or group. * * @param StaticPrefixCollection|array $item * @param string $prefix * @param mixed $route * * @return StaticPrefixCollection|null */ private function groupWithItem($item, $prefix, $route) { $itemPrefix = $item instanceof self ? $item->prefix : $item[0]; $commonPrefix = $this->detectCommonPrefix($prefix, $itemPrefix); if (!$commonPrefix) { return null; } $child = new self($commonPrefix); if ($item instanceof self) { $child->items = [$item]; } else { $child->addRoute($item[0], $item[1]); } $child->addRoute($prefix, $route); return $child; } /** * Checks whether a prefix can be contained within the group. * * @param string $prefix * * @return bool Whether a prefix could belong in a given group */ private function accepts($prefix) { return '' === $this->prefix || 0 === strpos($prefix, $this->prefix); } /** * Detects whether there's a common prefix relative to the group prefix and returns it. * * @param string $prefix * @param string $anotherPrefix * * @return false|string A common prefix, longer than the base/group prefix, or false when none available */ private function detectCommonPrefix($prefix, $anotherPrefix) { $baseLength = \strlen($this->prefix); $commonLength = $baseLength; $end = min(\strlen($prefix), \strlen($anotherPrefix)); for ($i = $baseLength; $i <= $end; ++$i) { if (substr($prefix, 0, $i) !== substr($anotherPrefix, 0, $i)) { break; } $commonLength = $i; } $commonPrefix = rtrim(substr($prefix, 0, $commonLength), '/'); if (\strlen($commonPrefix) > $baseLength) { return $commonPrefix; } return false; } /** * Optimizes the tree by inlining items from groups with less than 3 items. */ public function optimizeGroups() { $index = -1; while (isset($this->items[++$index])) { $item = $this->items[$index]; if ($item instanceof self) { $item->optimizeGroups(); // When a group contains only two items there's no reason to optimize because at minimum // the amount of prefix check is 2. In this case inline the group. if ($item->shouldBeInlined()) { array_splice($this->items, $index, 1, $item->items); // Lower index to pass through the same index again after optimizing. // The first item of the replacements might be a group needing optimization. --$index; } } } } private function shouldBeInlined() { if (\count($this->items) >= 3) { return false; } foreach ($this->items as $item) { if ($item instanceof self) { return true; } } foreach ($this->items as $item) { if (\is_array($item) && $item[0] === $this->prefix) { return false; } } return true; } /** * Guards against adding incompatible prefixes in a group. * * @param string $prefix * * @throws \LogicException when a prefix does not belong in a group */ private function guardAgainstAddingNotAcceptedRoutes($prefix) { if (!$this->accepts($prefix)) { $message = sprintf('Could not add route with prefix %s to collection with prefix %s', $prefix, $this->prefix); throw new \LogicException($message); } } } PK[ZZ*Matcher/Dumper/CompiledUrlMatcherTrait.phpnu[ * * 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\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\NoConfigurationException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; use Symfony\Component\Routing\RequestContext; /** * @author Nicolas Grekas * * @internal * * @property RequestContext $context */ trait CompiledUrlMatcherTrait { private bool $matchHost = false; private array $staticRoutes = []; private array $regexpList = []; private array $dynamicRoutes = []; /** * @var callable|null */ private $checkCondition; public function match(string $pathinfo): array { $allow = $allowSchemes = []; if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { return $ret; } if ($allow) { throw new MethodNotAllowedException(array_keys($allow)); } if (!$this instanceof RedirectableUrlMatcherInterface) { throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); } if (!\in_array($this->context->getMethod(), ['HEAD', 'GET'], true)) { // no-op } elseif ($allowSchemes) { redirect_scheme: $scheme = $this->context->getScheme(); $this->context->setScheme(key($allowSchemes)); try { if ($ret = $this->doMatch($pathinfo)) { return $this->redirect($pathinfo, $ret['_route'], $this->context->getScheme()) + $ret; } } finally { $this->context->setScheme($scheme); } } elseif ('/' !== $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/') { $pathinfo = $trimmedPathinfo === $pathinfo ? $pathinfo.'/' : $trimmedPathinfo; if ($ret = $this->doMatch($pathinfo, $allow, $allowSchemes)) { return $this->redirect($pathinfo, $ret['_route']) + $ret; } if ($allowSchemes) { goto redirect_scheme; } } throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); } private function doMatch(string $pathinfo, array &$allow = [], array &$allowSchemes = []): array { $allow = $allowSchemes = []; $pathinfo = rawurldecode($pathinfo) ?: '/'; $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/'; $context = $this->context; $requestMethod = $canonicalMethod = $context->getMethod(); if ($this->matchHost) { $host = strtolower($context->getHost()); } if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } $supportsRedirections = 'GET' === $canonicalMethod && $this instanceof RedirectableUrlMatcherInterface; foreach ($this->staticRoutes[$trimmedPathinfo] ?? [] as [$ret, $requiredHost, $requiredMethods, $requiredSchemes, $hasTrailingSlash, , $condition]) { if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ?? $request = $this->request ?: $this->createRequest($pathinfo) : null)) { continue; } if ($requiredHost) { if ('{' !== $requiredHost[0] ? $requiredHost !== $host : !preg_match($requiredHost, $host, $hostMatches)) { continue; } if ('{' === $requiredHost[0] && $hostMatches) { $hostMatches['_route'] = $ret['_route']; $ret = $this->mergeDefaults($hostMatches, $ret); } } if ('/' !== $pathinfo && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) { if ($supportsRedirections && (!$requiredMethods || isset($requiredMethods['GET']))) { return $allow = $allowSchemes = []; } continue; } $hasRequiredScheme = !$requiredSchemes || isset($requiredSchemes[$context->getScheme()]); if ($hasRequiredScheme && $requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { $allow += $requiredMethods; continue; } if (!$hasRequiredScheme) { $allowSchemes += $requiredSchemes; continue; } return $ret; } $matchedPathinfo = $this->matchHost ? $host.'.'.$pathinfo : $pathinfo; foreach ($this->regexpList as $offset => $regex) { while (preg_match($regex, $matchedPathinfo, $matches)) { foreach ($this->dynamicRoutes[$m = (int) $matches['MARK']] as [$ret, $vars, $requiredMethods, $requiredSchemes, $hasTrailingSlash, $hasTrailingVar, $condition]) { 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; } } $hasTrailingVar = $trimmedPathinfo !== $pathinfo && $hasTrailingVar; if ($hasTrailingVar && ($hasTrailingSlash || (null === $n = $matches[\count($vars)] ?? null) || '/' !== ($n[-1] ?? '/')) && preg_match($regex, $this->matchHost ? $host.'.'.$trimmedPathinfo : $trimmedPathinfo, $n) && $m === (int) $n['MARK']) { if ($hasTrailingSlash) { $matches = $n; } else { $hasTrailingVar = false; } } if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) { if ($supportsRedirections && (!$requiredMethods || isset($requiredMethods['GET']))) { return $allow = $allowSchemes = []; } continue; } foreach ($vars as $i => $v) { if (isset($matches[1 + $i])) { $ret[$v] = $matches[1 + $i]; } } if ($requiredSchemes && !isset($requiredSchemes[$context->getScheme()])) { $allowSchemes += $requiredSchemes; continue; } if ($requiredMethods && !isset($requiredMethods[$canonicalMethod]) && !isset($requiredMethods[$requestMethod])) { $allow += $requiredMethods; continue; } return $ret; } $regex = substr_replace($regex, 'F', $m - $offset, 1 + \strlen($m)); $offset += \strlen($m); } } if ('/' === $pathinfo && !$allow && !$allowSchemes) { throw new NoConfigurationException(); } return []; } } PK[ZZ#R Matcher/Dumper/MatcherDumper.phpnu[ * * 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 */ abstract class MatcherDumper implements MatcherDumperInterface { private $routes; public function __construct(RouteCollection $routes) { $this->routes = $routes; } /** * {@inheritdoc} */ public function getRoutes() { return $this->routes; } } PK[ZZtۃJJ+Matcher/Dumper/CompiledUrlMatcherDumper.phpnu[ * * 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 * @author Tobias Schultze * @author Arnaud Le Blanc * @author Nicolas Grekas */ class CompiledUrlMatcherDumper extends MatcherDumper { private $expressionLanguage; private ?\Exception $signalingException = null; /** * @var ExpressionFunctionProviderInterface[] */ private array $expressionLanguageProviders = []; /** * {@inheritdoc} */ public function dump(array $options = []): string { return <<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(); } [$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 = <<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 { [$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(); $staticPrefix = rtrim($compiledRoute->getStaticPrefix(), '/'); $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 [$hostRx, $rx, $prefix]) { if (('' === $prefix || str_starts_with($url, $prefix)) && (preg_match($rx, $url) || preg_match($rx, $url.'/')) && (!$host || !$hostRx || preg_match($hostRx, $host))) { $dynamicRegex[] = [$hostRegex, $regex, $staticPrefix]; $dynamicRoutes->add($name, $route); continue 2; } } $staticRoutes[$url][$name] = [$route, $hasTrailingSlash]; } else { $dynamicRegex[] = [$hostRegex, $regex, $staticPrefix]; $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 => [$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 possible 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 excluded 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 [$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 [$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 str_contains($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; } [$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, string|array|null $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] = (str_contains($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(): ExpressionLanguage { if (!isset($this->expressionLanguage)) { if (!class_exists(ExpressionLanguage::class)) { 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(string $code, int $level = 1): string { return preg_replace('/^./m', str_repeat(' ', $level).'$0', $code); } /** * @internal */ public static function export(mixed $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); } } PK[ZZg)Matcher/Dumper/MatcherDumperInterface.phpnu[ * * 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 */ 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 = []); /** * Gets the routes to dump. * * @return RouteCollection A RouteCollection instance */ public function getRoutes(); } PK[ZZw_SSMatcher/TraceableUrlMatcher.phpnu[ * * 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\HttpFoundation\Request; use Symfony\Component\Routing\Exception\ExceptionInterface; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * TraceableUrlMatcher helps debug path info matching by tracing the match. * * @author Fabien Potencier */ class TraceableUrlMatcher extends UrlMatcher { const ROUTE_DOES_NOT_MATCH = 0; const ROUTE_ALMOST_MATCHES = 1; const ROUTE_MATCHES = 2; protected $traces; public function getTraces($pathinfo) { $this->traces = []; try { $this->match($pathinfo); } catch (ExceptionInterface $e) { } return $this->traces; } public function getTracesForRequest(Request $request) { $this->request = $request; $traces = $this->getTraces($request->getPathInfo()); $this->request = null; return $traces; } protected function matchCollection($pathinfo, RouteCollection $routes) { // HEAD and GET are equivalent as per RFC if ('HEAD' === $method = $this->context->getMethod()) { $method = 'GET'; } $supportsTrailingSlash = '/' !== $pathinfo && '' !== $pathinfo && $this instanceof RedirectableUrlMatcherInterface; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); $staticPrefix = $compiledRoute->getStaticPrefix(); $requiredMethods = $route->getMethods(); // check the static prefix of the URL first. Only use the more expensive preg_match when it matches if ('' === $staticPrefix || 0 === strpos($pathinfo, $staticPrefix)) { // no-op } elseif (!$supportsTrailingSlash || ($requiredMethods && !\in_array('GET', $requiredMethods)) || 'GET' !== $method) { $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route); continue; } elseif ('/' === substr($staticPrefix, -1) && substr($staticPrefix, 0, -1) === $pathinfo) { $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route); return $this->allow = []; } else { $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route); continue; } $regex = $compiledRoute->getRegex(); if ($supportsTrailingSlash && $pos = strpos($regex, '/$')) { $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2); $hasTrailingSlash = true; } else { $hasTrailingSlash = false; } if (!preg_match($regex, $pathinfo, $matches)) { // does it match without any requirements? $r = new Route($route->getPath(), $route->getDefaults(), [], $route->getOptions()); $cr = $r->compile(); if (!preg_match($cr->getRegex(), $pathinfo)) { $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route); continue; } foreach ($route->getRequirements() as $n => $regex) { $r = new Route($route->getPath(), $route->getDefaults(), [$n => $regex], $route->getOptions()); $cr = $r->compile(); if (\in_array($n, $cr->getVariables()) && !preg_match($cr->getRegex(), $pathinfo)) { $this->addTrace(sprintf('Requirement for "%s" does not match (%s)', $n, $regex), self::ROUTE_ALMOST_MATCHES, $name, $route); continue 2; } } continue; } if ($hasTrailingSlash && '/' !== substr($pathinfo, -1)) { if ((!$requiredMethods || \in_array('GET', $requiredMethods)) && 'GET' === $method) { $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route); return $this->allow = []; } $this->addTrace(sprintf('Method "%s" does not match any of the required methods (%s)', $this->context->getMethod(), implode(', ', $requiredMethods)), self::ROUTE_ALMOST_MATCHES, $name, $route); continue; } $hostMatches = []; if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { $this->addTrace(sprintf('Host "%s" does not match the requirement ("%s")', $this->context->getHost(), $route->getHost()), self::ROUTE_ALMOST_MATCHES, $name, $route); continue; } $status = $this->handleRouteRequirements($pathinfo, $name, $route); if (self::REQUIREMENT_MISMATCH === $status[0]) { if ($route->getCondition()) { $this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $route->getCondition()), self::ROUTE_ALMOST_MATCHES, $name, $route); } else { $this->addTrace(sprintf('Scheme "%s" does not match any of the required schemes (%s); the user will be redirected to first required scheme', $this->getContext()->getScheme(), implode(', ', $route->getSchemes())), self::ROUTE_ALMOST_MATCHES, $name, $route); } continue; } // check HTTP method requirement if ($requiredMethods) { if (!\in_array($method, $requiredMethods)) { if (self::REQUIREMENT_MATCH === $status[0]) { $this->allow = array_merge($this->allow, $requiredMethods); } $this->addTrace(sprintf('Method "%s" does not match any of the required methods (%s)', $this->context->getMethod(), implode(', ', $requiredMethods)), self::ROUTE_ALMOST_MATCHES, $name, $route); continue; } } $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route); return $this->getAttributes($route, $name, array_replace($matches, $hostMatches, isset($status[1]) ? $status[1] : [])); } return []; } private function addTrace($log, $level = self::ROUTE_DOES_NOT_MATCH, $name = null, $route = null) { $this->traces[] = [ 'log' => $log, 'name' => $name, 'level' => $level, 'path' => null !== $route ? $route->getPath() : null, ]; } } PK[ZZy "Generator/CompiledUrlGenerator.phpnu[ * * 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 array $compiledRoutes = []; private ?string $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(string $name, array $parameters = [], int $referenceType = self::ABSOLUTE_PATH): string { $locale = $parameters['_locale'] ?? $this->context->getParameter('_locale') ?: $this->defaultLocale; if (null !== $locale) { do { if (($this->compiledRoutes[$name.'.'.$locale][1]['_canonical_route'] ?? null) === $name) { $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)); } [$variables, $defaults, $requirements, $tokens, $hostTokens, $requiredSchemes, $deprecations] = $this->compiledRoutes[$name] + [6 => []]; foreach ($deprecations as $deprecation) { trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } if (isset($defaults['_canonical_route']) && isset($defaults['_locale'])) { if (!\in_array('_locale', $variables, true)) { unset($parameters['_locale']); } elseif (!isset($parameters['_locale'])) { $parameters['_locale'] = $defaults['_locale']; } } return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes); } } PK[ZZiO1l5l5Generator/UrlGenerator.phpnu[ * * 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\InvalidParameterException; use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; /** * UrlGenerator can generate a URL or a path for any route in the RouteCollection * based on the passed parameters. * * @author Fabien Potencier * @author Tobias Schultze */ class UrlGenerator implements UrlGeneratorInterface, ConfigurableRequirementsInterface { protected $routes; protected $context; /** * @var bool|null */ protected $strictRequirements = true; protected $logger; /** * This array defines the characters (besides alphanumeric ones) that will not be percent-encoded in the path segment of the generated URL. * * PHP's rawurlencode() encodes all chars except "a-zA-Z0-9-._~" according to RFC 3986. But we want to allow some chars * to be used in their literal form (reasons below). Other chars inside the path must of course be encoded, e.g. * "?" and "#" (would be interpreted wrongly as query and fragment identifier), * "'" and """ (are used as delimiters in HTML). */ protected $decodedChars = [ // the slash can be used to designate a hierarchical structure and we want allow using it with this meaning // some webservers don't allow the slash in encoded form in the path for security reasons anyway // see http://stackoverflow.com/questions/4069002/http-400-if-2f-part-of-get-url-in-jboss '%2F' => '/', // the following chars are general delimiters in the URI specification but have only special meaning in the authority component // so they can safely be used in the path in unencoded form '%40' => '@', '%3A' => ':', // these chars are only sub-delimiters that have no predefined meaning and can therefore be used literally // so URI producing applications can use these chars to delimit subcomponents in a path segment without being encoded for better readability '%3B' => ';', '%2C' => ',', '%3D' => '=', '%2B' => '+', '%21' => '!', '%2A' => '*', '%7C' => '|', ]; public function __construct(RouteCollection $routes, RequestContext $context, LoggerInterface $logger = null) { $this->routes = $routes; $this->context = $context; $this->logger = $logger; } /** * {@inheritdoc} */ public function setContext(RequestContext $context) { $this->context = $context; } /** * {@inheritdoc} */ public function getContext() { return $this->context; } /** * {@inheritdoc} */ public function setStrictRequirements($enabled) { $this->strictRequirements = null === $enabled ? null : (bool) $enabled; } /** * {@inheritdoc} */ public function isStrictRequirements() { return $this->strictRequirements; } /** * {@inheritdoc} */ public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) { if (null === $route = $this->routes->get($name)) { throw new RouteNotFoundException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $name)); } // the Route has a cache of its own and is not recompiled as long as it does not get modified $compiledRoute = $route->compile(); return $this->doGenerate($compiledRoute->getVariables(), $route->getDefaults(), $route->getRequirements(), $compiledRoute->getTokens(), $parameters, $name, $referenceType, $compiledRoute->getHostTokens(), $route->getSchemes()); } /** * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route * @throws InvalidParameterException When a parameter value for a placeholder is not correct because * it does not match the requirement * * @return string|null */ protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, array $requiredSchemes = []) { $variables = array_flip($variables); $mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters); // all params must be given if ($diff = array_diff_key($variables, $mergedParams)) { throw new MissingMandatoryParametersException(sprintf('Some mandatory parameters are missing ("%s") to generate a URL for route "%s".', implode('", "', array_keys($diff)), $name)); } $url = ''; $optional = true; $message = 'Parameter "{parameter}" for route "{route}" must match "{expected}" ("{given}" given) to generate a corresponding URL.'; foreach ($tokens as $token) { if ('variable' === $token[0]) { if (!$optional || !\array_key_exists($token[3], $defaults) || null !== $mergedParams[$token[3]] && (string) $mergedParams[$token[3]] !== (string) $defaults[$token[3]]) { // check requirement (while ignoring look-around patterns) if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|strictRequirements) { throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]])); } if ($this->logger) { $this->logger->error($message, ['parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]]); } return null; } $url = $token[1].$mergedParams[$token[3]].$url; $optional = false; } } else { // static text $url = $token[1].$url; $optional = false; } } if ('' === $url) { $url = '/'; } // the contexts base URL is already encoded (see Symfony\Component\HttpFoundation\Request) $url = strtr(rawurlencode($url), $this->decodedChars); // the path segments "." and ".." are interpreted as relative reference when resolving a URI; see http://tools.ietf.org/html/rfc3986#section-3.3 // so we need to encode them as they are not used for this purpose here // otherwise we would generate a URI that, when followed by a user agent (e.g. browser), does not match this route $url = strtr($url, ['/../' => '/%2E%2E/', '/./' => '/%2E/']); if ('/..' === substr($url, -3)) { $url = substr($url, 0, -2).'%2E%2E'; } elseif ('/.' === substr($url, -2)) { $url = substr($url, 0, -1).'%2E'; } $schemeAuthority = ''; $host = $this->context->getHost(); $scheme = $this->context->getScheme(); if ($requiredSchemes) { if (!\in_array($scheme, $requiredSchemes, true)) { $referenceType = self::ABSOLUTE_URL; $scheme = current($requiredSchemes); } } if ($hostTokens) { $routeHost = ''; foreach ($hostTokens as $token) { if ('variable' === $token[0]) { // check requirement (while ignoring look-around patterns) if (null !== $this->strictRequirements && !preg_match('#^'.preg_replace('/\(\?(?:=|<=|!|strictRequirements) { throw new InvalidParameterException(strtr($message, ['{parameter}' => $token[3], '{route}' => $name, '{expected}' => $token[2], '{given}' => $mergedParams[$token[3]]])); } if ($this->logger) { $this->logger->error($message, ['parameter' => $token[3], 'route' => $name, 'expected' => $token[2], 'given' => $mergedParams[$token[3]]]); } return null; } $routeHost = $token[1].$mergedParams[$token[3]].$routeHost; } else { $routeHost = $token[1].$routeHost; } } if ($routeHost !== $host) { $host = $routeHost; if (self::ABSOLUTE_URL !== $referenceType) { $referenceType = self::NETWORK_PATH; } } } if (self::ABSOLUTE_URL === $referenceType || self::NETWORK_PATH === $referenceType) { if ('' !== $host || ('' !== $scheme && 'http' !== $scheme && 'https' !== $scheme)) { $port = ''; if ('http' === $scheme && 80 !== $this->context->getHttpPort()) { $port = ':'.$this->context->getHttpPort(); } elseif ('https' === $scheme && 443 !== $this->context->getHttpsPort()) { $port = ':'.$this->context->getHttpsPort(); } $schemeAuthority = self::NETWORK_PATH === $referenceType || '' === $scheme ? '//' : "$scheme://"; $schemeAuthority .= $host.$port; } } if (self::RELATIVE_PATH === $referenceType) { $url = self::getRelativePath($this->context->getPathInfo(), $url); } else { $url = $schemeAuthority.$this->context->getBaseUrl().$url; } // add a query string if needed $extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, function ($a, $b) { return $a == $b ? 0 : 1; }); // extract fragment $fragment = ''; if (isset($defaults['_fragment'])) { $fragment = $defaults['_fragment']; } if (isset($extra['_fragment'])) { $fragment = $extra['_fragment']; unset($extra['_fragment']); } if ($extra && $query = http_build_query($extra, '', '&', \PHP_QUERY_RFC3986)) { // "/" and "?" can be left decoded for better user experience, see // http://tools.ietf.org/html/rfc3986#section-3.4 $url .= '?'.strtr($query, ['%2F' => '/']); } if ('' !== $fragment) { $url .= '#'.strtr(rawurlencode($fragment), ['%2F' => '/', '%3F' => '?']); } return $url; } /** * Returns the target path as relative reference from the base path. * * Only the URIs path component (no schema, host etc.) is relevant and must be given, starting with a slash. * Both paths must be absolute and not contain relative parts. * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives. * Furthermore, they can be used to reduce the link size in documents. * * Example target paths, given a base path of "/a/b/c/d": * - "/a/b/c/d" -> "" * - "/a/b/c/" -> "./" * - "/a/b/" -> "../" * - "/a/b/c/other" -> "other" * - "/a/x/y" -> "../../x/y" * * @param string $basePath The base path * @param string $targetPath The target path * * @return string The relative target path */ public static function getRelativePath($basePath, $targetPath) { if ($basePath === $targetPath) { return ''; } $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath); $targetDirs = explode('/', isset($targetPath[0]) && '/' === $targetPath[0] ? substr($targetPath, 1) : $targetPath); array_pop($sourceDirs); $targetFile = array_pop($targetDirs); foreach ($sourceDirs as $i => $dir) { if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) { unset($sourceDirs[$i], $targetDirs[$i]); } else { break; } } $targetDirs[] = $targetFile; $path = str_repeat('../', \count($sourceDirs)).implode('/', $targetDirs); // A reference to the same base directory or an empty subdirectory must be prefixed with "./". // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used // as the first segment of a relative-path reference, as it would be mistaken for a scheme name // (see http://tools.ietf.org/html/rfc3986#section-4.2). return '' === $path || '/' === $path[0] || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos) ? "./$path" : $path; } } PK[ZZoff#Generator/UrlGeneratorInterface.phpnu[ * * 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 Symfony\Component\Routing\Exception\InvalidParameterException; use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\RequestContextAwareInterface; /** * UrlGeneratorInterface is the interface that all URL generator classes must implement. * * The constants in this interface define the different types of resource references that * are declared in RFC 3986: http://tools.ietf.org/html/rfc3986 * We are using the term "URL" instead of "URI" as this is more common in web applications * and we do not need to distinguish them as the difference is mostly semantical and * less technical. Generating URIs, i.e. representation-independent resource identifiers, * is also possible. * * @author Fabien Potencier * @author Tobias Schultze */ interface UrlGeneratorInterface extends RequestContextAwareInterface { /** * Generates an absolute URL, e.g. "http://example.com/dir/file". */ const ABSOLUTE_URL = 0; /** * Generates an absolute path, e.g. "/dir/file". */ const ABSOLUTE_PATH = 1; /** * Generates a relative path based on the current request path, e.g. "../parent-file". * * @see UrlGenerator::getRelativePath() */ const RELATIVE_PATH = 2; /** * Generates a network path, e.g. "//example.com/dir/file". * Such reference reuses the current scheme but specifies the host. */ const NETWORK_PATH = 3; /** * Generates a URL or path for a specific route based on the given parameters. * * Parameters that reference placeholders in the route pattern will substitute them in the * path or host. Extra params are added as query string to the URL. * * When the passed reference type cannot be generated for the route because it requires a different * host or scheme than the current one, the method will return a more comprehensive reference * that includes the required params. For example, when you call this method with $referenceType = ABSOLUTE_PATH * but the route requires the https scheme whereas the current scheme is http, it will instead return an * ABSOLUTE_URL with the https scheme and the current host. This makes sure the generated URL matches * the route in any case. * * If there is no route with the given name, the generator must throw the RouteNotFoundException. * * The special parameter _fragment will be used as the document fragment suffixed to the final URL. * * @param string $name The name of the route * @param mixed[] $parameters An array of parameters * @param int $referenceType The type of reference to be generated (one of the constants) * * @return string The generated URL * * @throws RouteNotFoundException If the named route doesn't exist * @throws MissingMandatoryParametersException When some parameters are missing that are mandatory for the route * @throws InvalidParameterException When a parameter value for a placeholder is not correct because * it does not match the requirement */ public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH); } PK[ZZΑC  $Generator/Dumper/GeneratorDumper.phpnu[ * * 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\RouteCollection; /** * GeneratorDumper is the base class for all built-in generator dumpers. * * @author Fabien Potencier */ abstract class GeneratorDumper implements GeneratorDumperInterface { private $routes; public function __construct(RouteCollection $routes) { $this->routes = $routes; } /** * {@inheritdoc} */ public function getRoutes() { return $this->routes; } } PK[ZZ-Generator/Dumper/GeneratorDumperInterface.phpnu[ * * 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\RouteCollection; /** * GeneratorDumperInterface is the interface that all generator dumper classes must implement. * * @author Fabien Potencier */ interface GeneratorDumperInterface { /** * Dumps a set of routes to a string representation of executable code * that can then be used to generate a URL of such a route. * * @param array $options An array of options * * @return string Executable code */ public function dump(array $options = []); /** * Gets the routes to dump. * * @return RouteCollection A RouteCollection instance */ public function getRoutes(); } PK[ZZ#/Generator/Dumper/CompiledUrlGeneratorDumper.phpnu[ * * 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\Exception\RouteCircularReferenceException; use Symfony\Component\Routing\Exception\RouteNotFoundException; use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper; /** * CompiledUrlGeneratorDumper creates a PHP array to be used with CompiledUrlGenerator. * * @author Fabien Potencier * @author Tobias Schultze * @author Nicolas Grekas */ 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; } public function getCompiledAliases(): array { $routes = $this->getRoutes(); $compiledAliases = []; foreach ($routes->getAliases() as $name => $alias) { $deprecations = $alias->isDeprecated() ? [$alias->getDeprecation($name)] : []; $currentId = $alias->getId(); $visited = []; while (null !== $alias = $routes->getAlias($currentId) ?? null) { if (false !== $searchKey = array_search($currentId, $visited)) { $visited[] = $currentId; throw new RouteCircularReferenceException($currentId, \array_slice($visited, $searchKey)); } if ($alias->isDeprecated()) { $deprecations[] = $deprecation = $alias->getDeprecation($currentId); trigger_deprecation($deprecation['package'], $deprecation['version'], $deprecation['message']); } $visited[] = $currentId; $currentId = $alias->getId(); } if (null === $target = $routes->get($currentId)) { throw new RouteNotFoundException(sprintf('Target route "%s" for alias "%s" does not exist.', $currentId, $name)); } $compiledTarget = $target->compile(); $compiledAliases[$name] = [ $compiledTarget->getVariables(), $target->getDefaults(), $target->getRequirements(), $compiledTarget->getTokens(), $compiledTarget->getHostTokens(), $target->getSchemes(), $deprecations, ]; } return $compiledAliases; } /** * {@inheritdoc} */ public function dump(array $options = []): string { return <<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)); } foreach ($this->getCompiledAliases() as $alias => $properties) { $routes .= sprintf("\n '%s' => %s,", $alias, CompiledUrlMatcherDumper::export($properties)); } return $routes; } } PK[ZZ!/Generator/ConfigurableRequirementsInterface.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Generator; /** * ConfigurableRequirementsInterface must be implemented by URL generators that * can be configured whether an exception should be generated when the parameters * do not match the requirements. It is also possible to disable the requirements * check for URL generation completely. * * The possible configurations and use-cases: * - setStrictRequirements(true): Throw an exception for mismatching requirements. This * is mostly useful in development environment. * - setStrictRequirements(false): Don't throw an exception but return null as URL for * mismatching requirements and log the problem. Useful when you cannot control all * params because they come from third party libs but don't want to have a 404 in * production environment. It should log the mismatch so one can review it. * - setStrictRequirements(null): Return the URL with the given parameters without * checking the requirements at all. When generating a URL you should either trust * your params or you validated them beforehand because otherwise it would break your * link anyway. So in production environment you should know that params always pass * the requirements. Thus this option allows to disable the check on URL generation for * performance reasons (saving a preg_match for each requirement every time a URL is * generated). * * @author Fabien Potencier * @author Tobias Schultze */ interface ConfigurableRequirementsInterface { /** * Enables or disables the exception on incorrect parameters. * Passing null will deactivate the requirements check completely. * * @param bool|null $enabled */ public function setStrictRequirements($enabled); /** * Returns whether to throw an exception on incorrect parameters. * Null means the requirements check is deactivated completely. * * @return bool|null */ public function isStrictRequirements(); } PK[ZZNI_}1}1 Router.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; use Psr\Log\LoggerInterface; use Symfony\Component\Config\ConfigCacheFactory; use Symfony\Component\Config\ConfigCacheFactoryInterface; 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\ConfigurableRequirementsInterface; use Symfony\Component\Routing\Generator\Dumper\GeneratorDumperInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\Matcher\Dumper\MatcherDumperInterface; use Symfony\Component\Routing\Matcher\RequestMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcherInterface; /** * The Router class is an example of the integration of all pieces of the * routing system for easier use. * * @author Fabien Potencier */ class Router implements RouterInterface, RequestMatcherInterface { /** * @var UrlMatcherInterface|null */ protected $matcher; /** * @var UrlGeneratorInterface|null */ protected $generator; /** * @var RequestContext */ protected $context; /** * @var LoaderInterface */ protected $loader; /** * @var RouteCollection|null */ protected $collection; /** * @var mixed */ protected $resource; /** * @var array */ protected $options = []; /** * @var LoggerInterface|null */ protected $logger; /** * @var ConfigCacheFactoryInterface|null */ private $configCacheFactory; /** * @var ExpressionFunctionProviderInterface[] */ private $expressionLanguageProviders = []; /** * @param LoaderInterface $loader A LoaderInterface instance * @param mixed $resource The main resource to load * @param array $options An array of options * @param RequestContext $context The context * @param LoggerInterface $logger A logger instance */ public function __construct(LoaderInterface $loader, $resource, array $options = [], RequestContext $context = null, LoggerInterface $logger = null) { $this->loader = $loader; $this->resource = $resource; $this->logger = $logger; $this->context = $context ?: new RequestContext(); $this->setOptions($options); } /** * Sets options. * * Available options: * * * cache_dir: The cache directory (or null to disable caching) * * debug: Whether to enable debugging or not (false by default) * * generator_class: The name of a UrlGeneratorInterface implementation * * generator_base_class: The base class for the dumped generator class * * generator_cache_class: The class name for the dumped generator class * * generator_dumper_class: The name of a GeneratorDumperInterface implementation * * matcher_class: The name of a UrlMatcherInterface implementation * * matcher_base_class: The base class for the dumped matcher class * * matcher_dumper_class: The class name for the dumped matcher class * * matcher_cache_class: The name of a MatcherDumperInterface implementation * * resource_type: Type hint for the main resource (optional) * * strict_requirements: Configure strict requirement checking for generators * implementing ConfigurableRequirementsInterface (default is true) * * @param array $options An array of options * * @throws \InvalidArgumentException When unsupported option is provided */ public function setOptions(array $options) { $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', 'resource_type' => null, 'strict_requirements' => true, ]; // check option names and live merge, if errors are encountered Exception will be thrown $invalid = []; foreach ($options as $key => $value) { if (\array_key_exists($key, $this->options)) { $this->options[$key] = $value; } else { $invalid[] = $key; } } if ($invalid) { throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('", "', $invalid))); } } /** * Sets an option. * * @param string $key The key * @param mixed $value The value * * @throws \InvalidArgumentException */ public function setOption($key, $value) { if (!\array_key_exists($key, $this->options)) { throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key)); } $this->options[$key] = $value; } /** * Gets an option value. * * @param string $key The key * * @return mixed The value * * @throws \InvalidArgumentException */ public function getOption($key) { if (!\array_key_exists($key, $this->options)) { throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key)); } return $this->options[$key]; } /** * {@inheritdoc} */ public function getRouteCollection() { if (null === $this->collection) { $this->collection = $this->loader->load($this->resource, $this->options['resource_type']); } return $this->collection; } /** * {@inheritdoc} */ public function setContext(RequestContext $context) { $this->context = $context; if (null !== $this->matcher) { $this->getMatcher()->setContext($context); } if (null !== $this->generator) { $this->getGenerator()->setContext($context); } } /** * {@inheritdoc} */ public function getContext() { return $this->context; } /** * Sets the ConfigCache factory to use. */ public function setConfigCacheFactory(ConfigCacheFactoryInterface $configCacheFactory) { $this->configCacheFactory = $configCacheFactory; } /** * {@inheritdoc} */ public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) { return $this->getGenerator()->generate($name, $parameters, $referenceType); } /** * {@inheritdoc} */ public function match($pathinfo) { return $this->getMatcher()->match($pathinfo); } /** * {@inheritdoc} */ public function matchRequest(Request $request) { $matcher = $this->getMatcher(); if (!$matcher instanceof RequestMatcherInterface) { // fallback to the default UrlMatcherInterface return $matcher->match($request->getPathInfo()); } return $matcher->matchRequest($request); } /** * Gets the UrlMatcher or RequestMatcher instance associated with this Router. * * @return UrlMatcherInterface|RequestMatcherInterface */ public function getMatcher() { if (null !== $this->matcher) { return $this->matcher; } if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) { $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context); if (method_exists($this->matcher, 'addExpressionLanguageProvider')) { foreach ($this->expressionLanguageProviders as $provider) { $this->matcher->addExpressionLanguageProvider($provider); } } return $this->matcher; } $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['matcher_cache_class'].'.php', function (ConfigCacheInterface $cache) { $dumper = $this->getMatcherDumperInstance(); if (method_exists($dumper, 'addExpressionLanguageProvider')) { foreach ($this->expressionLanguageProviders as $provider) { $dumper->addExpressionLanguageProvider($provider); } } $options = [ 'class' => $this->options['matcher_cache_class'], 'base_class' => $this->options['matcher_base_class'], ]; $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); } ); if (!class_exists($this->options['matcher_cache_class'], false)) { require_once $cache->getPath(); } return $this->matcher = new $this->options['matcher_cache_class']($this->context); } /** * Gets the UrlGenerator instance associated with this Router. * * @return UrlGeneratorInterface A UrlGeneratorInterface instance */ public function getGenerator() { if (null !== $this->generator) { return $this->generator; } 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); } else { $cache = $this->getConfigCacheFactory()->cache($this->options['cache_dir'].'/'.$this->options['generator_cache_class'].'.php', function (ConfigCacheInterface $cache) { $dumper = $this->getGeneratorDumperInstance(); $options = [ 'class' => $this->options['generator_cache_class'], 'base_class' => $this->options['generator_base_class'], ]; $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); } ); if (!class_exists($this->options['generator_cache_class'], false)) { require_once $cache->getPath(); } $this->generator = new $this->options['generator_cache_class']($this->context, $this->logger); } if ($this->generator instanceof ConfigurableRequirementsInterface) { $this->generator->setStrictRequirements($this->options['strict_requirements']); } return $this->generator; } public function addExpressionLanguageProvider(ExpressionFunctionProviderInterface $provider) { $this->expressionLanguageProviders[] = $provider; } /** * @return GeneratorDumperInterface */ protected function getGeneratorDumperInstance() { return new $this->options['generator_dumper_class']($this->getRouteCollection()); } /** * @return MatcherDumperInterface */ protected function getMatcherDumperInstance() { return new $this->options['matcher_dumper_class']($this->getRouteCollection()); } /** * Provides the ConfigCache factory implementation, falling back to a * default implementation if necessary. * * @return ConfigCacheFactoryInterface */ private function getConfigCacheFactory() { if (null === $this->configCacheFactory) { $this->configCacheFactory = new ConfigCacheFactory($this->options['debug']); } return $this->configCacheFactory; } } PK~Z BBphpunit.xml.distnu[ ./Tests/ ./ ./Tests ./vendor PK~ZU"" .gitignorenu[vendor/ composer.lock phpunit.xml PK~Zj5Tests/DependencyInjection/RoutingResolverPassTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\DependencyInjection; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\Routing\DependencyInjection\RoutingResolverPass; class RoutingResolverPassTest extends TestCase { public function testProcess() { $container = new ContainerBuilder(); $container->register('routing.resolver', LoaderResolver::class); $container->register('loader1')->addTag('routing.loader'); $container->register('loader2')->addTag('routing.loader'); (new RoutingResolverPass())->process($container); $this->assertEquals( [['addLoader', [new Reference('loader1')]], ['addLoader', [new Reference('loader2')]]], $container->getDefinition('routing.resolver')->getMethodCalls() ); } } PK~ZdOfI4I4Tests/RouteTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Route; class RouteTest extends TestCase { public function testConstructor() { $route = new Route('/{foo}', ['foo' => 'bar'], ['foo' => '\d+'], ['foo' => 'bar'], '{locale}.example.com'); $this->assertEquals('/{foo}', $route->getPath(), '__construct() takes a path as its first argument'); $this->assertEquals(['foo' => 'bar'], $route->getDefaults(), '__construct() takes defaults as its second argument'); $this->assertEquals(['foo' => '\d+'], $route->getRequirements(), '__construct() takes requirements as its third argument'); $this->assertEquals('bar', $route->getOption('foo'), '__construct() takes options as its fourth argument'); $this->assertEquals('{locale}.example.com', $route->getHost(), '__construct() takes a host pattern as its fifth argument'); $route = new Route('/', [], [], [], '', ['Https'], ['POST', 'put'], 'context.getMethod() == "GET"'); $this->assertEquals(['https'], $route->getSchemes(), '__construct() takes schemes as its sixth argument and lowercases it'); $this->assertEquals(['POST', 'PUT'], $route->getMethods(), '__construct() takes methods as its seventh argument and uppercases it'); $this->assertEquals('context.getMethod() == "GET"', $route->getCondition(), '__construct() takes a condition as its eight argument'); $route = new Route('/', [], [], [], '', 'Https', 'Post'); $this->assertEquals(['https'], $route->getSchemes(), '__construct() takes a single scheme as its sixth argument'); $this->assertEquals(['POST'], $route->getMethods(), '__construct() takes a single method as its seventh argument'); } public function testPath() { $route = new Route('/{foo}'); $route->setPath('/{bar}'); $this->assertEquals('/{bar}', $route->getPath(), '->setPath() sets the path'); $route->setPath(''); $this->assertEquals('/', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed'); $route->setPath('bar'); $this->assertEquals('/bar', $route->getPath(), '->setPath() adds a / at the beginning of the path if needed'); $this->assertEquals($route, $route->setPath(''), '->setPath() implements a fluent interface'); $route->setPath('//path'); $this->assertEquals('/path', $route->getPath(), '->setPath() does not allow two slashes "//" at the beginning of the path as it would be confused with a network path when generating the path from the route'); } public function testOptions() { $route = new Route('/{foo}'); $route->setOptions(['foo' => 'bar']); $this->assertEquals(array_merge([ 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler', ], ['foo' => 'bar']), $route->getOptions(), '->setOptions() sets the options'); $this->assertEquals($route, $route->setOptions([]), '->setOptions() implements a fluent interface'); $route->setOptions(['foo' => 'foo']); $route->addOptions(['bar' => 'bar']); $this->assertEquals($route, $route->addOptions([]), '->addOptions() implements a fluent interface'); $this->assertEquals(['foo' => 'foo', 'bar' => 'bar', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'], $route->getOptions(), '->addDefaults() keep previous defaults'); } public function testOption() { $route = new Route('/{foo}'); $this->assertFalse($route->hasOption('foo'), '->hasOption() return false if option is not set'); $this->assertEquals($route, $route->setOption('foo', 'bar'), '->setOption() implements a fluent interface'); $this->assertEquals('bar', $route->getOption('foo'), '->setOption() sets the option'); $this->assertTrue($route->hasOption('foo'), '->hasOption() return true if option is set'); } public function testDefaults() { $route = new Route('/{foo}'); $route->setDefaults(['foo' => 'bar']); $this->assertEquals(['foo' => 'bar'], $route->getDefaults(), '->setDefaults() sets the defaults'); $this->assertEquals($route, $route->setDefaults([]), '->setDefaults() implements a fluent interface'); $route->setDefault('foo', 'bar'); $this->assertEquals('bar', $route->getDefault('foo'), '->setDefault() sets a default value'); $route->setDefault('foo2', 'bar2'); $this->assertEquals('bar2', $route->getDefault('foo2'), '->getDefault() return the default value'); $this->assertNull($route->getDefault('not_defined'), '->getDefault() return null if default value is not set'); $route->setDefault('_controller', $closure = function () { return 'Hello'; }); $this->assertEquals($closure, $route->getDefault('_controller'), '->setDefault() sets a default value'); $route->setDefaults(['foo' => 'foo']); $route->addDefaults(['bar' => 'bar']); $this->assertEquals($route, $route->addDefaults([]), '->addDefaults() implements a fluent interface'); $this->assertEquals(['foo' => 'foo', 'bar' => 'bar'], $route->getDefaults(), '->addDefaults() keep previous defaults'); } public function testRequirements() { $route = new Route('/{foo}'); $route->setRequirements(['foo' => '\d+']); $this->assertEquals(['foo' => '\d+'], $route->getRequirements(), '->setRequirements() sets the requirements'); $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() returns a requirement'); $this->assertNull($route->getRequirement('bar'), '->getRequirement() returns null if a requirement is not defined'); $route->setRequirements(['foo' => '^\d+$']); $this->assertEquals('\d+', $route->getRequirement('foo'), '->getRequirement() removes ^ and $ from the path'); $this->assertEquals($route, $route->setRequirements([]), '->setRequirements() implements a fluent interface'); $route->setRequirements(['foo' => '\d+']); $route->addRequirements(['bar' => '\d+']); $this->assertEquals($route, $route->addRequirements([]), '->addRequirements() implements a fluent interface'); $this->assertEquals(['foo' => '\d+', 'bar' => '\d+'], $route->getRequirements(), '->addRequirement() keep previous requirements'); } public function testRequirement() { $route = new Route('/{foo}'); $this->assertFalse($route->hasRequirement('foo'), '->hasRequirement() return false if requirement is not set'); $route->setRequirement('foo', '^\d+$'); $this->assertEquals('\d+', $route->getRequirement('foo'), '->setRequirement() removes ^ and $ from the path'); $this->assertTrue($route->hasRequirement('foo'), '->hasRequirement() return true if requirement is set'); } /** * @dataProvider getInvalidRequirements */ public function testSetInvalidRequirement($req) { $this->expectException('InvalidArgumentException'); $route = new Route('/{foo}'); $route->setRequirement('foo', $req); } public function getInvalidRequirements() { return [ [''], [[]], ['^$'], ['^'], ['$'], ]; } public function testHost() { $route = new Route('/'); $route->setHost('{locale}.example.net'); $this->assertEquals('{locale}.example.net', $route->getHost(), '->setHost() sets the host pattern'); } public function testScheme() { $route = new Route('/'); $this->assertEquals([], $route->getSchemes(), 'schemes is initialized with []'); $this->assertFalse($route->hasScheme('http')); $route->setSchemes('hTTp'); $this->assertEquals(['http'], $route->getSchemes(), '->setSchemes() accepts a single scheme string and lowercases it'); $this->assertTrue($route->hasScheme('htTp')); $this->assertFalse($route->hasScheme('httpS')); $route->setSchemes(['HttpS', 'hTTp']); $this->assertEquals(['https', 'http'], $route->getSchemes(), '->setSchemes() accepts an array of schemes and lowercases them'); $this->assertTrue($route->hasScheme('htTp')); $this->assertTrue($route->hasScheme('httpS')); } public function testMethod() { $route = new Route('/'); $this->assertEquals([], $route->getMethods(), 'methods is initialized with []'); $route->setMethods('gEt'); $this->assertEquals(['GET'], $route->getMethods(), '->setMethods() accepts a single method string and uppercases it'); $route->setMethods(['gEt', 'PosT']); $this->assertEquals(['GET', 'POST'], $route->getMethods(), '->setMethods() accepts an array of methods and uppercases them'); } public function testCondition() { $route = new Route('/'); $this->assertSame('', $route->getCondition()); $route->setCondition('context.getMethod() == "GET"'); $this->assertSame('context.getMethod() == "GET"', $route->getCondition()); } public function testCompile() { $route = new Route('/{foo}'); $this->assertInstanceOf('Symfony\Component\Routing\CompiledRoute', $compiled = $route->compile(), '->compile() returns a compiled route'); $this->assertSame($compiled, $route->compile(), '->compile() only compiled the route once if unchanged'); $route->setRequirement('foo', '.*'); $this->assertNotSame($compiled, $route->compile(), '->compile() recompiles if the route was modified'); } public function testSerialize() { $route = new Route('/prefix/{foo}', ['foo' => 'default'], ['foo' => '\d+']); $serialized = serialize($route); $unserialized = unserialize($serialized); $this->assertEquals($route, $unserialized); $this->assertNotSame($route, $unserialized); } /** * Tests that the compiled version is also serialized to prevent the overhead * of compiling it again after unserialize. */ public function testSerializeWhenCompiled() { $route = new Route('/prefix/{foo}', ['foo' => 'default'], ['foo' => '\d+']); $route->setHost('{locale}.example.net'); $route->compile(); $serialized = serialize($route); $unserialized = unserialize($serialized); $this->assertEquals($route, $unserialized); $this->assertNotSame($route, $unserialized); } /** * Tests that unserialization does not fail when the compiled Route is of a * class other than CompiledRoute, such as a subclass of it. */ public function testSerializeWhenCompiledWithClass() { $route = new Route('/', [], [], ['compiler_class' => '\Symfony\Component\Routing\Tests\Fixtures\CustomRouteCompiler']); $this->assertInstanceOf('\Symfony\Component\Routing\Tests\Fixtures\CustomCompiledRoute', $route->compile(), '->compile() returned a proper route'); $serialized = serialize($route); try { $unserialized = unserialize($serialized); $this->assertInstanceOf('\Symfony\Component\Routing\Tests\Fixtures\CustomCompiledRoute', $unserialized->compile(), 'the unserialized route compiled successfully'); } catch (\Exception $e) { $this->fail('unserializing a route which uses a custom compiled route class'); } } /** * Tests that the serialized representation of a route in one symfony version * also works in later symfony versions, i.e. the unserialized route is in the * same state as another, semantically equivalent, route. */ public function testSerializedRepresentationKeepsWorking() { $serialized = 'C:31:"Symfony\Component\Routing\Route":936:{a:8:{s:4:"path";s:13:"/prefix/{foo}";s:4:"host";s:20:"{locale}.example.net";s:8:"defaults";a:1:{s:3:"foo";s:7:"default";}s:12:"requirements";a:1:{s:3:"foo";s:3:"\d+";}s:7:"options";a:1:{s:14:"compiler_class";s:39:"Symfony\Component\Routing\RouteCompiler";}s:7:"schemes";a:0:{}s:7:"methods";a:0:{}s:8:"compiled";C:39:"Symfony\Component\Routing\CompiledRoute":571:{a:8:{s:4:"vars";a:2:{i:0;s:6:"locale";i:1;s:3:"foo";}s:11:"path_prefix";s:7:"/prefix";s:10:"path_regex";s:31:"#^/prefix(?:/(?P\d+))?$#sD";s:11:"path_tokens";a:2:{i:0;a:4:{i:0;s:8:"variable";i:1;s:1:"/";i:2;s:3:"\d+";i:3;s:3:"foo";}i:1;a:2:{i:0;s:4:"text";i:1;s:7:"/prefix";}}s:9:"path_vars";a:1:{i:0;s:3:"foo";}s:10:"host_regex";s:40:"#^(?P[^\.]++)\.example\.net$#sDi";s:11:"host_tokens";a:2:{i:0;a:2:{i:0;s:4:"text";i:1;s:12:".example.net";}i:1;a:4:{i:0;s:8:"variable";i:1;s:0:"";i:2;s:7:"[^\.]++";i:3;s:6:"locale";}}s:9:"host_vars";a:1:{i:0;s:6:"locale";}}}}}'; $unserialized = unserialize($serialized); $route = new Route('/prefix/{foo}', ['foo' => 'default'], ['foo' => '\d+']); $route->setHost('{locale}.example.net'); $route->compile(); $this->assertEquals($route, $unserialized); $this->assertNotSame($route, $unserialized); } } PK~ZTests/RequestContextTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RequestContext; class RequestContextTest extends TestCase { public function testConstruct() { $requestContext = new RequestContext( 'foo', 'post', 'foo.bar', 'HTTPS', 8080, 444, '/baz', 'bar=foobar' ); $this->assertEquals('foo', $requestContext->getBaseUrl()); $this->assertEquals('POST', $requestContext->getMethod()); $this->assertEquals('foo.bar', $requestContext->getHost()); $this->assertEquals('https', $requestContext->getScheme()); $this->assertSame(8080, $requestContext->getHttpPort()); $this->assertSame(444, $requestContext->getHttpsPort()); $this->assertEquals('/baz', $requestContext->getPathInfo()); $this->assertEquals('bar=foobar', $requestContext->getQueryString()); } public function testFromRequest() { $request = Request::create('https://test.com:444/foo?bar=baz'); $requestContext = new RequestContext(); $requestContext->setHttpPort(123); $requestContext->fromRequest($request); $this->assertEquals('', $requestContext->getBaseUrl()); $this->assertEquals('GET', $requestContext->getMethod()); $this->assertEquals('test.com', $requestContext->getHost()); $this->assertEquals('https', $requestContext->getScheme()); $this->assertEquals('/foo', $requestContext->getPathInfo()); $this->assertEquals('bar=baz', $requestContext->getQueryString()); $this->assertSame(123, $requestContext->getHttpPort()); $this->assertSame(444, $requestContext->getHttpsPort()); $request = Request::create('http://test.com:8080/foo?bar=baz'); $requestContext = new RequestContext(); $requestContext->setHttpsPort(567); $requestContext->fromRequest($request); $this->assertSame(8080, $requestContext->getHttpPort()); $this->assertSame(567, $requestContext->getHttpsPort()); } public function testGetParameters() { $requestContext = new RequestContext(); $this->assertEquals([], $requestContext->getParameters()); $requestContext->setParameters(['foo' => 'bar']); $this->assertEquals(['foo' => 'bar'], $requestContext->getParameters()); } public function testHasParameter() { $requestContext = new RequestContext(); $requestContext->setParameters(['foo' => 'bar']); $this->assertTrue($requestContext->hasParameter('foo')); $this->assertFalse($requestContext->hasParameter('baz')); } public function testGetParameter() { $requestContext = new RequestContext(); $requestContext->setParameters(['foo' => 'bar']); $this->assertEquals('bar', $requestContext->getParameter('foo')); $this->assertNull($requestContext->getParameter('baz')); } public function testSetParameter() { $requestContext = new RequestContext(); $requestContext->setParameter('foo', 'bar'); $this->assertEquals('bar', $requestContext->getParameter('foo')); } public function testMethod() { $requestContext = new RequestContext(); $requestContext->setMethod('post'); $this->assertSame('POST', $requestContext->getMethod()); } public function testScheme() { $requestContext = new RequestContext(); $requestContext->setScheme('HTTPS'); $this->assertSame('https', $requestContext->getScheme()); } public function testHost() { $requestContext = new RequestContext(); $requestContext->setHost('eXampLe.com'); $this->assertSame('example.com', $requestContext->getHost()); } public function testQueryString() { $requestContext = new RequestContext(); $requestContext->setQueryString(null); $this->assertSame('', $requestContext->getQueryString()); } public function testPort() { $requestContext = new RequestContext(); $requestContext->setHttpPort('123'); $requestContext->setHttpsPort('456'); $this->assertSame(123, $requestContext->getHttpPort()); $this->assertSame(456, $requestContext->getHttpsPort()); } public function testFluentInterface() { $requestContext = new RequestContext(); $this->assertSame($requestContext, $requestContext->setBaseUrl('/app.php')); $this->assertSame($requestContext, $requestContext->setPathInfo('/index')); $this->assertSame($requestContext, $requestContext->setMethod('POST')); $this->assertSame($requestContext, $requestContext->setScheme('https')); $this->assertSame($requestContext, $requestContext->setHost('example.com')); $this->assertSame($requestContext, $requestContext->setQueryString('foo=bar')); $this->assertSame($requestContext, $requestContext->setHttpPort(80)); $this->assertSame($requestContext, $requestContext->setHttpsPort(443)); $this->assertSame($requestContext, $requestContext->setParameters([])); $this->assertSame($requestContext, $requestContext->setParameter('foo', 'bar')); } } PK~Z e6e6Tests/RouteCompilerTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCompiler; class RouteCompilerTest extends TestCase { /** * @dataProvider provideCompileData */ public function testCompile($name, $arguments, $prefix, $regex, $variables, $tokens) { $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route'); $route = $r->newInstanceArgs($arguments); $compiled = $route->compile(); $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)'); $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)'); $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)'); $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)'); } public function provideCompileData() { return [ [ 'Static route', ['/foo'], '/foo', '#^/foo$#sD', [], [ ['text', '/foo'], ], ], [ 'Route with a variable', ['/foo/{bar}'], '/foo', '#^/foo/(?P[^/]++)$#sD', ['bar'], [ ['variable', '/', '[^/]++', 'bar'], ['text', '/foo'], ], ], [ 'Route with a variable that has a default value', ['/foo/{bar}', ['bar' => 'bar']], '/foo', '#^/foo(?:/(?P[^/]++))?$#sD', ['bar'], [ ['variable', '/', '[^/]++', 'bar'], ['text', '/foo'], ], ], [ 'Route with several variables', ['/foo/{bar}/{foobar}'], '/foo', '#^/foo/(?P[^/]++)/(?P[^/]++)$#sD', ['bar', 'foobar'], [ ['variable', '/', '[^/]++', 'foobar'], ['variable', '/', '[^/]++', 'bar'], ['text', '/foo'], ], ], [ 'Route with several variables that have default values', ['/foo/{bar}/{foobar}', ['bar' => 'bar', 'foobar' => '']], '/foo', '#^/foo(?:/(?P[^/]++)(?:/(?P[^/]++))?)?$#sD', ['bar', 'foobar'], [ ['variable', '/', '[^/]++', 'foobar'], ['variable', '/', '[^/]++', 'bar'], ['text', '/foo'], ], ], [ 'Route with several variables but some of them have no default values', ['/foo/{bar}/{foobar}', ['bar' => 'bar']], '/foo', '#^/foo/(?P[^/]++)/(?P[^/]++)$#sD', ['bar', 'foobar'], [ ['variable', '/', '[^/]++', 'foobar'], ['variable', '/', '[^/]++', 'bar'], ['text', '/foo'], ], ], [ 'Route with an optional variable as the first segment', ['/{bar}', ['bar' => 'bar']], '', '#^/(?P[^/]++)?$#sD', ['bar'], [ ['variable', '/', '[^/]++', 'bar'], ], ], [ 'Route with a requirement of 0', ['/{bar}', ['bar' => null], ['bar' => '0']], '', '#^/(?P0)?$#sD', ['bar'], [ ['variable', '/', '0', 'bar'], ], ], [ 'Route with an optional variable as the first segment with requirements', ['/{bar}', ['bar' => 'bar'], ['bar' => '(foo|bar)']], '', '#^/(?P(foo|bar))?$#sD', ['bar'], [ ['variable', '/', '(foo|bar)', 'bar'], ], ], [ 'Route with only optional variables', ['/{foo}/{bar}', ['foo' => 'foo', 'bar' => 'bar']], '', '#^/(?P[^/]++)?(?:/(?P[^/]++))?$#sD', ['foo', 'bar'], [ ['variable', '/', '[^/]++', 'bar'], ['variable', '/', '[^/]++', 'foo'], ], ], [ 'Route with a variable in last position', ['/foo-{bar}'], '/foo-', '#^/foo\-(?P[^/]++)$#sD', ['bar'], [ ['variable', '-', '[^/]++', 'bar'], ['text', '/foo'], ], ], [ 'Route with nested placeholders', ['/{static{var}static}'], '/{static', '#^/\{static(?P[^/]+)static\}$#sD', ['var'], [ ['text', 'static}'], ['variable', '', '[^/]+', 'var'], ['text', '/{static'], ], ], [ 'Route without separator between variables', ['/{w}{x}{y}{z}.{_format}', ['z' => 'default-z', '_format' => 'html'], ['y' => '(y|Y)']], '', '#^/(?P[^/\.]+)(?P[^/\.]+)(?P(y|Y))(?:(?P[^/\.]++)(?:\.(?P<_format>[^/]++))?)?$#sD', ['w', 'x', 'y', 'z', '_format'], [ ['variable', '.', '[^/]++', '_format'], ['variable', '', '[^/\.]++', 'z'], ['variable', '', '(y|Y)', 'y'], ['variable', '', '[^/\.]+', 'x'], ['variable', '/', '[^/\.]+', 'w'], ], ], [ 'Route with a format', ['/foo/{bar}.{_format}'], '/foo', '#^/foo/(?P[^/\.]++)\.(?P<_format>[^/]++)$#sD', ['bar', '_format'], [ ['variable', '.', '[^/]++', '_format'], ['variable', '/', '[^/\.]++', 'bar'], ['text', '/foo'], ], ], [ 'Static non UTF-8 route', ["/fo\xE9"], "/fo\xE9", "#^/fo\xE9$#sD", [], [ ['text', "/fo\xE9"], ], ], [ 'Route with an explicit UTF-8 requirement', ['/{bar}', ['bar' => null], ['bar' => '.'], ['utf8' => true]], '', '#^/(?P.)?$#sDu', ['bar'], [ ['variable', '/', '.', 'bar', true], ], ], ]; } /** * @group legacy * @dataProvider provideCompileImplicitUtf8Data * @expectedDeprecation Using UTF-8 route %s without setting the "utf8" option is deprecated %s. */ public function testCompileImplicitUtf8Data($name, $arguments, $prefix, $regex, $variables, $tokens, $deprecationType) { $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route'); $route = $r->newInstanceArgs($arguments); $compiled = $route->compile(); $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)'); $this->assertEquals($regex, $compiled->getRegex(), $name.' (regex)'); $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)'); $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)'); } public function provideCompileImplicitUtf8Data() { return [ [ 'Static UTF-8 route', ['/foé'], '/foé', '#^/foé$#sDu', [], [ ['text', '/foé'], ], 'patterns', ], [ 'Route with an implicit UTF-8 requirement', ['/{bar}', ['bar' => null], ['bar' => 'é']], '', '#^/(?Pé)?$#sDu', ['bar'], [ ['variable', '/', 'é', 'bar', true], ], 'requirements', ], [ 'Route with a UTF-8 class requirement', ['/{bar}', ['bar' => null], ['bar' => '\pM']], '', '#^/(?P\pM)?$#sDu', ['bar'], [ ['variable', '/', '\pM', 'bar', true], ], 'requirements', ], [ 'Route with a UTF-8 separator', ['/foo/{bar}§{_format}', [], [], ['compiler_class' => Utf8RouteCompiler::class]], '/foo', '#^/foo/(?P[^/§]++)§(?P<_format>[^/]++)$#sDu', ['bar', '_format'], [ ['variable', '§', '[^/]++', '_format', true], ['variable', '/', '[^/§]++', 'bar', true], ['text', '/foo'], ], 'patterns', ], ]; } public function testRouteWithSameVariableTwice() { $this->expectException('LogicException'); $route = new Route('/{name}/{name}'); $route->compile(); } public function testRouteCharsetMismatch() { $this->expectException('LogicException'); $route = new Route("/\xE9/{bar}", [], ['bar' => '.'], ['utf8' => true]); $route->compile(); } public function testRequirementCharsetMismatch() { $this->expectException('LogicException'); $route = new Route('/foo/{bar}', [], ['bar' => "\xE9"], ['utf8' => true]); $route->compile(); } public function testRouteWithFragmentAsPathParameter() { $this->expectException('InvalidArgumentException'); $route = new Route('/{_fragment}'); $route->compile(); } /** * @dataProvider getVariableNamesStartingWithADigit */ public function testRouteWithVariableNameStartingWithADigit($name) { $this->expectException('DomainException'); $route = new Route('/{'.$name.'}'); $route->compile(); } public function getVariableNamesStartingWithADigit() { return [ ['09'], ['123'], ['1e2'], ]; } /** * @dataProvider provideCompileWithHostData */ public function testCompileWithHost($name, $arguments, $prefix, $regex, $variables, $pathVariables, $tokens, $hostRegex, $hostVariables, $hostTokens) { $r = new \ReflectionClass('Symfony\\Component\\Routing\\Route'); $route = $r->newInstanceArgs($arguments); $compiled = $route->compile(); $this->assertEquals($prefix, $compiled->getStaticPrefix(), $name.' (static prefix)'); $this->assertEquals($regex, str_replace(["\n", ' '], '', $compiled->getRegex()), $name.' (regex)'); $this->assertEquals($variables, $compiled->getVariables(), $name.' (variables)'); $this->assertEquals($pathVariables, $compiled->getPathVariables(), $name.' (path variables)'); $this->assertEquals($tokens, $compiled->getTokens(), $name.' (tokens)'); $this->assertEquals($hostRegex, str_replace(["\n", ' '], '', $compiled->getHostRegex()), $name.' (host regex)'); $this->assertEquals($hostVariables, $compiled->getHostVariables(), $name.' (host variables)'); $this->assertEquals($hostTokens, $compiled->getHostTokens(), $name.' (host tokens)'); } public function provideCompileWithHostData() { return [ [ 'Route with host pattern', ['/hello', [], [], [], 'www.example.com'], '/hello', '#^/hello$#sD', [], [], [ ['text', '/hello'], ], '#^www\.example\.com$#sDi', [], [ ['text', 'www.example.com'], ], ], [ 'Route with host pattern and some variables', ['/hello/{name}', [], [], [], 'www.example.{tld}'], '/hello', '#^/hello/(?P[^/]++)$#sD', ['tld', 'name'], ['name'], [ ['variable', '/', '[^/]++', 'name'], ['text', '/hello'], ], '#^www\.example\.(?P[^\.]++)$#sDi', ['tld'], [ ['variable', '.', '[^\.]++', 'tld'], ['text', 'www.example'], ], ], [ 'Route with variable at beginning of host', ['/hello', [], [], [], '{locale}.example.{tld}'], '/hello', '#^/hello$#sD', ['locale', 'tld'], [], [ ['text', '/hello'], ], '#^(?P[^\.]++)\.example\.(?P[^\.]++)$#sDi', ['locale', 'tld'], [ ['variable', '.', '[^\.]++', 'tld'], ['text', '.example'], ['variable', '', '[^\.]++', 'locale'], ], ], [ 'Route with host variables that has a default value', ['/hello', ['locale' => 'a', 'tld' => 'b'], [], [], '{locale}.example.{tld}'], '/hello', '#^/hello$#sD', ['locale', 'tld'], [], [ ['text', '/hello'], ], '#^(?P[^\.]++)\.example\.(?P[^\.]++)$#sDi', ['locale', 'tld'], [ ['variable', '.', '[^\.]++', 'tld'], ['text', '.example'], ['variable', '', '[^\.]++', 'locale'], ], ], ]; } public function testRouteWithTooLongVariableName() { $this->expectException('DomainException'); $route = new Route(sprintf('/{%s}', str_repeat('a', RouteCompiler::VARIABLE_MAXIMUM_LENGTH + 1))); $route->compile(); } } class Utf8RouteCompiler extends RouteCompiler { const SEPARATORS = '/§'; } PK~ZsIS(!Tests/Fixtures/localized/utf8.xmlnu[ PK~Z<\ ?Tests/Fixtures/map_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 foo PK~Z;,FSS,Tests/Fixtures/with_define_path_variable.phpnu[import('validpattern.php'); $collection->addDefaults([ 'foo' => 123, ]); $collection->addRequirements([ 'foo' => '\d+', ]); $collection->addOptions([ 'foo' => 'bar', ]); $collection->setCondition('context.getMethod() == "POST"'); $collection->addPrefix('/prefix'); return $collection; PK~ZTests/Fixtures/foo1.xmlnu[PK~ZTests/Fixtures/bar.xmlnu[PK~Z.ĉ6Tests/Fixtures/OtherAnnotatedClasses/VariadicClass.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses; class VariadicClass { public function routeAction(...$params) { } } PK~Z8Tests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.phpnu[class NoStartTagClass { } PK~Z˳>Tests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures\OtherAnnotatedClasses; trait AnonymousClassInTrait { public function test() { return new class() { public function foo() { } }; } } PK~Zx' 11Tests/Fixtures/withdoctype.xmlnu[ PK~ZT,Tests/Fixtures/AnnotatedClasses/BarClass.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; class BarClass { public function routeAction($arg1, $arg2 = 'defaultValue2', $arg3 = 'defaultValue3') { } } PK~Z|rr,Tests/Fixtures/AnnotatedClasses/BazClass.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; class BazClass { public function __invoke() { } } PK~Za``1Tests/Fixtures/AnnotatedClasses/EncodingClass.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; abstract class AbstractClass { abstract public function abstractRouteAction(); public function routeAction($arg1, $arg2 = 'defaultValue2', $arg3 = 'defaultValue3') { } } PK~Zq,Tests/Fixtures/AnnotatedClasses/FooTrait.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses; class FooClass { } PK~ZϮBBTests/Fixtures/incomplete.ymlnu[blog_show: defaults: { _controller: MyBlogBundle:Blog:show } PK~Zcr  &Tests/Fixtures/map_in_map_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 foo PK~Z}2  Tests/Fixtures/php_dsl.phpnu[collection() ->add('foo', '/foo') ->condition('abc') ->options(['utf8' => true]) ->add('buz', 'zub') ->controller('foo:act'); $routes->import('php_dsl_sub.php') ->prefix('/sub') ->requirements(['id' => '\d+']); $routes->add('ouf', '/ouf') ->schemes(['https']) ->methods(['GET']) ->defaults(['id' => 0]); }; PK~Zsf00Tests/Fixtures/missing_id.xmlnu[ PK~Z&,#Tests/Fixtures/list_null_values.xmlnu[ AcmeBlogBundle:Blog:index PK~Zf,Tests/Fixtures/directory/recurse/routes1.ymlnu[route1: path: /route/1 PK~ZTests/Fixtures/annotated.phpnu[PK~Zj`koo&Tests/Fixtures/CustomXmlFileLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures; use Symfony\Component\Config\Util\XmlUtils; use Symfony\Component\Routing\Loader\XmlFileLoader; /** * XmlFileLoader with schema validation turned off. */ class CustomXmlFileLoader extends XmlFileLoader { protected function loadFile($file) { return XmlUtils::loadFile($file, function () { return true; }); } } PK~Z Tests/Fixtures/file_resource.ymlnu[PK~Z}8Ɯ &Tests/Fixtures/dumper/url_matcher4.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } // just_head if ('/just_head' === $pathinfo) { $ret = ['_route' => 'just_head']; if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_just_head; } return $ret; } not_just_head: // head_and_get if ('/head_and_get' === $pathinfo) { $ret = ['_route' => 'head_and_get']; if (!in_array($canonicalMethod, ['HEAD', 'GET'])) { $allow = array_merge($allow, ['HEAD', 'GET']); goto not_head_and_get; } return $ret; } not_head_and_get: // get_and_head if ('/get_and_head' === $pathinfo) { $ret = ['_route' => 'get_and_head']; if (!in_array($canonicalMethod, ['GET', 'HEAD'])) { $allow = array_merge($allow, ['GET', 'HEAD']); goto not_get_and_head; } return $ret; } not_get_and_head: // post_and_head if ('/post_and_head' === $pathinfo) { $ret = ['_route' => 'post_and_head']; if (!in_array($requestMethod, ['POST', 'HEAD'])) { $allow = array_merge($allow, ['POST', 'HEAD']); goto not_post_and_head; } return $ret; } not_post_and_head: if (0 === strpos($pathinfo, '/put_and_post')) { // put_and_post if ('/put_and_post' === $pathinfo) { $ret = ['_route' => 'put_and_post']; if (!in_array($requestMethod, ['PUT', 'POST'])) { $allow = array_merge($allow, ['PUT', 'POST']); goto not_put_and_post; } return $ret; } not_put_and_post: // put_and_get_and_head if ('/put_and_post' === $pathinfo) { $ret = ['_route' => 'put_and_get_and_head']; if (!in_array($canonicalMethod, ['PUT', 'GET', 'HEAD'])) { $allow = array_merge($allow, ['PUT', 'GET', 'HEAD']); goto not_put_and_get_and_head; } return $ret; } not_put_and_get_and_head: } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~Z!^&Tests/Fixtures/dumper/url_matcher5.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if (0 === strpos($pathinfo, '/a')) { // a_first if ('/a/11' === $pathinfo) { return ['_route' => 'a_first']; } // a_second if ('/a/22' === $pathinfo) { return ['_route' => 'a_second']; } // a_third if ('/a/333' === $pathinfo) { return ['_route' => 'a_third']; } } // a_wildcard if (preg_match('#^/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'a_wildcard']), array ()); } if (0 === strpos($pathinfo, '/a')) { // a_fourth if ('/a/44' === $trimmedPathinfo) { $ret = ['_route' => 'a_fourth']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_a_fourth; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fourth')); } return $ret; } not_a_fourth: // a_fifth if ('/a/55' === $trimmedPathinfo) { $ret = ['_route' => 'a_fifth']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_a_fifth; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_fifth')); } return $ret; } not_a_fifth: // a_sixth if ('/a/66' === $trimmedPathinfo) { $ret = ['_route' => 'a_sixth']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_a_sixth; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'a_sixth')); } return $ret; } not_a_sixth: } // nested_wildcard if (0 === strpos($pathinfo, '/nested') && preg_match('#^/nested/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'nested_wildcard']), array ()); } if (0 === strpos($pathinfo, '/nested/group')) { // nested_a if ('/nested/group/a' === $trimmedPathinfo) { $ret = ['_route' => 'nested_a']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_nested_a; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_a')); } return $ret; } not_nested_a: // nested_b if ('/nested/group/b' === $trimmedPathinfo) { $ret = ['_route' => 'nested_b']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_nested_b; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_b')); } return $ret; } not_nested_b: // nested_c if ('/nested/group/c' === $trimmedPathinfo) { $ret = ['_route' => 'nested_c']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_nested_c; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'nested_c')); } return $ret; } not_nested_c: } elseif (0 === strpos($pathinfo, '/slashed/group')) { // slashed_a if ('/slashed/group' === $trimmedPathinfo) { $ret = ['_route' => 'slashed_a']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_slashed_a; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_a')); } return $ret; } not_slashed_a: // slashed_b if ('/slashed/group/b' === $trimmedPathinfo) { $ret = ['_route' => 'slashed_b']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_slashed_b; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_b')); } return $ret; } not_slashed_b: // slashed_c if ('/slashed/group/c' === $trimmedPathinfo) { $ret = ['_route' => 'slashed_c']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_slashed_c; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'slashed_c')); } return $ret; } not_slashed_c: } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~Zq4$$&Tests/Fixtures/dumper/url_matcher6.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if (0 === strpos($pathinfo, '/trailing/simple')) { // simple_trailing_slash_no_methods if ('/trailing/simple/no-methods/' === $pathinfo) { return ['_route' => 'simple_trailing_slash_no_methods']; } // simple_trailing_slash_GET_method if ('/trailing/simple/get-method/' === $pathinfo) { $ret = ['_route' => 'simple_trailing_slash_GET_method']; if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_simple_trailing_slash_GET_method; } return $ret; } not_simple_trailing_slash_GET_method: // simple_trailing_slash_HEAD_method if ('/trailing/simple/head-method/' === $pathinfo) { $ret = ['_route' => 'simple_trailing_slash_HEAD_method']; if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_simple_trailing_slash_HEAD_method; } return $ret; } not_simple_trailing_slash_HEAD_method: // simple_trailing_slash_POST_method if ('/trailing/simple/post-method/' === $pathinfo) { $ret = ['_route' => 'simple_trailing_slash_POST_method']; if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_simple_trailing_slash_POST_method; } return $ret; } not_simple_trailing_slash_POST_method: } elseif (0 === strpos($pathinfo, '/trailing/regex')) { // regex_trailing_slash_no_methods if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_no_methods']), array ()); } // regex_trailing_slash_GET_method if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_GET_method']), array ()); if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_regex_trailing_slash_GET_method; } return $ret; } not_regex_trailing_slash_GET_method: // regex_trailing_slash_HEAD_method if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_HEAD_method']), array ()); if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_regex_trailing_slash_HEAD_method; } return $ret; } not_regex_trailing_slash_HEAD_method: // regex_trailing_slash_POST_method if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_POST_method']), array ()); if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_regex_trailing_slash_POST_method; } return $ret; } not_regex_trailing_slash_POST_method: } elseif (0 === strpos($pathinfo, '/not-trailing/simple')) { // simple_not_trailing_slash_no_methods if ('/not-trailing/simple/no-methods' === $pathinfo) { return ['_route' => 'simple_not_trailing_slash_no_methods']; } // simple_not_trailing_slash_GET_method if ('/not-trailing/simple/get-method' === $pathinfo) { $ret = ['_route' => 'simple_not_trailing_slash_GET_method']; if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_simple_not_trailing_slash_GET_method; } return $ret; } not_simple_not_trailing_slash_GET_method: // simple_not_trailing_slash_HEAD_method if ('/not-trailing/simple/head-method' === $pathinfo) { $ret = ['_route' => 'simple_not_trailing_slash_HEAD_method']; if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_simple_not_trailing_slash_HEAD_method; } return $ret; } not_simple_not_trailing_slash_HEAD_method: // simple_not_trailing_slash_POST_method if ('/not-trailing/simple/post-method' === $pathinfo) { $ret = ['_route' => 'simple_not_trailing_slash_POST_method']; if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_simple_not_trailing_slash_POST_method; } return $ret; } not_simple_not_trailing_slash_POST_method: } elseif (0 === strpos($pathinfo, '/not-trailing/regex')) { // regex_not_trailing_slash_no_methods if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_no_methods']), array ()); } // regex_not_trailing_slash_GET_method if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_GET_method']), array ()); if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_regex_not_trailing_slash_GET_method; } return $ret; } not_regex_not_trailing_slash_GET_method: // regex_not_trailing_slash_HEAD_method if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_HEAD_method']), array ()); if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_regex_not_trailing_slash_HEAD_method; } return $ret; } not_regex_not_trailing_slash_HEAD_method: // regex_not_trailing_slash_POST_method if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_POST_method']), array ()); if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_regex_not_trailing_slash_POST_method; } return $ret; } not_regex_not_trailing_slash_POST_method: } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~Z J&Tests/Fixtures/dumper/url_matcher3.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if (0 === strpos($pathinfo, '/rootprefix')) { // static if ('/rootprefix/test' === $pathinfo) { return ['_route' => 'static']; } // dynamic if (preg_match('#^/rootprefix/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'dynamic']), array ()); } } // with-condition if ('/with-condition' === $pathinfo && ($context->getMethod() == "GET")) { return ['_route' => 'with-condition']; } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~Z*?44&Tests/Fixtures/dumper/url_matcher2.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if (0 === strpos($pathinfo, '/foo')) { // foo if (preg_match('#^/foo/(?Pbaz|symfony)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo']), array ( 'def' => 'test',)); } // foofoo if ('/foofoo' === $pathinfo) { return array ( 'def' => 'test', '_route' => 'foofoo',); } } elseif (0 === strpos($pathinfo, '/bar')) { // bar if (preg_match('#^/bar/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'bar']), array ()); if (!in_array($canonicalMethod, ['GET', 'HEAD'])) { $allow = array_merge($allow, ['GET', 'HEAD']); goto not_bar; } return $ret; } not_bar: // barhead if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'barhead']), array ()); if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_barhead; } return $ret; } not_barhead: } elseif (0 === strpos($pathinfo, '/test')) { if (0 === strpos($pathinfo, '/test/baz')) { // baz if ('/test/baz' === $pathinfo) { return ['_route' => 'baz']; } // baz2 if ('/test/baz.html' === $pathinfo) { return ['_route' => 'baz2']; } // baz3 if ('/test/baz3' === $trimmedPathinfo) { $ret = ['_route' => 'baz3']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_baz3; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz3')); } return $ret; } not_baz3: } // baz4 if (preg_match('#^/test/(?P[^/]++)/?$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'baz4']), array ()); if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_baz4; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'baz4')); } return $ret; } not_baz4: // baz5 if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'baz5']), array ()); if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_baz5; } return $ret; } not_baz5: // baz.baz6 if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'baz.baz6']), array ()); if (!in_array($requestMethod, ['PUT'])) { $allow = array_merge($allow, ['PUT']); goto not_bazbaz6; } return $ret; } not_bazbaz6: } // quoter if (preg_match('#^/(?P[\']+)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'quoter']), array ()); } // space if ('/spa ce' === $pathinfo) { return ['_route' => 'space']; } if (0 === strpos($pathinfo, '/a')) { if (0 === strpos($pathinfo, '/a/b\'b')) { // foo1 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo1']), array ()); } // bar1 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'bar1']), array ()); } } // overridden if (preg_match('#^/a/(?P.*)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'overridden']), array ()); } if (0 === strpos($pathinfo, '/a/b\'b')) { // foo2 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo2']), array ()); } // bar2 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'bar2']), array ()); } } } elseif (0 === strpos($pathinfo, '/multi')) { // helloWorld if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P[^/]++))?$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'helloWorld']), array ( 'who' => 'World!',)); } // hey if ('/multi/hey' === $trimmedPathinfo) { $ret = ['_route' => 'hey']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_hey; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'hey')); } return $ret; } not_hey: // overridden2 if ('/multi/new' === $pathinfo) { return ['_route' => 'overridden2']; } } // foo3 if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo3']), array ()); } // bar3 if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'bar3']), array ()); } if (0 === strpos($pathinfo, '/aba')) { // ababa if ('/ababa' === $pathinfo) { return ['_route' => 'ababa']; } // foo4 if (preg_match('#^/aba/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo4']), array ()); } } $host = $context->getHost(); if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { // route1 if ('/route1' === $pathinfo) { return ['_route' => 'route1']; } // route2 if ('/c2/route2' === $pathinfo) { return ['_route' => 'route2']; } } if (preg_match('#^b\\.example\\.com$#sDi', $host, $hostMatches)) { // route3 if ('/c2/route3' === $pathinfo) { return ['_route' => 'route3']; } } if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { // route4 if ('/route4' === $pathinfo) { return ['_route' => 'route4']; } } if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { // route5 if ('/route5' === $pathinfo) { return ['_route' => 'route5']; } } // route6 if ('/route6' === $pathinfo) { return ['_route' => 'route6']; } if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#sDi', $host, $hostMatches)) { if (0 === strpos($pathinfo, '/route1')) { // route11 if ('/route11' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, ['_route' => 'route11']), array ()); } // route12 if ('/route12' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, ['_route' => 'route12']), array ( 'var1' => 'val',)); } // route13 if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($hostMatches, $matches, ['_route' => 'route13']), array ()); } // route14 if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($hostMatches, $matches, ['_route' => 'route14']), array ( 'var1' => 'val',)); } } } if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { // route15 if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'route15']), array ()); } } // route16 if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'route16']), array ( 'var1' => 'val',)); } // route17 if ('/route17' === $pathinfo) { return ['_route' => 'route17']; } // a if ('/a/a...' === $pathinfo) { return ['_route' => 'a']; } if (0 === strpos($pathinfo, '/a/b')) { // b if (preg_match('#^/a/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'b']), array ()); } // c if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'c']), array ()); } } // secure if ('/secure' === $pathinfo) { $ret = ['_route' => 'secure']; $requiredSchemes = array ( 'https' => 0,); if (!isset($requiredSchemes[$context->getScheme()])) { if ('GET' !== $canonicalMethod) { goto not_secure; } return array_replace($ret, $this->redirect($rawPathinfo, 'secure', key($requiredSchemes))); } return $ret; } not_secure: // nonsecure if ('/nonsecure' === $pathinfo) { $ret = ['_route' => 'nonsecure']; $requiredSchemes = array ( 'http' => 0,); if (!isset($requiredSchemes[$context->getScheme()])) { if ('GET' !== $canonicalMethod) { goto not_nonsecure; } return array_replace($ret, $this->redirect($rawPathinfo, 'nonsecure', key($requiredSchemes))); } return $ret; } not_nonsecure: if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~ZU+U+&Tests/Fixtures/dumper/url_matcher7.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if (0 === strpos($pathinfo, '/trailing/simple')) { // simple_trailing_slash_no_methods if ('/trailing/simple/no-methods' === $trimmedPathinfo) { $ret = ['_route' => 'simple_trailing_slash_no_methods']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_simple_trailing_slash_no_methods; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_no_methods')); } return $ret; } not_simple_trailing_slash_no_methods: // simple_trailing_slash_GET_method if ('/trailing/simple/get-method' === $trimmedPathinfo) { $ret = ['_route' => 'simple_trailing_slash_GET_method']; if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_simple_trailing_slash_GET_method; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'simple_trailing_slash_GET_method')); } if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_simple_trailing_slash_GET_method; } return $ret; } not_simple_trailing_slash_GET_method: // simple_trailing_slash_HEAD_method if ('/trailing/simple/head-method/' === $pathinfo) { $ret = ['_route' => 'simple_trailing_slash_HEAD_method']; if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_simple_trailing_slash_HEAD_method; } return $ret; } not_simple_trailing_slash_HEAD_method: // simple_trailing_slash_POST_method if ('/trailing/simple/post-method/' === $pathinfo) { $ret = ['_route' => 'simple_trailing_slash_POST_method']; if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_simple_trailing_slash_POST_method; } return $ret; } not_simple_trailing_slash_POST_method: } elseif (0 === strpos($pathinfo, '/trailing/regex')) { // regex_trailing_slash_no_methods if (0 === strpos($pathinfo, '/trailing/regex/no-methods') && preg_match('#^/trailing/regex/no\\-methods/(?P[^/]++)/?$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_no_methods']), array ()); if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_regex_trailing_slash_no_methods; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_no_methods')); } return $ret; } not_regex_trailing_slash_no_methods: // regex_trailing_slash_GET_method if (0 === strpos($pathinfo, '/trailing/regex/get-method') && preg_match('#^/trailing/regex/get\\-method/(?P[^/]++)/?$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_GET_method']), array ()); if ('/' === substr($pathinfo, -1)) { // no-op } elseif ('GET' !== $canonicalMethod) { goto not_regex_trailing_slash_GET_method; } else { return array_replace($ret, $this->redirect($rawPathinfo.'/', 'regex_trailing_slash_GET_method')); } if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_regex_trailing_slash_GET_method; } return $ret; } not_regex_trailing_slash_GET_method: // regex_trailing_slash_HEAD_method if (0 === strpos($pathinfo, '/trailing/regex/head-method') && preg_match('#^/trailing/regex/head\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_HEAD_method']), array ()); if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_regex_trailing_slash_HEAD_method; } return $ret; } not_regex_trailing_slash_HEAD_method: // regex_trailing_slash_POST_method if (0 === strpos($pathinfo, '/trailing/regex/post-method') && preg_match('#^/trailing/regex/post\\-method/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_trailing_slash_POST_method']), array ()); if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_regex_trailing_slash_POST_method; } return $ret; } not_regex_trailing_slash_POST_method: } elseif (0 === strpos($pathinfo, '/not-trailing/simple')) { // simple_not_trailing_slash_no_methods if ('/not-trailing/simple/no-methods' === $pathinfo) { return ['_route' => 'simple_not_trailing_slash_no_methods']; } // simple_not_trailing_slash_GET_method if ('/not-trailing/simple/get-method' === $pathinfo) { $ret = ['_route' => 'simple_not_trailing_slash_GET_method']; if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_simple_not_trailing_slash_GET_method; } return $ret; } not_simple_not_trailing_slash_GET_method: // simple_not_trailing_slash_HEAD_method if ('/not-trailing/simple/head-method' === $pathinfo) { $ret = ['_route' => 'simple_not_trailing_slash_HEAD_method']; if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_simple_not_trailing_slash_HEAD_method; } return $ret; } not_simple_not_trailing_slash_HEAD_method: // simple_not_trailing_slash_POST_method if ('/not-trailing/simple/post-method' === $pathinfo) { $ret = ['_route' => 'simple_not_trailing_slash_POST_method']; if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_simple_not_trailing_slash_POST_method; } return $ret; } not_simple_not_trailing_slash_POST_method: } elseif (0 === strpos($pathinfo, '/not-trailing/regex')) { // regex_not_trailing_slash_no_methods if (0 === strpos($pathinfo, '/not-trailing/regex/no-methods') && preg_match('#^/not\\-trailing/regex/no\\-methods/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_no_methods']), array ()); } // regex_not_trailing_slash_GET_method if (0 === strpos($pathinfo, '/not-trailing/regex/get-method') && preg_match('#^/not\\-trailing/regex/get\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_GET_method']), array ()); if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_regex_not_trailing_slash_GET_method; } return $ret; } not_regex_not_trailing_slash_GET_method: // regex_not_trailing_slash_HEAD_method if (0 === strpos($pathinfo, '/not-trailing/regex/head-method') && preg_match('#^/not\\-trailing/regex/head\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_HEAD_method']), array ()); if (!in_array($requestMethod, ['HEAD'])) { $allow = array_merge($allow, ['HEAD']); goto not_regex_not_trailing_slash_HEAD_method; } return $ret; } not_regex_not_trailing_slash_HEAD_method: // regex_not_trailing_slash_POST_method if (0 === strpos($pathinfo, '/not-trailing/regex/post-method') && preg_match('#^/not\\-trailing/regex/post\\-method/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'regex_not_trailing_slash_POST_method']), array ()); if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_regex_not_trailing_slash_POST_method; } return $ret; } not_regex_not_trailing_slash_POST_method: } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~Z &Tests/Fixtures/dumper/url_matcher0.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~ZP++&Tests/Fixtures/dumper/url_matcher1.phpnu[context = $context; } public function match($rawPathinfo) { $allow = []; $pathinfo = rawurldecode($rawPathinfo); $trimmedPathinfo = rtrim($pathinfo, '/'); $context = $this->context; $request = $this->request ?: $this->createRequest($pathinfo); $requestMethod = $canonicalMethod = $context->getMethod(); if ('HEAD' === $requestMethod) { $canonicalMethod = 'GET'; } if (0 === strpos($pathinfo, '/foo')) { // foo if (preg_match('#^/foo/(?Pbaz|symfony)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo']), array ( 'def' => 'test',)); } // foofoo if ('/foofoo' === $pathinfo) { return array ( 'def' => 'test', '_route' => 'foofoo',); } } elseif (0 === strpos($pathinfo, '/bar')) { // bar if (preg_match('#^/bar/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'bar']), array ()); if (!in_array($canonicalMethod, ['GET', 'HEAD'])) { $allow = array_merge($allow, ['GET', 'HEAD']); goto not_bar; } return $ret; } not_bar: // barhead if (0 === strpos($pathinfo, '/barhead') && preg_match('#^/barhead/(?P[^/]++)$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'barhead']), array ()); if (!in_array($canonicalMethod, ['GET'])) { $allow = array_merge($allow, ['GET']); goto not_barhead; } return $ret; } not_barhead: } elseif (0 === strpos($pathinfo, '/test')) { if (0 === strpos($pathinfo, '/test/baz')) { // baz if ('/test/baz' === $pathinfo) { return ['_route' => 'baz']; } // baz2 if ('/test/baz.html' === $pathinfo) { return ['_route' => 'baz2']; } // baz3 if ('/test/baz3/' === $pathinfo) { return ['_route' => 'baz3']; } } // baz4 if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'baz4']), array ()); } // baz5 if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'baz5']), array ()); if (!in_array($requestMethod, ['POST'])) { $allow = array_merge($allow, ['POST']); goto not_baz5; } return $ret; } not_baz5: // baz.baz6 if (preg_match('#^/test/(?P[^/]++)/$#sD', $pathinfo, $matches)) { $ret = $this->mergeDefaults(array_replace($matches, ['_route' => 'baz.baz6']), array ()); if (!in_array($requestMethod, ['PUT'])) { $allow = array_merge($allow, ['PUT']); goto not_bazbaz6; } return $ret; } not_bazbaz6: } // quoter if (preg_match('#^/(?P[\']+)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'quoter']), array ()); } // space if ('/spa ce' === $pathinfo) { return ['_route' => 'space']; } if (0 === strpos($pathinfo, '/a')) { if (0 === strpos($pathinfo, '/a/b\'b')) { // foo1 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo1']), array ()); } // bar1 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'bar1']), array ()); } } // overridden if (preg_match('#^/a/(?P.*)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'overridden']), array ()); } if (0 === strpos($pathinfo, '/a/b\'b')) { // foo2 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo2']), array ()); } // bar2 if (preg_match('#^/a/b\'b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'bar2']), array ()); } } } elseif (0 === strpos($pathinfo, '/multi')) { // helloWorld if (0 === strpos($pathinfo, '/multi/hello') && preg_match('#^/multi/hello(?:/(?P[^/]++))?$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'helloWorld']), array ( 'who' => 'World!',)); } // hey if ('/multi/hey/' === $pathinfo) { return ['_route' => 'hey']; } // overridden2 if ('/multi/new' === $pathinfo) { return ['_route' => 'overridden2']; } } // foo3 if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo3']), array ()); } // bar3 if (preg_match('#^/(?P<_locale>[^/]++)/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'bar3']), array ()); } if (0 === strpos($pathinfo, '/aba')) { // ababa if ('/ababa' === $pathinfo) { return ['_route' => 'ababa']; } // foo4 if (preg_match('#^/aba/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'foo4']), array ()); } } $host = $context->getHost(); if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { // route1 if ('/route1' === $pathinfo) { return ['_route' => 'route1']; } // route2 if ('/c2/route2' === $pathinfo) { return ['_route' => 'route2']; } } if (preg_match('#^b\\.example\\.com$#sDi', $host, $hostMatches)) { // route3 if ('/c2/route3' === $pathinfo) { return ['_route' => 'route3']; } } if (preg_match('#^a\\.example\\.com$#sDi', $host, $hostMatches)) { // route4 if ('/route4' === $pathinfo) { return ['_route' => 'route4']; } } if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { // route5 if ('/route5' === $pathinfo) { return ['_route' => 'route5']; } } // route6 if ('/route6' === $pathinfo) { return ['_route' => 'route6']; } if (preg_match('#^(?P[^\\.]++)\\.example\\.com$#sDi', $host, $hostMatches)) { if (0 === strpos($pathinfo, '/route1')) { // route11 if ('/route11' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, ['_route' => 'route11']), array ()); } // route12 if ('/route12' === $pathinfo) { return $this->mergeDefaults(array_replace($hostMatches, ['_route' => 'route12']), array ( 'var1' => 'val',)); } // route13 if (0 === strpos($pathinfo, '/route13') && preg_match('#^/route13/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($hostMatches, $matches, ['_route' => 'route13']), array ()); } // route14 if (0 === strpos($pathinfo, '/route14') && preg_match('#^/route14/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($hostMatches, $matches, ['_route' => 'route14']), array ( 'var1' => 'val',)); } } } if (preg_match('#^c\\.example\\.com$#sDi', $host, $hostMatches)) { // route15 if (0 === strpos($pathinfo, '/route15') && preg_match('#^/route15/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'route15']), array ()); } } // route16 if (0 === strpos($pathinfo, '/route16') && preg_match('#^/route16/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'route16']), array ( 'var1' => 'val',)); } // route17 if ('/route17' === $pathinfo) { return ['_route' => 'route17']; } // a if ('/a/a...' === $pathinfo) { return ['_route' => 'a']; } if (0 === strpos($pathinfo, '/a/b')) { // b if (preg_match('#^/a/b/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'b']), array ()); } // c if (0 === strpos($pathinfo, '/a/b/c') && preg_match('#^/a/b/c/(?P[^/]++)$#sD', $pathinfo, $matches)) { return $this->mergeDefaults(array_replace($matches, ['_route' => 'c']), array ()); } } if ('/' === $pathinfo && !$allow) { throw new Symfony\Component\Routing\Exception\NoConfigurationException(); } throw 0 < count($allow) ? new MethodNotAllowedException(array_unique($allow)) : new ResourceNotFoundException(); } } PK~Zj??"Tests/Fixtures/namespaceprefix.xmlnu[ MyBundle:Blog:show \w+ en|fr|de RouteCompiler 1 PK~Zq992Tests/Fixtures/nonesense_type_without_resource.ymlnu[blog_show: path: /blog/{slug} type: custom PK~Z==Tests/Fixtures/nonvalidkeys.ymlnu[someroute: resource: path/to/some.yml name_prefix: test_ PK~ZaTests/Fixtures/validpattern.ymlnu[blog_show: path: /blog/{slug} defaults: { _controller: "MyBundle:Blog:show" } host: "{locale}.example.com" requirements: { 'locale': '\w+' } methods: ['GET','POST','put','OpTiOnS'] schemes: ['https'] condition: 'context.getMethod() == "GET"' options: compiler_class: RouteCompiler blog_show_inherited: path: /blog/{slug} PK~ZUTests/Fixtures/null_values.xmlnu[ foo bar PK~Z9v= Tests/Fixtures/validresource.xmlnu[ 123 \d+ context.getMethod() == "POST" PK~Z'Tests/Fixtures/map_in_list_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 foo PK~ZTests/Fixtures/foo.xmlnu[PK~Z/$>>/Tests/Fixtures/nonesense_resource_plus_path.ymlnu[blog_show: resource: validpattern.yml path: /test PK~ZUAؿTests/Fixtures/validpattern.xmlnu[ MyBundle:Blog:show \w+ context.getMethod() == "GET" PK~ZE(Tests/Fixtures/list_in_list_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 foo PK~ZJ?%Tests/Fixtures/special_route_name.ymlnu["#$péß^a|": path: "true" PK~Z B#'Tests/Fixtures/list_in_map_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 foo PK~Z)"Tests/Fixtures/scalar_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 false 1 0 PK~ZLNN)Tests/Fixtures/RedirectableUrlMatcher.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures; use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcher; /** * @author Fabien Potencier */ class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface { public function redirect($path, $route, $scheme = null) { return [ '_controller' => 'Some controller reference...', 'path' => $path, 'scheme' => $scheme, ]; } } PK~Z$Vzz/Tests/Fixtures/controller/override_defaults.ymlnu[app_blog: path: /blog controller: AppBundle:Homepage:show defaults: _controller: AppBundle:Blog:index PK~Z_y;%Tests/Fixtures/controller/routing.xmlnu[ AppBundle:Blog:list PK~Z:kk/Tests/Fixtures/controller/import_controller.xmlnu[ PK~ZEhh0Tests/Fixtures/controller/import__controller.ymlnu[_static: resource: routing.yml defaults: _controller: FrameworkBundle:Template:template PK~Z@=L6Tests/Fixtures/controller/import_override_defaults.xmlnu[ AppBundle:Blog:index PK~ZP.0Tests/Fixtures/controller/import__controller.xmlnu[ FrameworkBundle:Template:template PK~Z}UU/Tests/Fixtures/controller/import_controller.ymlnu[_static: resource: routing.yml controller: FrameworkBundle:Template:template PK~Z:T%Tests/Fixtures/controller/routing.ymlnu[app_homepage: path: / controller: AppBundle:Homepage:show app_blog: path: /blog defaults: _controller: AppBundle:Blog:list app_logout: path: /logout PK~Z>܎6Tests/Fixtures/controller/import_override_defaults.ymlnu[_static: resource: routing.yml controller: FrameworkBundle:Template:template defaults: _controller: AppBundle:Homepage:show PK~Zy/Tests/Fixtures/controller/override_defaults.xmlnu[ AppBundle:Blog:index PK~ZĮwTests/Fixtures/nonvalid.xmlnu[ MyBundle:Blog:show PK~ZHH"Tests/Fixtures/map_null_values.xmlnu[ AcmeBlogBundle:Blog:index PK~Z%Tests/Fixtures/glob/import_single.ymlnu[_static: resource: b?r.yml PK~Z y#Tests/Fixtures/glob/php_dsl_bar.phpnu[collection(); $collection->add('bar_route', '/bar') ->defaults(['_controller' => 'AppBundle:Bar:view']); return $collection; }; PK~ZkggTests/Fixtures/glob/bar.xmlnu[ PK~Zt@@'Tests/Fixtures/glob/import_multiple.xmlnu[ PK~Z:_PPTests/Fixtures/glob/baz.ymlnu[baz_route: path: /baz defaults: _controller: AppBundle:Baz:view PK~Z)Tests/Fixtures/glob/php_dsl.phpnu[import('php_dsl_ba?.php'); }; PK~Z#Tests/Fixtures/glob/php_dsl_baz.phpnu[collection(); $collection->add('baz_route', '/baz') ->defaults(['_controller' => 'AppBundle:Baz:view']); return $collection; }; PK~Z`6/ggTests/Fixtures/glob/baz.xmlnu[ PK~ZӏD'Tests/Fixtures/glob/import_multiple.ymlnu[_static: resource: ba?.yml PK~ZbX@@%Tests/Fixtures/glob/import_single.xmlnu[ PK~Z'dPPTests/Fixtures/glob/bar.ymlnu[bar_route: path: /bar defaults: _controller: AppBundle:Bar:view PK~Z&Tests/Fixtures/CustomCompiledRoute.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures; use Symfony\Component\Routing\CompiledRoute; class CustomCompiledRoute extends CompiledRoute { } PK~ZPVTests/Fixtures/validpattern.phpnu[add('blog_show', new Route( '/blog/{slug}', ['_controller' => 'MyBlogBundle:Blog:show'], ['locale' => '\w+'], ['compiler_class' => 'RouteCompiler'], '{locale}.example.com', ['https'], ['GET', 'POST', 'put', 'OpTiOnS'], 'context.getMethod() == "GET"' )); return $collection; PK~ZjX Tests/Fixtures/validresource.ymlnu[_blog: resource: validpattern.yml prefix: /{foo} defaults: { 'foo': '123' } requirements: { 'foo': '\d+' } options: { 'foo': 'bar' } host: "" condition: 'context.getMethod() == "POST"' PK~Z00Tests/Fixtures/missing_path.xmlnu[ PK~Zdt WDD&Tests/Fixtures/CustomRouteCompiler.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Fixtures; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCompiler; class CustomRouteCompiler extends RouteCompiler { /** * {@inheritdoc} */ public static function compile(Route $route) { return new CustomCompiledRoute('', '', [], []); } } PK~Z}""Tests/Fixtures/nonvalidnode.xmlnu[ bar PK~Z4" Tests/Fixtures/nonvalidroute.xmlnu[ MyBundle:Blog:show baz PK~Ze2~Tests/Fixtures/nonvalid.ymlnu[foo PK~ZMtTests/Fixtures/nonvalid2.ymlnu[route: string PK~ZZ==*Tests/Fixtures/directory_import/import.ymlnu[_directory: resource: "../directory" type: directory PK~ZtzTests/Fixtures/php_dsl_sub.phpnu[collection('c_') ->prefix('pub'); $add('bar', '/bar'); $add->collection('pub_') ->host('host') ->add('buz', 'buz'); }; PK~Z Ny TTTests/Fixtures/bad_format.ymlnu[blog_show: path: /blog/{slug} defaults: { _controller: "MyBundle:Blog:show" } PK~Z|( Tests/Fixtures/list_defaults.xmlnu[ AcmeBlogBundle:Blog:index true 1 3.5 foo PK~ZTests/Fixtures/empty.ymlnu[PK~ZQ|ӰTests/Annotation/RouteTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Annotation; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Annotation\Route; class RouteTest extends TestCase { public function testInvalidRouteParameter() { $this->expectException('BadMethodCallException'); new Route(['foo' => 'bar']); } /** * @dataProvider getValidParameters */ public function testRouteParameters($parameter, $value, $getter) { $route = new Route([$parameter => $value]); $this->assertEquals($route->$getter(), $value); } public function getValidParameters() { return [ ['value', '/Blog', 'getPath'], ['requirements', ['locale' => 'en'], 'getRequirements'], ['options', ['compiler_class' => 'RouteCompiler'], 'getOptions'], ['name', 'blog_index', 'getName'], ['defaults', ['_controller' => 'MyBlogBundle:Blog:index'], 'getDefaults'], ['schemes', ['https'], 'getSchemes'], ['methods', ['GET', 'POST'], 'getMethods'], ['host', '{locale}.example.com', 'getHost'], ['condition', 'context.getMethod() == "GET"', 'getCondition'], ]; } } PK~Z=v8Tests/RouterTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Router; class RouterTest extends TestCase { private $router = null; private $loader = null; protected function setUp() { $this->loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); $this->router = new Router($this->loader, 'routing.yml'); } public function testSetOptionsWithSupportedOptions() { $this->router->setOptions([ 'cache_dir' => './cache', 'debug' => true, 'resource_type' => 'ResourceType', ]); $this->assertSame('./cache', $this->router->getOption('cache_dir')); $this->assertTrue($this->router->getOption('debug')); $this->assertSame('ResourceType', $this->router->getOption('resource_type')); } public function testSetOptionsWithUnsupportedOptions() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('The Router does not support the following options: "option_foo", "option_bar"'); $this->router->setOptions([ 'cache_dir' => './cache', 'option_foo' => true, 'option_bar' => 'baz', 'resource_type' => 'ResourceType', ]); } public function testSetOptionWithSupportedOption() { $this->router->setOption('cache_dir', './cache'); $this->assertSame('./cache', $this->router->getOption('cache_dir')); } public function testSetOptionWithUnsupportedOption() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('The Router does not support the "option_foo" option'); $this->router->setOption('option_foo', true); } public function testGetOptionWithUnsupportedOption() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('The Router does not support the "option_foo" option'); $this->router->getOption('option_foo', true); } public function testThatRouteCollectionIsLoaded() { $this->router->setOption('resource_type', 'ResourceType'); $routeCollection = new RouteCollection(); $this->loader->expects($this->once()) ->method('load')->with('routing.yml', 'ResourceType') ->willReturn($routeCollection); $this->assertSame($routeCollection, $this->router->getRouteCollection()); } /** * @dataProvider provideMatcherOptionsPreventingCaching */ public function testMatcherIsCreatedIfCacheIsNotConfigured($option) { $this->router->setOption($option, null); $this->loader->expects($this->once()) ->method('load')->with('routing.yml', null) ->willReturn(new RouteCollection()); $this->assertInstanceOf('Symfony\\Component\\Routing\\Matcher\\UrlMatcher', $this->router->getMatcher()); } public function provideMatcherOptionsPreventingCaching() { return [ ['cache_dir'], ['matcher_cache_class'], ]; } /** * @dataProvider provideGeneratorOptionsPreventingCaching */ public function testGeneratorIsCreatedIfCacheIsNotConfigured($option) { $this->router->setOption($option, null); $this->loader->expects($this->once()) ->method('load')->with('routing.yml', null) ->willReturn(new RouteCollection()); $this->assertInstanceOf('Symfony\\Component\\Routing\\Generator\\UrlGenerator', $this->router->getGenerator()); } public function provideGeneratorOptionsPreventingCaching() { return [ ['cache_dir'], ['generator_cache_class'], ]; } public function testMatchRequestWithUrlMatcherInterface() { $matcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\UrlMatcherInterface')->getMock(); $matcher->expects($this->once())->method('match'); $p = new \ReflectionProperty($this->router, 'matcher'); $p->setAccessible(true); $p->setValue($this->router, $matcher); $this->router->matchRequest(Request::create('/')); } public function testMatchRequestWithRequestMatcherInterface() { $matcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock(); $matcher->expects($this->once())->method('matchRequest'); $p = new \ReflectionProperty($this->router, 'matcher'); $p->setAccessible(true); $p->setValue($this->router, $matcher); $this->router->matchRequest(Request::create('/')); } } PK~Z?;, #Tests/Loader/YamlFileLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Loader\YamlFileLoader; class YamlFileLoaderTest extends TestCase { public function testSupports() { $loader = new YamlFileLoader($this->getMockBuilder('Symfony\Component\Config\FileLocator')->getMock()); $this->assertTrue($loader->supports('foo.yml'), '->supports() returns true if the resource is loadable'); $this->assertTrue($loader->supports('foo.yaml'), '->supports() returns true if the resource is loadable'); $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertTrue($loader->supports('foo.yml', 'yaml'), '->supports() checks the resource type if specified'); $this->assertTrue($loader->supports('foo.yaml', 'yaml'), '->supports() checks the resource type if specified'); $this->assertFalse($loader->supports('foo.yml', 'foo'), '->supports() checks the resource type if specified'); } public function testLoadDoesNothingIfEmpty() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $collection = $loader->load('empty.yml'); $this->assertEquals([], $collection->all()); $this->assertEquals([new FileResource(realpath(__DIR__.'/../Fixtures/empty.yml'))], $collection->getResources()); } /** * @dataProvider getPathsToInvalidFiles */ public function testLoadThrowsExceptionWithInvalidFile($filePath) { $this->expectException('InvalidArgumentException'); $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $loader->load($filePath); } public function getPathsToInvalidFiles() { return [ ['nonvalid.yml'], ['nonvalid2.yml'], ['incomplete.yml'], ['nonvalidkeys.yml'], ['nonesense_resource_plus_path.yml'], ['nonesense_type_without_resource.yml'], ['bad_format.yml'], ]; } public function testLoadSpecialRouteName() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('special_route_name.yml'); $route = $routeCollection->get('#$péß^a|'); $this->assertInstanceOf('Symfony\Component\Routing\Route', $route); $this->assertSame('/true', $route->getPath()); } public function testLoadWithRoute() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('validpattern.yml'); $route = $routeCollection->get('blog_show'); $this->assertInstanceOf('Symfony\Component\Routing\Route', $route); $this->assertSame('/blog/{slug}', $route->getPath()); $this->assertSame('{locale}.example.com', $route->getHost()); $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller')); $this->assertSame('\w+', $route->getRequirement('locale')); $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); $this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods()); $this->assertEquals(['https'], $route->getSchemes()); $this->assertEquals('context.getMethod() == "GET"', $route->getCondition()); } public function testLoadWithResource() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('validresource.yml'); $routes = $routeCollection->all(); $this->assertCount(2, $routes, 'Two routes are loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); foreach ($routes as $route) { $this->assertSame('/{foo}/blog/{slug}', $route->getPath()); $this->assertSame('123', $route->getDefault('foo')); $this->assertSame('\d+', $route->getRequirement('foo')); $this->assertSame('bar', $route->getOption('foo')); $this->assertSame('', $route->getHost()); $this->assertSame('context.getMethod() == "POST"', $route->getCondition()); } } public function testLoadRouteWithControllerAttribute() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load('routing.yml'); $route = $routeCollection->get('app_homepage'); $this->assertSame('AppBundle:Homepage:show', $route->getDefault('_controller')); } public function testLoadRouteWithoutControllerAttribute() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load('routing.yml'); $route = $routeCollection->get('app_logout'); $this->assertNull($route->getDefault('_controller')); } public function testLoadRouteWithControllerSetInDefaults() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load('routing.yml'); $route = $routeCollection->get('app_blog'); $this->assertSame('AppBundle:Blog:list', $route->getDefault('_controller')); } public function testOverrideControllerInDefaults() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "app_blog"/'); $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $loader->load('override_defaults.yml'); } /** * @dataProvider provideFilesImportingRoutesWithControllers */ public function testImportRouteWithController($file) { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load($file); $route = $routeCollection->get('app_homepage'); $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); $route = $routeCollection->get('app_blog'); $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); $route = $routeCollection->get('app_logout'); $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); } public function provideFilesImportingRoutesWithControllers() { yield ['import_controller.yml']; yield ['import__controller.yml']; } public function testImportWithOverriddenController() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" key and the defaults key "_controller" for "_static"/'); $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $loader->load('import_override_defaults.yml'); } public function testImportRouteWithGlobMatchingSingleFile() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/glob'])); $routeCollection = $loader->load('import_single.yml'); $route = $routeCollection->get('bar_route'); $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); } public function testImportRouteWithGlobMatchingMultipleFiles() { $loader = new YamlFileLoader(new FileLocator([__DIR__.'/../Fixtures/glob'])); $routeCollection = $loader->load('import_multiple.yml'); $route = $routeCollection->get('bar_route'); $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); $route = $routeCollection->get('baz_route'); $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); } } PK~Z:o3o3*Tests/Loader/AnnotationClassLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use Symfony\Component\Routing\Annotation\Route; class AnnotationClassLoaderTest extends AbstractAnnotationLoaderTest { protected $loader; private $reader; protected function setUp() { parent::setUp(); $this->reader = $this->getReader(); $this->loader = $this->getClassLoader($this->reader); } public function testLoadMissingClass() { $this->expectException('InvalidArgumentException'); $this->loader->load('MissingClass'); } public function testLoadAbstractClass() { $this->expectException('InvalidArgumentException'); $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\AbstractClass'); } /** * @dataProvider provideTestSupportsChecksResource */ public function testSupportsChecksResource($resource, $expectedSupports) { $this->assertSame($expectedSupports, $this->loader->supports($resource), '->supports() returns true if the resource is loadable'); } public function provideTestSupportsChecksResource() { return [ ['class', true], ['\fully\qualified\class\name', true], ['namespaced\class\without\leading\slash', true], ['ÿClassWithLegalSpecialCharacters', true], ['5', false], ['foo.foo', false], [null, false], ]; } public function testSupportsChecksTypeIfSpecified() { $this->assertTrue($this->loader->supports('class', 'annotation'), '->supports() checks the resource type if specified'); $this->assertFalse($this->loader->supports('class', 'foo'), '->supports() checks the resource type if specified'); } public function getLoadTests() { return [ [ 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', ['name' => 'route1', 'path' => '/path'], ['arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'], ], [ 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', ['defaults' => ['arg2' => 'foo'], 'requirements' => ['arg3' => '\w+']], ['arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'], ], [ 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', ['options' => ['foo' => 'bar']], ['arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'], ], [ 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', ['schemes' => ['https'], 'methods' => ['GET']], ['arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'], ], [ 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', ['condition' => 'context.getMethod() == "GET"'], ['arg2' => 'defaultValue2', 'arg3' => 'defaultValue3'], ], ]; } /** * @dataProvider getLoadTests */ public function testLoad($className, $routeData = [], $methodArgs = []) { $routeData = array_replace([ 'name' => 'route', 'path' => '/', 'requirements' => [], 'options' => [], 'defaults' => [], 'schemes' => [], 'methods' => [], 'condition' => '', ], $routeData); $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([$this->getAnnotatedRoute($routeData)]) ; $routeCollection = $this->loader->load($className); $route = $routeCollection->get($routeData['name']); $this->assertSame($routeData['path'], $route->getPath(), '->load preserves path annotation'); $this->assertCount( \count($routeData['requirements']), array_intersect_assoc($routeData['requirements'], $route->getRequirements()), '->load preserves requirements annotation' ); $this->assertCount( \count($routeData['options']), array_intersect_assoc($routeData['options'], $route->getOptions()), '->load preserves options annotation' ); $this->assertCount( \count($routeData['defaults']), $route->getDefaults(), '->load preserves defaults annotation' ); $this->assertEquals($routeData['schemes'], $route->getSchemes(), '->load preserves schemes annotation'); $this->assertEquals($routeData['methods'], $route->getMethods(), '->load preserves methods annotation'); $this->assertSame($routeData['condition'], $route->getCondition(), '->load preserves condition annotation'); } public function testClassRouteLoad() { $classRouteData = [ 'name' => 'prefix_', 'path' => '/prefix', 'schemes' => ['https'], 'methods' => ['GET'], ]; $methodRouteData = [ 'name' => 'route1', 'path' => '/path', 'schemes' => ['http'], 'methods' => ['POST', 'PUT'], ]; $this->reader ->expects($this->once()) ->method('getClassAnnotation') ->willReturn($this->getAnnotatedRoute($classRouteData)) ; $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([$this->getAnnotatedRoute($methodRouteData)]) ; $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass'); $route = $routeCollection->get($classRouteData['name'].$methodRouteData['name']); $this->assertSame($classRouteData['path'].$methodRouteData['path'], $route->getPath(), '->load concatenates class and method route path'); $this->assertEquals(array_merge($classRouteData['schemes'], $methodRouteData['schemes']), $route->getSchemes(), '->load merges class and method route schemes'); $this->assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods'); } public function testInvokableClassRouteLoadWithMethodAnnotation() { $classRouteData = [ 'name' => 'route1', 'path' => '/', 'schemes' => ['https'], 'methods' => ['GET'], ]; $this->reader ->expects($this->exactly(1)) ->method('getClassAnnotations') ->willReturn([$this->getAnnotatedRoute($classRouteData)]) ; $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([]) ; $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); $route = $routeCollection->get($classRouteData['name']); $this->assertSame($classRouteData['path'], $route->getPath(), '->load preserves class route path'); $this->assertEquals($classRouteData['schemes'], $route->getSchemes(), '->load preserves class route schemes'); $this->assertEquals($classRouteData['methods'], $route->getMethods(), '->load preserves class route methods'); } public function testInvokableClassRouteLoadWithClassAnnotation() { $classRouteData = [ 'name' => 'route1', 'path' => '/', 'schemes' => ['https'], 'methods' => ['GET'], ]; $this->reader ->expects($this->exactly(1)) ->method('getClassAnnotation') ->willReturn($this->getAnnotatedRoute($classRouteData)) ; $this->reader ->expects($this->exactly(1)) ->method('getClassAnnotations') ->willReturn([$this->getAnnotatedRoute($classRouteData)]) ; $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([]) ; $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); $route = $routeCollection->get($classRouteData['name']); $this->assertSame($classRouteData['path'], $route->getPath(), '->load preserves class route path'); $this->assertEquals($classRouteData['schemes'], $route->getSchemes(), '->load preserves class route schemes'); $this->assertEquals($classRouteData['methods'], $route->getMethods(), '->load preserves class route methods'); } public function testInvokableClassMultipleRouteLoad() { $classRouteData1 = [ 'name' => 'route1', 'path' => '/1', 'schemes' => ['https'], 'methods' => ['GET'], ]; $classRouteData2 = [ 'name' => 'route2', 'path' => '/2', 'schemes' => ['https'], 'methods' => ['GET'], ]; $this->reader ->expects($this->exactly(1)) ->method('getClassAnnotations') ->willReturn([$this->getAnnotatedRoute($classRouteData1), $this->getAnnotatedRoute($classRouteData2)]) ; $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([]) ; $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); $route = $routeCollection->get($classRouteData1['name']); $this->assertSame($classRouteData1['path'], $route->getPath(), '->load preserves class route path'); $this->assertEquals($classRouteData1['schemes'], $route->getSchemes(), '->load preserves class route schemes'); $this->assertEquals($classRouteData1['methods'], $route->getMethods(), '->load preserves class route methods'); $route = $routeCollection->get($classRouteData2['name']); $this->assertSame($classRouteData2['path'], $route->getPath(), '->load preserves class route path'); $this->assertEquals($classRouteData2['schemes'], $route->getSchemes(), '->load preserves class route schemes'); $this->assertEquals($classRouteData2['methods'], $route->getMethods(), '->load preserves class route methods'); } public function testInvokableClassWithMethodRouteLoad() { $classRouteData = [ 'name' => 'route1', 'path' => '/prefix', 'schemes' => ['https'], 'methods' => ['GET'], ]; $methodRouteData = [ 'name' => 'route2', 'path' => '/path', 'schemes' => ['http'], 'methods' => ['POST', 'PUT'], ]; $this->reader ->expects($this->once()) ->method('getClassAnnotation') ->willReturn($this->getAnnotatedRoute($classRouteData)) ; $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([$this->getAnnotatedRoute($methodRouteData)]) ; $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass'); $route = $routeCollection->get($classRouteData['name']); $this->assertNull($route, '->load ignores class route'); $route = $routeCollection->get($classRouteData['name'].$methodRouteData['name']); $this->assertSame($classRouteData['path'].$methodRouteData['path'], $route->getPath(), '->load concatenates class and method route path'); $this->assertEquals(array_merge($classRouteData['schemes'], $methodRouteData['schemes']), $route->getSchemes(), '->load merges class and method route schemes'); $this->assertEquals(array_merge($classRouteData['methods'], $methodRouteData['methods']), $route->getMethods(), '->load merges class and method route methods'); } /** * @requires function mb_strtolower */ public function testDefaultRouteName() { $methodRouteData = [ 'name' => null, ]; $this->reader ->expects($this->once()) ->method('getMethodAnnotations') ->willReturn([$this->getAnnotatedRoute($methodRouteData)]) ; $routeCollection = $this->loader->load('Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass'); $defaultName = array_keys($routeCollection->all())[0]; $this->assertSame($defaultName, 'symfony_component_routing_tests_fixtures_annotatedclasses_encodingclass_routeàction'); } private function getAnnotatedRoute($data) { return new Route($data); } } PK~ZQqF"Tests/Loader/PhpFileLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Loader\PhpFileLoader; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class PhpFileLoaderTest extends TestCase { public function testSupports() { $loader = new PhpFileLoader($this->getMockBuilder('Symfony\Component\Config\FileLocator')->getMock()); $this->assertTrue($loader->supports('foo.php'), '->supports() returns true if the resource is loadable'); $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertTrue($loader->supports('foo.php', 'php'), '->supports() checks the resource type if specified'); $this->assertFalse($loader->supports('foo.php', 'foo'), '->supports() checks the resource type if specified'); } public function testLoadWithRoute() { $loader = new PhpFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('validpattern.php'); $routes = $routeCollection->all(); $this->assertCount(1, $routes, 'One route is loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); foreach ($routes as $route) { $this->assertSame('/blog/{slug}', $route->getPath()); $this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller')); $this->assertSame('{locale}.example.com', $route->getHost()); $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); $this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods()); $this->assertEquals(['https'], $route->getSchemes()); } } public function testLoadWithImport() { $loader = new PhpFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('validresource.php'); $routes = $routeCollection->all(); $this->assertCount(1, $routes, 'One route is loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); foreach ($routes as $route) { $this->assertSame('/prefix/blog/{slug}', $route->getPath()); $this->assertSame('MyBlogBundle:Blog:show', $route->getDefault('_controller')); $this->assertSame('{locale}.example.com', $route->getHost()); $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); $this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods()); $this->assertEquals(['https'], $route->getSchemes()); } } public function testThatDefiningVariableInConfigFileHasNoSideEffects() { $locator = new FileLocator([__DIR__.'/../Fixtures']); $loader = new PhpFileLoader($locator); $routeCollection = $loader->load('with_define_path_variable.php'); $resources = $routeCollection->getResources(); $this->assertCount(1, $resources); $this->assertContainsOnly('Symfony\Component\Config\Resource\ResourceInterface', $resources); $fileResource = reset($resources); $this->assertSame( realpath($locator->locate('with_define_path_variable.php')), (string) $fileResource ); } public function testRoutingConfigurator() { $locator = new FileLocator([__DIR__.'/../Fixtures']); $loader = new PhpFileLoader($locator); $routeCollection = $loader->load('php_dsl.php'); $expectedCollection = new RouteCollection(); $expectedCollection->add('foo', (new Route('/foo')) ->setOptions(['utf8' => true]) ->setCondition('abc') ); $expectedCollection->add('buz', (new Route('/zub')) ->setDefaults(['_controller' => 'foo:act']) ); $expectedCollection->add('c_bar', (new Route('/sub/pub/bar')) ->setRequirements(['id' => '\d+']) ); $expectedCollection->add('c_pub_buz', (new Route('/sub/pub/buz')) ->setHost('host') ->setRequirements(['id' => '\d+']) ); $expectedCollection->add('ouf', (new Route('/ouf')) ->setSchemes(['https']) ->setMethods(['GET']) ->setDefaults(['id' => 0]) ); $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub.php'))); $expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl.php'))); $this->assertEquals($expectedCollection, $routeCollection); } public function testRoutingConfiguratorCanImportGlobPatterns() { $locator = new FileLocator([__DIR__.'/../Fixtures/glob']); $loader = new PhpFileLoader($locator); $routeCollection = $loader->load('php_dsl.php'); $route = $routeCollection->get('bar_route'); $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); $route = $routeCollection->get('baz_route'); $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); } } PK~Z"Tests/Loader/ClosureLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Loader\ClosureLoader; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class ClosureLoaderTest extends TestCase { public function testSupports() { $loader = new ClosureLoader(); $closure = function () {}; $this->assertTrue($loader->supports($closure), '->supports() returns true if the resource is loadable'); $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertTrue($loader->supports($closure, 'closure'), '->supports() checks the resource type if specified'); $this->assertFalse($loader->supports($closure, 'foo'), '->supports() checks the resource type if specified'); } public function testLoad() { $loader = new ClosureLoader(); $route = new Route('/'); $routes = $loader->load(function () use ($route) { $routes = new RouteCollection(); $routes->add('foo', $route); return $routes; }); $this->assertEquals($route, $routes->get('foo'), '->load() loads a \Closure resource'); } } PK~Z vG G $Tests/Loader/DirectoryLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Loader\LoaderResolver; use Symfony\Component\Routing\Loader\AnnotationFileLoader; use Symfony\Component\Routing\Loader\DirectoryLoader; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\RouteCollection; class DirectoryLoaderTest extends AbstractAnnotationLoaderTest { private $loader; private $reader; protected function setUp() { parent::setUp(); $locator = new FileLocator(); $this->reader = $this->getReader(); $this->loader = new DirectoryLoader($locator); $resolver = new LoaderResolver([ new YamlFileLoader($locator), new AnnotationFileLoader($locator, $this->getClassLoader($this->reader)), $this->loader, ]); $this->loader->setResolver($resolver); } public function testLoadDirectory() { $collection = $this->loader->load(__DIR__.'/../Fixtures/directory', 'directory'); $this->verifyCollection($collection); } public function testImportDirectory() { $collection = $this->loader->load(__DIR__.'/../Fixtures/directory_import', 'directory'); $this->verifyCollection($collection); } private function verifyCollection(RouteCollection $collection) { $routes = $collection->all(); $this->assertCount(3, $routes, 'Three routes are loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); for ($i = 1; $i <= 3; ++$i) { $this->assertSame('/route/'.$i, $routes['route'.$i]->getPath()); } } public function testSupports() { $fixturesDir = __DIR__.'/../Fixtures'; $this->assertFalse($this->loader->supports($fixturesDir), '->supports(*) returns false'); $this->assertTrue($this->loader->supports($fixturesDir, 'directory'), '->supports(*, "directory") returns true'); $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports(*, "foo") returns false'); } } PK~Z4 .Tests/Loader/AnnotationDirectoryLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\AnnotationDirectoryLoader; class AnnotationDirectoryLoaderTest extends AbstractAnnotationLoaderTest { protected $loader; protected $reader; protected function setUp() { parent::setUp(); $this->reader = $this->getReader(); $this->loader = new AnnotationDirectoryLoader(new FileLocator(), $this->getClassLoader($this->reader)); } public function testLoad() { $this->reader->expects($this->exactly(4))->method('getClassAnnotation'); $this->reader ->expects($this->any()) ->method('getMethodAnnotations') ->willReturn([]) ; $this->reader ->expects($this->any()) ->method('getClassAnnotations') ->willReturn([]) ; $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses'); } public function testLoadIgnoresHiddenDirectories() { $this->expectAnnotationsToBeReadFrom([ 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BarClass', 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\BazClass', 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\FooClass', 'Symfony\Component\Routing\Tests\Fixtures\AnnotatedClasses\EncodingClass', ]); $this->reader ->expects($this->any()) ->method('getMethodAnnotations') ->willReturn([]) ; $this->reader ->expects($this->any()) ->method('getClassAnnotations') ->willReturn([]) ; $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses'); } public function testSupports() { $fixturesDir = __DIR__.'/../Fixtures'; $this->assertTrue($this->loader->supports($fixturesDir), '->supports() returns true if the resource is loadable'); $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertTrue($this->loader->supports($fixturesDir, 'annotation'), '->supports() checks the resource type if specified'); $this->assertFalse($this->loader->supports($fixturesDir, 'foo'), '->supports() checks the resource type if specified'); } public function testItSupportsAnyAnnotation() { $this->assertTrue($this->loader->supports(__DIR__.'/../Fixtures/even-with-not-existing-folder', 'annotation')); } public function testLoadFileIfLocatedResourceIsFile() { $this->reader->expects($this->exactly(1))->method('getClassAnnotation'); $this->reader ->expects($this->any()) ->method('getMethodAnnotations') ->willReturn([]) ; $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); } private function expectAnnotationsToBeReadFrom(array $classes) { $this->reader->expects($this->exactly(\count($classes))) ->method('getClassAnnotation') ->with($this->callback(function (\ReflectionClass $class) use ($classes) { return \in_array($class->getName(), $classes); })); } } PK~Zƥ#Tests/Loader/GlobFileLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\GlobResource; use Symfony\Component\Routing\Loader\GlobFileLoader; use Symfony\Component\Routing\RouteCollection; class GlobFileLoaderTest extends TestCase { public function testSupports() { $loader = new GlobFileLoader(new FileLocator()); $this->assertTrue($loader->supports('any-path', 'glob'), '->supports() returns true if the resource has the glob type'); $this->assertFalse($loader->supports('any-path'), '->supports() returns false if the resource is not of glob type'); } public function testLoadAddsTheGlobResourceToTheContainer() { $loader = new GlobFileLoaderWithoutImport(new FileLocator()); $collection = $loader->load(__DIR__.'/../Fixtures/directory/*.yml'); $this->assertEquals(new GlobResource(__DIR__.'/../Fixtures/directory', '/*.yml', false), $collection->getResources()[0]); } } class GlobFileLoaderWithoutImport extends GlobFileLoader { public function import($resource, $type = null, $ignoreErrors = false, $sourceResource = null) { return new RouteCollection(); } } PK~Zx==-Tests/Loader/AbstractAnnotationLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; abstract class AbstractAnnotationLoaderTest extends TestCase { public function getReader() { return $this->getMockBuilder('Doctrine\Common\Annotations\Reader') ->disableOriginalConstructor() ->getMock() ; } public function getClassLoader($reader) { return $this->getMockBuilder('Symfony\Component\Routing\Loader\AnnotationClassLoader') ->setConstructorArgs([$reader]) ->getMockForAbstractClass() ; } } PK~Z=͎۬ )Tests/Loader/AnnotationFileLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Annotation\Route; use Symfony\Component\Routing\Loader\AnnotationFileLoader; class AnnotationFileLoaderTest extends AbstractAnnotationLoaderTest { protected $loader; protected $reader; protected function setUp() { parent::setUp(); $this->reader = $this->getReader(); $this->loader = new AnnotationFileLoader(new FileLocator(), $this->getClassLoader($this->reader)); } public function testLoad() { $this->reader->expects($this->once())->method('getClassAnnotation'); $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooClass.php'); } public function testLoadTraitWithClassConstant() { $this->reader->expects($this->never())->method('getClassAnnotation'); $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/FooTrait.php'); } public function testLoadFileWithoutStartTag() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('Did you forgot to add the "loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/NoStartTagClass.php'); } /** * @requires PHP 5.6 */ public function testLoadVariadic() { $route = new Route(['path' => '/path/to/{id}']); $this->reader->expects($this->once())->method('getClassAnnotation'); $this->reader->expects($this->once())->method('getMethodAnnotations') ->willReturn([$route]); $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/VariadicClass.php'); } /** * @requires PHP 7.0 */ public function testLoadAnonymousClass() { $this->reader->expects($this->never())->method('getClassAnnotation'); $this->reader->expects($this->never())->method('getMethodAnnotations'); $this->loader->load(__DIR__.'/../Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.php'); } public function testLoadAbstractClass() { $this->reader->expects($this->never())->method('getClassAnnotation'); $this->reader->expects($this->never())->method('getMethodAnnotations'); $this->loader->load(__DIR__.'/../Fixtures/AnnotatedClasses/AbstractClass.php'); } public function testSupports() { $fixture = __DIR__.'/../Fixtures/annotated.php'; $this->assertTrue($this->loader->supports($fixture), '->supports() returns true if the resource is loadable'); $this->assertFalse($this->loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertTrue($this->loader->supports($fixture, 'annotation'), '->supports() checks the resource type if specified'); $this->assertFalse($this->loader->supports($fixture, 'foo'), '->supports() checks the resource type if specified'); } } PK~Z"t t &Tests/Loader/ObjectRouteLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Loader\ObjectRouteLoader; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class ObjectRouteLoaderTest extends TestCase { public function testLoadCallsServiceAndReturnsCollection() { $loader = new ObjectRouteLoaderForTest(); // create a basic collection that will be returned $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $loader->loaderMap = [ 'my_route_provider_service' => new RouteService($collection), ]; $actualRoutes = $loader->load( 'my_route_provider_service:loadRoutes', 'service' ); $this->assertSame($collection, $actualRoutes); // the service file should be listed as a resource $this->assertNotEmpty($actualRoutes->getResources()); } /** * @dataProvider getBadResourceStrings */ public function testExceptionWithoutSyntax($resourceString) { $this->expectException('InvalidArgumentException'); $loader = new ObjectRouteLoaderForTest(); $loader->load($resourceString); } public function getBadResourceStrings() { return [ ['Foo'], ['Bar::baz'], ['Foo:Bar:baz'], ]; } public function testExceptionOnNoObjectReturned() { $this->expectException('LogicException'); $loader = new ObjectRouteLoaderForTest(); $loader->loaderMap = ['my_service' => 'NOT_AN_OBJECT']; $loader->load('my_service:method'); } public function testExceptionOnBadMethod() { $this->expectException('BadMethodCallException'); $loader = new ObjectRouteLoaderForTest(); $loader->loaderMap = ['my_service' => new \stdClass()]; $loader->load('my_service:method'); } public function testExceptionOnMethodNotReturningCollection() { $this->expectException('LogicException'); $service = $this->getMockBuilder('stdClass') ->setMethods(['loadRoutes']) ->getMock(); $service->expects($this->once()) ->method('loadRoutes') ->willReturn('NOT_A_COLLECTION'); $loader = new ObjectRouteLoaderForTest(); $loader->loaderMap = ['my_service' => $service]; $loader->load('my_service:loadRoutes'); } } class ObjectRouteLoaderForTest extends ObjectRouteLoader { public $loaderMap = []; protected function getServiceObject($id) { return isset($this->loaderMap[$id]) ? $this->loaderMap[$id] : null; } } class RouteService { private $collection; public function __construct($collection) { $this->collection = $collection; } public function loadRoutes() { return $this->collection; } } PK~Z3=ir:r:"Tests/Loader/XmlFileLoaderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests\Loader; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Loader\XmlFileLoader; use Symfony\Component\Routing\Tests\Fixtures\CustomXmlFileLoader; class XmlFileLoaderTest extends TestCase { public function testSupports() { $loader = new XmlFileLoader($this->getMockBuilder('Symfony\Component\Config\FileLocator')->getMock()); $this->assertTrue($loader->supports('foo.xml'), '->supports() returns true if the resource is loadable'); $this->assertFalse($loader->supports('foo.foo'), '->supports() returns true if the resource is loadable'); $this->assertTrue($loader->supports('foo.xml', 'xml'), '->supports() checks the resource type if specified'); $this->assertFalse($loader->supports('foo.xml', 'foo'), '->supports() checks the resource type if specified'); } public function testLoadWithRoute() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('validpattern.xml'); $route = $routeCollection->get('blog_show'); $this->assertInstanceOf('Symfony\Component\Routing\Route', $route); $this->assertSame('/blog/{slug}', $route->getPath()); $this->assertSame('{locale}.example.com', $route->getHost()); $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller')); $this->assertSame('\w+', $route->getRequirement('locale')); $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); $this->assertEquals(['GET', 'POST', 'PUT', 'OPTIONS'], $route->getMethods()); $this->assertEquals(['https'], $route->getSchemes()); $this->assertEquals('context.getMethod() == "GET"', $route->getCondition()); } public function testLoadWithNamespacePrefix() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('namespaceprefix.xml'); $this->assertCount(1, $routeCollection->all(), 'One route is loaded'); $route = $routeCollection->get('blog_show'); $this->assertSame('/blog/{slug}', $route->getPath()); $this->assertSame('{_locale}.example.com', $route->getHost()); $this->assertSame('MyBundle:Blog:show', $route->getDefault('_controller')); $this->assertSame('\w+', $route->getRequirement('slug')); $this->assertSame('en|fr|de', $route->getRequirement('_locale')); $this->assertNull($route->getDefault('slug')); $this->assertSame('RouteCompiler', $route->getOption('compiler_class')); $this->assertSame(1, $route->getDefault('page')); } public function testLoadWithImport() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('validresource.xml'); $routes = $routeCollection->all(); $this->assertCount(2, $routes, 'Two routes are loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); foreach ($routes as $route) { $this->assertSame('/{foo}/blog/{slug}', $route->getPath()); $this->assertSame('123', $route->getDefault('foo')); $this->assertSame('\d+', $route->getRequirement('foo')); $this->assertSame('bar', $route->getOption('foo')); $this->assertSame('', $route->getHost()); $this->assertSame('context.getMethod() == "POST"', $route->getCondition()); } } public function testUtf8Route() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/localized'])); $routeCollection = $loader->load('utf8.xml'); $routes = $routeCollection->all(); $this->assertCount(2, $routes, 'Two routes are loaded'); $this->assertContainsOnly('Symfony\Component\Routing\Route', $routes); $utf8Route = $routeCollection->get('app_utf8'); $this->assertSame('/utf8', $utf8Route->getPath()); $this->assertTrue($utf8Route->getOption('utf8'), 'Must be utf8'); $noUtf8Route = $routeCollection->get('app_no_utf8'); $this->assertSame('/no-utf8', $noUtf8Route->getPath()); $this->assertFalse($noUtf8Route->getOption('utf8'), 'Must not be utf8'); } /** * @dataProvider getPathsToInvalidFiles */ public function testLoadThrowsExceptionWithInvalidFile($filePath) { $this->expectException('InvalidArgumentException'); $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $loader->load($filePath); } /** * @dataProvider getPathsToInvalidFiles */ public function testLoadThrowsExceptionWithInvalidFileEvenWithoutSchemaValidation($filePath) { $this->expectException('InvalidArgumentException'); $loader = new CustomXmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $loader->load($filePath); } public function getPathsToInvalidFiles() { return [['nonvalidnode.xml'], ['nonvalidroute.xml'], ['nonvalid.xml'], ['missing_id.xml'], ['missing_path.xml']]; } public function testDocTypeIsNotAllowed() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessage('Document types are not allowed.'); $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $loader->load('withdoctype.xml'); } public function testNullValues() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('null_values.xml'); $route = $routeCollection->get('blog_show'); $this->assertTrue($route->hasDefault('foo')); $this->assertNull($route->getDefault('foo')); $this->assertTrue($route->hasDefault('bar')); $this->assertNull($route->getDefault('bar')); $this->assertEquals('foo', $route->getDefault('foobar')); $this->assertEquals('bar', $route->getDefault('baz')); } public function testScalarDataTypeDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('scalar_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'slug' => null, 'published' => true, 'page' => 1, 'price' => 3.5, 'archived' => false, 'free' => true, 'locked' => false, 'foo' => null, 'bar' => null, ], $route->getDefaults() ); } public function testListDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('list_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'values' => [true, 1, 3.5, 'foo'], ], $route->getDefaults() ); } public function testListInListDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('list_in_list_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'values' => [[true, 1, 3.5, 'foo']], ], $route->getDefaults() ); } public function testListInMapDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('list_in_map_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'values' => ['list' => [true, 1, 3.5, 'foo']], ], $route->getDefaults() ); } public function testMapDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('map_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'values' => [ 'public' => true, 'page' => 1, 'price' => 3.5, 'title' => 'foo', ], ], $route->getDefaults() ); } public function testMapInListDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('map_in_list_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'values' => [[ 'public' => true, 'page' => 1, 'price' => 3.5, 'title' => 'foo', ]], ], $route->getDefaults() ); } public function testMapInMapDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('map_in_map_defaults.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ '_controller' => 'AcmeBlogBundle:Blog:index', 'values' => ['map' => [ 'public' => true, 'page' => 1, 'price' => 3.5, 'title' => 'foo', ]], ], $route->getDefaults() ); } public function testNullValuesInList() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('list_null_values.xml'); $route = $routeCollection->get('blog'); $this->assertSame([null, null, null, null, null, null], $route->getDefault('list')); } public function testNullValuesInMap() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures'])); $routeCollection = $loader->load('map_null_values.xml'); $route = $routeCollection->get('blog'); $this->assertSame( [ 'boolean' => null, 'integer' => null, 'float' => null, 'string' => null, 'list' => null, 'map' => null, ], $route->getDefault('map') ); } public function testLoadRouteWithControllerAttribute() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load('routing.xml'); $route = $routeCollection->get('app_homepage'); $this->assertSame('AppBundle:Homepage:show', $route->getDefault('_controller')); } public function testLoadRouteWithoutControllerAttribute() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load('routing.xml'); $route = $routeCollection->get('app_logout'); $this->assertNull($route->getDefault('_controller')); } public function testLoadRouteWithControllerSetInDefaults() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load('routing.xml'); $route = $routeCollection->get('app_blog'); $this->assertSame('AppBundle:Blog:list', $route->getDefault('_controller')); } public function testOverrideControllerInDefaults() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for "app_blog"/'); $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $loader->load('override_defaults.xml'); } /** * @dataProvider provideFilesImportingRoutesWithControllers */ public function testImportRouteWithController($file) { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $routeCollection = $loader->load($file); $route = $routeCollection->get('app_homepage'); $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); $route = $routeCollection->get('app_blog'); $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); $route = $routeCollection->get('app_logout'); $this->assertSame('FrameworkBundle:Template:template', $route->getDefault('_controller')); } public function provideFilesImportingRoutesWithControllers() { yield ['import_controller.xml']; yield ['import__controller.xml']; } public function testImportWithOverriddenController() { $this->expectException('InvalidArgumentException'); $this->expectExceptionMessageMatches('/The routing file "[^"]*" must not specify both the "controller" attribute and the defaults key "_controller" for the "import" tag/'); $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/controller'])); $loader->load('import_override_defaults.xml'); } public function testImportRouteWithGlobMatchingSingleFile() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/glob'])); $routeCollection = $loader->load('import_single.xml'); $route = $routeCollection->get('bar_route'); $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); } public function testImportRouteWithGlobMatchingMultipleFiles() { $loader = new XmlFileLoader(new FileLocator([__DIR__.'/../Fixtures/glob'])); $routeCollection = $loader->load('import_multiple.xml'); $route = $routeCollection->get('bar_route'); $this->assertSame('AppBundle:Bar:view', $route->getDefault('_controller')); $route = $routeCollection->get('baz_route'); $this->assertSame('AppBundle:Baz:view', $route->getDefault('_controller')); } } PK~Z^s..Tests/CompiledRouteTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\CompiledRoute; class CompiledRouteTest extends TestCase { public function testAccessors() { $compiled = new CompiledRoute('prefix', 'regex', ['tokens'], [], null, [], [], ['variables']); $this->assertEquals('prefix', $compiled->getStaticPrefix(), '__construct() takes a static prefix as its second argument'); $this->assertEquals('regex', $compiled->getRegex(), '__construct() takes a regexp as its third argument'); $this->assertEquals(['tokens'], $compiled->getTokens(), '__construct() takes an array of tokens as its fourth argument'); $this->assertEquals(['variables'], $compiled->getVariables(), '__construct() takes an array of variables as its ninth argument'); } } PK~ZF)S6S6Tests/RouteCollectionTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class RouteCollectionTest extends TestCase { public function testRoute() { $collection = new RouteCollection(); $route = new Route('/foo'); $collection->add('foo', $route); $this->assertEquals(['foo' => $route], $collection->all(), '->add() adds a route'); $this->assertEquals($route, $collection->get('foo'), '->get() returns a route by name'); $this->assertNull($collection->get('bar'), '->get() returns null if a route does not exist'); } public function testOverriddenRoute() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $collection->add('foo', new Route('/foo1')); $this->assertEquals('/foo1', $collection->get('foo')->getPath()); } public function testDeepOverriddenRoute() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $collection1 = new RouteCollection(); $collection1->add('foo', new Route('/foo1')); $collection2 = new RouteCollection(); $collection2->add('foo', new Route('/foo2')); $collection1->addCollection($collection2); $collection->addCollection($collection1); $this->assertEquals('/foo2', $collection1->get('foo')->getPath()); $this->assertEquals('/foo2', $collection->get('foo')->getPath()); } public function testIterator() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $collection1 = new RouteCollection(); $collection1->add('bar', $bar = new Route('/bar')); $collection1->add('foo', $foo = new Route('/foo-new')); $collection->addCollection($collection1); $collection->add('last', $last = new Route('/last')); $this->assertInstanceOf('\ArrayIterator', $collection->getIterator()); $this->assertSame(['bar' => $bar, 'foo' => $foo, 'last' => $last], $collection->getIterator()->getArrayCopy()); } public function testCount() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $collection1 = new RouteCollection(); $collection1->add('bar', new Route('/bar')); $collection->addCollection($collection1); $this->assertCount(2, $collection); } public function testAddCollection() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $collection1 = new RouteCollection(); $collection1->add('bar', $bar = new Route('/bar')); $collection1->add('foo', $foo = new Route('/foo-new')); $collection2 = new RouteCollection(); $collection2->add('grandchild', $grandchild = new Route('/grandchild')); $collection1->addCollection($collection2); $collection->addCollection($collection1); $collection->add('last', $last = new Route('/last')); $this->assertSame(['bar' => $bar, 'foo' => $foo, 'grandchild' => $grandchild, 'last' => $last], $collection->all(), '->addCollection() imports routes of another collection, overrides if necessary and adds them at the end'); } public function testAddCollectionWithResources() { $collection = new RouteCollection(); $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml')); $collection1 = new RouteCollection(); $collection1->addResource($foo1 = new FileResource(__DIR__.'/Fixtures/foo1.xml')); $collection->addCollection($collection1); $this->assertEquals([$foo, $foo1], $collection->getResources(), '->addCollection() merges resources'); } public function testAddDefaultsAndRequirementsAndOptions() { $collection = new RouteCollection(); $collection->add('foo', new Route('/{placeholder}')); $collection1 = new RouteCollection(); $collection1->add('bar', new Route('/{placeholder}', ['_controller' => 'fixed', 'placeholder' => 'default'], ['placeholder' => '.+'], ['option' => 'value']) ); $collection->addCollection($collection1); $collection->addDefaults(['placeholder' => 'new-default']); $this->assertEquals(['placeholder' => 'new-default'], $collection->get('foo')->getDefaults(), '->addDefaults() adds defaults to all routes'); $this->assertEquals(['_controller' => 'fixed', 'placeholder' => 'new-default'], $collection->get('bar')->getDefaults(), '->addDefaults() adds defaults to all routes and overwrites existing ones'); $collection->addRequirements(['placeholder' => '\d+']); $this->assertEquals(['placeholder' => '\d+'], $collection->get('foo')->getRequirements(), '->addRequirements() adds requirements to all routes'); $this->assertEquals(['placeholder' => '\d+'], $collection->get('bar')->getRequirements(), '->addRequirements() adds requirements to all routes and overwrites existing ones'); $collection->addOptions(['option' => 'new-value']); $this->assertEquals( ['option' => 'new-value', 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler'], $collection->get('bar')->getOptions(), '->addOptions() adds options to all routes and overwrites existing ones' ); } public function testAddPrefix() { $collection = new RouteCollection(); $collection->add('foo', $foo = new Route('/foo')); $collection2 = new RouteCollection(); $collection2->add('bar', $bar = new Route('/bar')); $collection->addCollection($collection2); $collection->addPrefix(' / '); $this->assertSame('/foo', $collection->get('foo')->getPath(), '->addPrefix() trims the prefix and a single slash has no effect'); $collection->addPrefix('/{admin}', ['admin' => 'admin'], ['admin' => '\d+']); $this->assertEquals('/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() adds a prefix to all routes'); $this->assertEquals('/{admin}/bar', $collection->get('bar')->getPath(), '->addPrefix() adds a prefix to all routes'); $this->assertEquals(['admin' => 'admin'], $collection->get('foo')->getDefaults(), '->addPrefix() adds defaults to all routes'); $this->assertEquals(['admin' => 'admin'], $collection->get('bar')->getDefaults(), '->addPrefix() adds defaults to all routes'); $this->assertEquals(['admin' => '\d+'], $collection->get('foo')->getRequirements(), '->addPrefix() adds requirements to all routes'); $this->assertEquals(['admin' => '\d+'], $collection->get('bar')->getRequirements(), '->addPrefix() adds requirements to all routes'); $collection->addPrefix('0'); $this->assertEquals('/0/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() ensures a prefix must start with a slash and must not end with a slash'); $collection->addPrefix('/ /'); $this->assertSame('/ /0/{admin}/foo', $collection->get('foo')->getPath(), '->addPrefix() can handle spaces if desired'); $this->assertSame('/ /0/{admin}/bar', $collection->get('bar')->getPath(), 'the route pattern of an added collection is in synch with the added prefix'); } public function testAddPrefixOverridesDefaultsAndRequirements() { $collection = new RouteCollection(); $collection->add('foo', $foo = new Route('/foo.{_format}')); $collection->add('bar', $bar = new Route('/bar.{_format}', [], ['_format' => 'json'])); $collection->addPrefix('/admin', [], ['_format' => 'html']); $this->assertEquals('html', $collection->get('foo')->getRequirement('_format'), '->addPrefix() overrides existing requirements'); $this->assertEquals('html', $collection->get('bar')->getRequirement('_format'), '->addPrefix() overrides existing requirements'); } public function testResource() { $collection = new RouteCollection(); $collection->addResource($foo = new FileResource(__DIR__.'/Fixtures/foo.xml')); $collection->addResource($bar = new FileResource(__DIR__.'/Fixtures/bar.xml')); $collection->addResource(new FileResource(__DIR__.'/Fixtures/foo.xml')); $this->assertEquals([$foo, $bar], $collection->getResources(), '->addResource() adds a resource and getResources() only returns unique ones by comparing the string representation'); } public function testUniqueRouteWithGivenName() { $collection1 = new RouteCollection(); $collection1->add('foo', new Route('/old')); $collection2 = new RouteCollection(); $collection3 = new RouteCollection(); $collection3->add('foo', $new = new Route('/new')); $collection2->addCollection($collection3); $collection1->addCollection($collection2); $this->assertSame($new, $collection1->get('foo'), '->get() returns new route that overrode previous one'); // size of 1 because collection1 contains /new but not /old anymore $this->assertCount(1, $collection1->getIterator(), '->addCollection() removes previous routes when adding new routes with the same name'); } public function testGet() { $collection1 = new RouteCollection(); $collection1->add('a', $a = new Route('/a')); $collection2 = new RouteCollection(); $collection2->add('b', $b = new Route('/b')); $collection1->addCollection($collection2); $collection1->add('$péß^a|', $c = new Route('/special')); $this->assertSame($b, $collection1->get('b'), '->get() returns correct route in child collection'); $this->assertSame($c, $collection1->get('$péß^a|'), '->get() can handle special characters'); $this->assertNull($collection2->get('a'), '->get() does not return the route defined in parent collection'); $this->assertNull($collection1->get('non-existent'), '->get() returns null when route does not exist'); $this->assertNull($collection1->get(0), '->get() does not disclose internal child RouteCollection'); } public function testRemove() { $collection = new RouteCollection(); $collection->add('foo', $foo = new Route('/foo')); $collection1 = new RouteCollection(); $collection1->add('bar', $bar = new Route('/bar')); $collection->addCollection($collection1); $collection->add('last', $last = new Route('/last')); $collection->remove('foo'); $this->assertSame(['bar' => $bar, 'last' => $last], $collection->all(), '->remove() can remove a single route'); $collection->remove(['bar', 'last']); $this->assertSame([], $collection->all(), '->remove() accepts an array and can remove multiple routes at once'); } public function testSetHost() { $collection = new RouteCollection(); $routea = new Route('/a'); $routeb = new Route('/b', [], [], [], '{locale}.example.net'); $collection->add('a', $routea); $collection->add('b', $routeb); $collection->setHost('{locale}.example.com'); $this->assertEquals('{locale}.example.com', $routea->getHost()); $this->assertEquals('{locale}.example.com', $routeb->getHost()); } public function testSetCondition() { $collection = new RouteCollection(); $routea = new Route('/a'); $routeb = new Route('/b', [], [], [], '{locale}.example.net', [], [], 'context.getMethod() == "GET"'); $collection->add('a', $routea); $collection->add('b', $routeb); $collection->setCondition('context.getMethod() == "POST"'); $this->assertEquals('context.getMethod() == "POST"', $routea->getCondition()); $this->assertEquals('context.getMethod() == "POST"', $routeb->getCondition()); } public function testClone() { $collection = new RouteCollection(); $collection->add('a', new Route('/a')); $collection->add('b', new Route('/b', ['placeholder' => 'default'], ['placeholder' => '.+'])); $clonedCollection = clone $collection; $this->assertCount(2, $clonedCollection); $this->assertEquals($collection->get('a'), $clonedCollection->get('a')); $this->assertNotSame($collection->get('a'), $clonedCollection->get('a')); $this->assertEquals($collection->get('b'), $clonedCollection->get('b')); $this->assertNotSame($collection->get('b'), $clonedCollection->get('b')); } public function testSetSchemes() { $collection = new RouteCollection(); $routea = new Route('/a', [], [], [], '', 'http'); $routeb = new Route('/b'); $collection->add('a', $routea); $collection->add('b', $routeb); $collection->setSchemes(['http', 'https']); $this->assertEquals(['http', 'https'], $routea->getSchemes()); $this->assertEquals(['http', 'https'], $routeb->getSchemes()); } public function testSetMethods() { $collection = new RouteCollection(); $routea = new Route('/a', [], [], [], '', [], ['GET', 'POST']); $routeb = new Route('/b'); $collection->add('a', $routea); $collection->add('b', $routeb); $collection->setMethods('PUT'); $this->assertEquals(['PUT'], $routea->getMethods()); $this->assertEquals(['PUT'], $routeb->getMethods()); } } PK~Z!&zz2Tests/Matcher/DumpedRedirectableUrlMatcherTest.phpnu[ * * 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\Dumper\PhpMatcherDumper; use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; class DumpedRedirectableUrlMatcherTest extends RedirectableUrlMatcherTest { protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { static $i = 0; $class = 'DumpedRedirectableUrlMatcher'.++$i; $dumper = new PhpMatcherDumper($routes); eval('?>'.$dumper->dump(['class' => $class, 'base_class' => 'Symfony\Component\Routing\Tests\Matcher\TestDumpedRedirectableUrlMatcher'])); return $this->getMockBuilder($class) ->setConstructorArgs([$context ?: new RequestContext()]) ->setMethods(['redirect']) ->getMock(); } } class TestDumpedRedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface { public function redirect($path, $route, $scheme = null) { return []; } } PK~Zl=,Tests/Matcher/RedirectableUrlMatcherTest.phpnu[ * * 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\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class RedirectableUrlMatcherTest extends UrlMatcherTest { public function testMissingTrailingSlash() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/')); $matcher = $this->getUrlMatcher($coll); $matcher->expects($this->once())->method('redirect')->willReturn([]); $matcher->match('/foo'); } public function testRedirectWhenNoSlashForNonSafeMethod() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/')); $context = new RequestContext(); $context->setMethod('POST'); $matcher = $this->getUrlMatcher($coll, $context); $matcher->match('/foo'); } public function testSchemeRedirectRedirectsToFirstScheme() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', ['FTP', 'HTTPS'])); $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->once()) ->method('redirect') ->with('/foo', 'foo', 'ftp') ->willReturn(['_route' => 'foo']) ; $matcher->match('/foo'); } public function testNoSchemaRedirectIfOneOfMultipleSchemesMatches() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', ['https', 'http'])); $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->never()) ->method('redirect'); $matcher->match('/foo'); } public function testSchemeRedirectWithParams() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{bar}', [], [], [], '', ['https'])); $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->once()) ->method('redirect') ->with('/foo/baz', 'foo', 'https') ->willReturn(['redirect' => 'value']) ; $this->assertEquals(['_route' => 'foo', 'bar' => 'baz', 'redirect' => 'value'], $matcher->match('/foo/baz')); } public function testSlashRedirectWithParams() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{bar}/')); $matcher = $this->getUrlMatcher($coll); $matcher ->expects($this->once()) ->method('redirect') ->with('/foo/baz/', 'foo', null) ->willReturn(['redirect' => 'value']) ; $this->assertEquals(['_route' => 'foo', 'bar' => 'baz', 'redirect' => 'value'], $matcher->match('/foo/baz')); } public function testRedirectPreservesUrlEncoding() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo:bar/')); $matcher = $this->getUrlMatcher($coll); $matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/')->willReturn([]); $matcher->match('/foo%3Abar'); } public function testSchemeRequirement() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', ['https'])); $matcher = $this->getUrlMatcher($coll, new RequestContext()); $matcher->expects($this->once())->method('redirect')->with('/foo', 'foo', 'https')->willReturn([]); $this->assertSame(['_route' => 'foo'], $matcher->match('/foo')); } public function testFallbackPage() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/')); $coll->add('bar', new Route('/{name}')); $matcher = $this->getUrlMatcher($coll); $matcher->expects($this->once())->method('redirect')->with('/foo/')->willReturn(['_route' => 'foo']); $this->assertSame(['_route' => 'foo'], $matcher->match('/foo')); } public function testSlashAndVerbPrecedenceWithRedirection() { $coll = new RouteCollection(); $coll->add('a', new Route('/api/customers/{customerId}/contactpersons', [], [], [], '', [], ['post'])); $coll->add('b', new Route('/api/customers/{customerId}/contactpersons/', [], [], [], '', [], ['get'])); $matcher = $this->getUrlMatcher($coll); $expected = [ '_route' => 'b', 'customerId' => '123', ]; $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons/')); $matcher->expects($this->once())->method('redirect')->with('/api/customers/123/contactpersons/')->willReturn([]); $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); } protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return $this->getMockForAbstractClass('Symfony\Component\Routing\Matcher\RedirectableUrlMatcher', [$routes, $context ?: new RequestContext()]); } } PK~ZӔAaAa Tests/Matcher/UrlMatcherTest.phpnu[ * * 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 PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Exception\MethodNotAllowedException; use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class UrlMatcherTest extends TestCase { public function testNoMethodSoAllowed() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo')); $matcher = $this->getUrlMatcher($coll); $this->assertIsArray($matcher->match('/foo')); } public function testMethodNotAllowed() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', [], ['post'])); $matcher = $this->getUrlMatcher($coll); try { $matcher->match('/foo'); $this->fail(); } catch (MethodNotAllowedException $e) { $this->assertEquals(['POST'], $e->getAllowedMethods()); } } public function testMethodNotAllowedOnRoot() { $coll = new RouteCollection(); $coll->add('foo', new Route('/', [], [], [], '', [], ['GET'])); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'POST')); try { $matcher->match('/'); $this->fail(); } catch (MethodNotAllowedException $e) { $this->assertEquals(['GET'], $e->getAllowedMethods()); } } public function testHeadAllowedWhenRequirementContainsGet() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', [], ['get'])); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'head')); $this->assertIsArray($matcher->match('/foo')); } public function testMethodNotAllowedAggregatesAllowedMethods() { $coll = new RouteCollection(); $coll->add('foo1', new Route('/foo', [], [], [], '', [], ['post'])); $coll->add('foo2', new Route('/foo', [], [], [], '', [], ['put', 'delete'])); $matcher = $this->getUrlMatcher($coll); try { $matcher->match('/foo'); $this->fail(); } catch (MethodNotAllowedException $e) { $this->assertEquals(['POST', 'PUT', 'DELETE'], $e->getAllowedMethods()); } } public function testMatch() { // test the patterns are matched and parameters are returned $collection = new RouteCollection(); $collection->add('foo', new Route('/foo/{bar}')); $matcher = $this->getUrlMatcher($collection); try { $matcher->match('/no-match'); $this->fail(); } catch (ResourceNotFoundException $e) { } $this->assertEquals(['_route' => 'foo', 'bar' => 'baz'], $matcher->match('/foo/baz')); // test that defaults are merged $collection = new RouteCollection(); $collection->add('foo', new Route('/foo/{bar}', ['def' => 'test'])); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'foo', 'bar' => 'baz', 'def' => 'test'], $matcher->match('/foo/baz')); // test that route "method" is ignored if no method is given in the context $collection = new RouteCollection(); $collection->add('foo', new Route('/foo', [], [], [], '', [], ['get', 'head'])); $matcher = $this->getUrlMatcher($collection); $this->assertIsArray($matcher->match('/foo')); // route does not match with POST method context $matcher = $this->getUrlMatcher($collection, new RequestContext('', 'post')); try { $matcher->match('/foo'); $this->fail(); } catch (MethodNotAllowedException $e) { } // route does match with GET or HEAD method context $matcher = $this->getUrlMatcher($collection); $this->assertIsArray($matcher->match('/foo')); $matcher = $this->getUrlMatcher($collection, new RequestContext('', 'head')); $this->assertIsArray($matcher->match('/foo')); // route with an optional variable as the first segment $collection = new RouteCollection(); $collection->add('bar', new Route('/{bar}/foo', ['bar' => 'bar'], ['bar' => 'foo|bar'])); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'bar', 'bar' => 'bar'], $matcher->match('/bar/foo')); $this->assertEquals(['_route' => 'bar', 'bar' => 'foo'], $matcher->match('/foo/foo')); $collection = new RouteCollection(); $collection->add('bar', new Route('/{bar}', ['bar' => 'bar'], ['bar' => 'foo|bar'])); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'bar', 'bar' => 'foo'], $matcher->match('/foo')); $this->assertEquals(['_route' => 'bar', 'bar' => 'bar'], $matcher->match('/')); // route with only optional variables $collection = new RouteCollection(); $collection->add('bar', new Route('/{foo}/{bar}', ['foo' => 'foo', 'bar' => 'bar'], [])); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'bar', 'foo' => 'foo', 'bar' => 'bar'], $matcher->match('/')); $this->assertEquals(['_route' => 'bar', 'foo' => 'a', 'bar' => 'bar'], $matcher->match('/a')); $this->assertEquals(['_route' => 'bar', 'foo' => 'a', 'bar' => 'b'], $matcher->match('/a/b')); } public function testMatchWithPrefixes() { $collection = new RouteCollection(); $collection->add('foo', new Route('/{foo}')); $collection->addPrefix('/b'); $collection->addPrefix('/a'); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'foo', 'foo' => 'foo'], $matcher->match('/a/b/foo')); } public function testMatchWithDynamicPrefix() { $collection = new RouteCollection(); $collection->add('foo', new Route('/{foo}')); $collection->addPrefix('/b'); $collection->addPrefix('/{_locale}'); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_locale' => 'fr', '_route' => 'foo', 'foo' => 'foo'], $matcher->match('/fr/b/foo')); } public function testMatchSpecialRouteName() { $collection = new RouteCollection(); $collection->add('$péß^a|', new Route('/bar')); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => '$péß^a|'], $matcher->match('/bar')); } public function testTrailingEncodedNewlineIsNotOverlooked() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $matcher = $this->getUrlMatcher($collection); $matcher->match('/foo%0a'); } public function testMatchNonAlpha() { $collection = new RouteCollection(); $chars = '!"$%éà &\'()*+,./:;<=>@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\[]^_`abcdefghijklmnopqrstuvwxyz{|}~-'; $collection->add('foo', new Route('/{foo}/bar', [], ['foo' => '['.preg_quote($chars).']+'], ['utf8' => true])); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'foo', 'foo' => $chars], $matcher->match('/'.rawurlencode($chars).'/bar')); $this->assertEquals(['_route' => 'foo', 'foo' => $chars], $matcher->match('/'.strtr($chars, ['%' => '%25']).'/bar')); } public function testMatchWithDotMetacharacterInRequirements() { $collection = new RouteCollection(); $collection->add('foo', new Route('/{foo}/bar', [], ['foo' => '.+'])); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'foo', 'foo' => "\n"], $matcher->match('/'.urlencode("\n").'/bar'), 'linefeed character is matched'); } public function testMatchOverriddenRoute() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo')); $collection1 = new RouteCollection(); $collection1->add('foo', new Route('/foo1')); $collection->addCollection($collection1); $matcher = $this->getUrlMatcher($collection); $this->assertEquals(['_route' => 'foo'], $matcher->match('/foo1')); $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $this->assertEquals([], $matcher->match('/foo')); } public function testMatchRegression() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}')); $coll->add('bar', new Route('/foo/bar/{foo}')); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['foo' => 'bar', '_route' => 'bar'], $matcher->match('/foo/bar/bar')); $collection = new RouteCollection(); $collection->add('foo', new Route('/{bar}')); $matcher = $this->getUrlMatcher($collection); try { $matcher->match('/'); $this->fail(); } catch (ResourceNotFoundException $e) { } } public function testDefaultRequirementForOptionalVariables() { $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}', ['page' => 'index', '_format' => 'html'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['page' => 'my-page', '_format' => 'xml', '_route' => 'test'], $matcher->match('/my-page.xml')); } public function testMatchingIsEager() { $coll = new RouteCollection(); $coll->add('test', new Route('/{foo}-{bar}-', [], ['foo' => '.+', 'bar' => '.+'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['foo' => 'text1-text2-text3', 'bar' => 'text4', '_route' => 'test'], $matcher->match('/text1-text2-text3-text4-')); } public function testAdjacentVariables() { $coll = new RouteCollection(); $coll->add('test', new Route('/{w}{x}{y}{z}.{_format}', ['z' => 'default-z', '_format' => 'html'], ['y' => 'y|Y'])); $matcher = $this->getUrlMatcher($coll); // 'w' eagerly matches as much as possible and the other variables match the remaining chars. // This also shows that the variables w-z must all exclude the separating char (the dot '.' in this case) by default requirement. // Otherwise they would also consume '.xml' and _format would never match as it's an optional variable. $this->assertEquals(['w' => 'wwwww', 'x' => 'x', 'y' => 'Y', 'z' => 'Z', '_format' => 'xml', '_route' => 'test'], $matcher->match('/wwwwwxYZ.xml')); // As 'y' has custom requirement and can only be of value 'y|Y', it will leave 'ZZZ' to variable z. // So with carefully chosen requirements adjacent variables, can be useful. $this->assertEquals(['w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'ZZZ', '_format' => 'html', '_route' => 'test'], $matcher->match('/wwwwwxyZZZ')); // z and _format are optional. $this->assertEquals(['w' => 'wwwww', 'x' => 'x', 'y' => 'y', 'z' => 'default-z', '_format' => 'html', '_route' => 'test'], $matcher->match('/wwwwwxy')); $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $matcher->match('/wxy.html'); } public function testOptionalVariableWithNoRealSeparator() { $coll = new RouteCollection(); $coll->add('test', new Route('/get{what}', ['what' => 'All'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['what' => 'All', '_route' => 'test'], $matcher->match('/get')); $this->assertEquals(['what' => 'Sites', '_route' => 'test'], $matcher->match('/getSites')); // Usually the character in front of an optional parameter can be left out, e.g. with pattern '/get/{what}' just '/get' would match. // But here the 't' in 'get' is not a separating character, so it makes no sense to match without it. $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $matcher->match('/ge'); } public function testRequiredVariableWithNoRealSeparator() { $coll = new RouteCollection(); $coll->add('test', new Route('/get{what}Suffix')); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['what' => 'Sites', '_route' => 'test'], $matcher->match('/getSitesSuffix')); } public function testDefaultRequirementOfVariable() { $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}')); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['page' => 'index', '_format' => 'mobile.html', '_route' => 'test'], $matcher->match('/index.mobile.html')); } public function testDefaultRequirementOfVariableDisallowsSlash() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}')); $matcher = $this->getUrlMatcher($coll); $matcher->match('/index.sl/ash'); } public function testDefaultRequirementOfVariableDisallowsNextSeparator() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('test', new Route('/{page}.{_format}', [], ['_format' => 'html|xml'])); $matcher = $this->getUrlMatcher($coll); $matcher->match('/do.t.html'); } public function testSchemeRequirement() { $this->getExpectedException() ?: $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', ['https'])); $matcher = $this->getUrlMatcher($coll); $matcher->match('/foo'); } public function testCondition() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $route = new Route('/foo'); $route->setCondition('context.getMethod() == "POST"'); $coll->add('foo', $route); $matcher = $this->getUrlMatcher($coll); $matcher->match('/foo'); } public function testRequestCondition() { $coll = new RouteCollection(); $route = new Route('/foo/{bar}'); $route->setCondition('request.getBaseUrl() == "/sub/front.php" and request.getPathInfo() == "/foo/bar"'); $coll->add('foo', $route); $matcher = $this->getUrlMatcher($coll, new RequestContext('/sub/front.php')); $this->assertEquals(['bar' => 'bar', '_route' => 'foo'], $matcher->match('/foo/bar')); } public function testDecodeOnce() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}')); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['foo' => 'bar%23', '_route' => 'foo'], $matcher->match('/foo/bar%2523')); } public function testCannotRelyOnPrefix() { $coll = new RouteCollection(); $subColl = new RouteCollection(); $subColl->add('bar', new Route('/bar')); $subColl->addPrefix('/prefix'); // overwrite the pattern, so the prefix is not valid anymore for this route in the collection $subColl->get('bar')->setPath('/new'); $coll->addCollection($subColl); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['_route' => 'bar'], $matcher->match('/new')); } public function testWithHost() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}', [], [], [], '{locale}.example.com')); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(['foo' => 'bar', '_route' => 'foo', 'locale' => 'en'], $matcher->match('/foo/bar')); } public function testWithHostOnRouteCollection() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}')); $coll->add('bar', new Route('/bar/{foo}', [], [], [], '{locale}.example.net')); $coll->setHost('{locale}.example.com'); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(['foo' => 'bar', '_route' => 'foo', 'locale' => 'en'], $matcher->match('/foo/bar')); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(['foo' => 'bar', '_route' => 'bar', 'locale' => 'en'], $matcher->match('/bar/bar')); } public function testWithOutHostHostDoesNotMatch() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('foo', new Route('/foo/{foo}', [], [], [], '{locale}.example.com')); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'example.com')); $matcher->match('/foo/bar'); } public function testPathIsCaseSensitive() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('foo', new Route('/locale', [], ['locale' => 'EN|FR|DE'])); $matcher = $this->getUrlMatcher($coll); $matcher->match('/en'); } public function testHostIsCaseInsensitive() { $coll = new RouteCollection(); $coll->add('foo', new Route('/', [], ['locale' => 'EN|FR|DE'], [], '{locale}.example.com')); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'en.example.com')); $this->assertEquals(['_route' => 'foo', 'locale' => 'en'], $matcher->match('/')); } public function testNoConfiguration() { $this->expectException('Symfony\Component\Routing\Exception\NoConfigurationException'); $coll = new RouteCollection(); $matcher = $this->getUrlMatcher($coll); $matcher->match('/'); } public function testNestedCollections() { $coll = new RouteCollection(); $subColl = new RouteCollection(); $subColl->add('a', new Route('/a')); $subColl->add('b', new Route('/b')); $subColl->add('c', new Route('/c')); $subColl->addPrefix('/p'); $coll->addCollection($subColl); $coll->add('baz', new Route('/{baz}')); $subColl = new RouteCollection(); $subColl->add('buz', new Route('/buz')); $subColl->addPrefix('/prefix'); $coll->addCollection($subColl); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['_route' => 'a'], $matcher->match('/p/a')); $this->assertEquals(['_route' => 'baz', 'baz' => 'p'], $matcher->match('/p')); $this->assertEquals(['_route' => 'buz'], $matcher->match('/prefix/buz')); } public function testSchemeAndMethodMismatch() { $this->expectException('Symfony\Component\Routing\Exception\ResourceNotFoundException'); $coll = new RouteCollection(); $coll->add('foo', new Route('/', [], [], [], null, ['https'], ['POST'])); $matcher = $this->getUrlMatcher($coll); $matcher->match('/'); } public function testSiblingRoutes() { $coll = new RouteCollection(); $coll->add('a', (new Route('/a{a}'))->setMethods('POST')); $coll->add('b', (new Route('/a{a}'))->setMethods('PUT')); $coll->add('c', new Route('/a{a}')); $coll->add('d', (new Route('/b{a}'))->setCondition('false')); $coll->add('e', (new Route('/{b}{a}'))->setCondition('false')); $coll->add('f', (new Route('/{b}{a}'))->setRequirements(['b' => 'b'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['_route' => 'c', 'a' => 'a'], $matcher->match('/aa')); $this->assertEquals(['_route' => 'f', 'b' => 'b', 'a' => 'a'], $matcher->match('/ba')); } public function testRequirementWithCapturingGroup() { $coll = new RouteCollection(); $coll->add('a', new Route('/{a}/{b}', [], ['a' => '(a|b)'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['_route' => 'a', 'a' => 'a', 'b' => 'b'], $matcher->match('/a/b')); } public function testDotAllWithCatchAll() { $coll = new RouteCollection(); $coll->add('a', new Route('/{id}.html', [], ['id' => '.+'])); $coll->add('b', new Route('/{all}', [], ['all' => '.+'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['_route' => 'a', 'id' => 'foo/bar'], $matcher->match('/foo/bar.html')); } public function testHostPattern() { $coll = new RouteCollection(); $coll->add('a', new Route('/{app}/{action}/{unused}', [], [], [], '{host}')); $expected = [ '_route' => 'a', 'app' => 'an_app', 'action' => 'an_action', 'unused' => 'unused', 'host' => 'foo', ]; $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'GET', 'foo')); $this->assertEquals($expected, $matcher->match('/an_app/an_action/unused')); } public function testHostWithDot() { $coll = new RouteCollection(); $coll->add('a', new Route('/foo', [], [], [], 'foo.example.com')); $coll->add('b', new Route('/bar/{baz}')); $matcher = $this->getUrlMatcher($coll); $this->assertEquals('b', $matcher->match('/bar/abc.123')['_route']); } public function testSlashVariant() { $coll = new RouteCollection(); $coll->add('a', new Route('/foo/{bar}', [], ['bar' => '.*'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals('a', $matcher->match('/foo/')['_route']); } public function testSlashWithVerb() { $coll = new RouteCollection(); $coll->add('a', new Route('/{foo}', [], [], [], '', [], ['put', 'delete'])); $coll->add('b', new Route('/bar/')); $matcher = $this->getUrlMatcher($coll); $this->assertSame(['_route' => 'b'], $matcher->match('/bar/')); $coll = new RouteCollection(); $coll->add('a', new Route('/dav/{foo}', [], ['foo' => '.*'], [], '', [], ['GET', 'OPTIONS'])); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'OPTIONS')); $expected = [ '_route' => 'a', 'foo' => 'files/bar/', ]; $this->assertEquals($expected, $matcher->match('/dav/files/bar/')); } public function testSlashAndVerbPrecedence() { $coll = new RouteCollection(); $coll->add('a', new Route('/api/customers/{customerId}/contactpersons/', [], [], [], '', [], ['post'])); $coll->add('b', new Route('/api/customers/{customerId}/contactpersons', [], [], [], '', [], ['get'])); $matcher = $this->getUrlMatcher($coll); $expected = [ '_route' => 'b', 'customerId' => '123', ]; $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); $coll = new RouteCollection(); $coll->add('a', new Route('/api/customers/{customerId}/contactpersons/', [], [], [], '', [], ['get'])); $coll->add('b', new Route('/api/customers/{customerId}/contactpersons', [], [], [], '', [], ['post'])); $matcher = $this->getUrlMatcher($coll, new RequestContext('', 'POST')); $expected = [ '_route' => 'b', 'customerId' => '123', ]; $this->assertEquals($expected, $matcher->match('/api/customers/123/contactpersons')); } public function testGreedyTrailingRequirement() { $coll = new RouteCollection(); $coll->add('a', new Route('/{a}', [], ['a' => '.+'])); $matcher = $this->getUrlMatcher($coll); $this->assertEquals(['_route' => 'a', 'a' => 'foo'], $matcher->match('/foo')); $this->assertEquals(['_route' => 'a', 'a' => 'foo/'], $matcher->match('/foo/')); } protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new UrlMatcher($routes, $context ?: new RequestContext()); } } PK~Z::)Tests/Matcher/TraceableUrlMatcherTest.phpnu[ * * 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\HttpFoundation\Request; use Symfony\Component\Routing\Matcher\TraceableUrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class TraceableUrlMatcherTest extends UrlMatcherTest { public function test() { $coll = new RouteCollection(); $coll->add('foo', new Route('/foo', [], [], [], '', [], ['POST'])); $coll->add('bar', new Route('/bar/{id}', [], ['id' => '\d+'])); $coll->add('bar1', new Route('/bar/{name}', [], ['id' => '\w+'], [], '', [], ['POST'])); $coll->add('bar2', new Route('/foo', [], [], [], 'baz')); $coll->add('bar3', new Route('/foo1', [], [], [], 'baz')); $coll->add('bar4', new Route('/foo2', [], [], [], 'baz', [], [], 'context.getMethod() == "GET"')); $context = new RequestContext(); $context->setHost('baz'); $matcher = new TraceableUrlMatcher($coll, $context); $traces = $matcher->getTraces('/babar'); $this->assertSame([0, 0, 0, 0, 0, 0], $this->getLevels($traces)); $traces = $matcher->getTraces('/foo'); $this->assertSame([1, 0, 0, 2], $this->getLevels($traces)); $traces = $matcher->getTraces('/bar/12'); $this->assertSame([0, 2], $this->getLevels($traces)); $traces = $matcher->getTraces('/bar/dd'); $this->assertSame([0, 1, 1, 0, 0, 0], $this->getLevels($traces)); $traces = $matcher->getTraces('/foo1'); $this->assertSame([0, 0, 0, 0, 2], $this->getLevels($traces)); $context->setMethod('POST'); $traces = $matcher->getTraces('/foo'); $this->assertSame([2], $this->getLevels($traces)); $traces = $matcher->getTraces('/bar/dd'); $this->assertSame([0, 1, 2], $this->getLevels($traces)); $traces = $matcher->getTraces('/foo2'); $this->assertSame([0, 0, 0, 0, 0, 1], $this->getLevels($traces)); } public function testMatchRouteOnMultipleHosts() { $routes = new RouteCollection(); $routes->add('first', new Route( '/mypath/', ['_controller' => 'MainBundle:Info:first'], [], [], 'some.example.com' )); $routes->add('second', new Route( '/mypath/', ['_controller' => 'MainBundle:Info:second'], [], [], 'another.example.com' )); $context = new RequestContext(); $context->setHost('baz'); $matcher = new TraceableUrlMatcher($routes, $context); $traces = $matcher->getTraces('/mypath/'); $this->assertSame( [TraceableUrlMatcher::ROUTE_ALMOST_MATCHES, TraceableUrlMatcher::ROUTE_ALMOST_MATCHES], $this->getLevels($traces) ); } public function getLevels($traces) { $levels = []; foreach ($traces as $trace) { $levels[] = $trace['level']; } return $levels; } public function testRoutesWithConditions() { $routes = new RouteCollection(); $routes->add('foo', new Route('/foo', [], [], [], 'baz', [], [], "request.headers.get('User-Agent') matches '/firefox/i'")); $context = new RequestContext(); $context->setHost('baz'); $matcher = new TraceableUrlMatcher($routes, $context); $notMatchingRequest = Request::create('/foo', 'GET'); $traces = $matcher->getTracesForRequest($notMatchingRequest); $this->assertEquals("Condition \"request.headers.get('User-Agent') matches '/firefox/i'\" does not evaluate to \"true\"", $traces[0]['log']); $matchingRequest = Request::create('/foo', 'GET', [], [], [], ['HTTP_USER_AGENT' => 'Firefox']); $traces = $matcher->getTracesForRequest($matchingRequest); $this->assertEquals('Route matches!', $traces[0]['log']); } protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { return new TraceableUrlMatcher($routes, $context ?: new RequestContext()); } } PK~Zţ&Tests/Matcher/DumpedUrlMatcherTest.phpnu[ * * 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\Dumper\PhpMatcherDumper; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; class DumpedUrlMatcherTest extends UrlMatcherTest { public function testSchemeRequirement() { $this->expectException('LogicException'); $this->expectExceptionMessage('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); parent::testSchemeRequirement(); } public function testSchemeAndMethodMismatch() { $this->expectException('LogicException'); $this->expectExceptionMessage('The "schemes" requirement is only supported for URL matchers that implement RedirectableUrlMatcherInterface.'); parent::testSchemeRequirement(); } protected function getUrlMatcher(RouteCollection $routes, RequestContext $context = null) { static $i = 0; $class = 'DumpedUrlMatcher'.++$i; $dumper = new PhpMatcherDumper($routes); eval('?>'.$dumper->dump(['class' => $class])); return new $class($context ?: new RequestContext()); } } PK~ZC3Tests/Matcher/Dumper/StaticPrefixCollectionTest.phpnu[compile()->getStaticPrefix(); $collection->addRoute($staticPrefix, $name); } $collection->optimizeGroups(); $dumped = $this->dumpCollection($collection); $this->assertEquals($expected, $dumped); } public function routeProvider() { return [ 'Simple - not nested' => [ [ ['/', 'root'], ['/prefix/segment/', 'prefix_segment'], ['/leading/segment/', 'leading_segment'], ], << [ [ ['/', 'root'], ['/prefix/segment/aa', 'prefix_segment'], ['/prefix/segment/bb', 'leading_segment'], ], << [ [ ['/', 'root'], ['/prefix/segment/', 'prefix_segment'], ['/prefix/segment/bb', 'leading_segment'], ], << /prefix/segment prefix_segment -> /prefix/segment/bb leading_segment EOF ], 'Simple one level nesting' => [ [ ['/', 'root'], ['/group/segment/', 'nested_segment'], ['/group/thing/', 'some_segment'], ['/group/other/', 'other_segment'], ], << /group/segment nested_segment -> /group/thing some_segment -> /group/other other_segment EOF ], 'Retain matching order with groups' => [ [ ['/group/aa/', 'aa'], ['/group/bb/', 'bb'], ['/group/cc/', 'cc'], ['/', 'root'], ['/group/dd/', 'dd'], ['/group/ee/', 'ee'], ['/group/ff/', 'ff'], ], << /group/aa aa -> /group/bb bb -> /group/cc cc / root /group -> /group/dd dd -> /group/ee ee -> /group/ff ff EOF ], 'Retain complex matching order with groups at base' => [ [ ['/aaa/111/', 'first_aaa'], ['/prefixed/group/aa/', 'aa'], ['/prefixed/group/bb/', 'bb'], ['/prefixed/group/cc/', 'cc'], ['/prefixed/', 'root'], ['/prefixed/group/dd/', 'dd'], ['/prefixed/group/ee/', 'ee'], ['/prefixed/group/ff/', 'ff'], ['/aaa/222/', 'second_aaa'], ['/aaa/333/', 'third_aaa'], ], << /aaa/111 first_aaa -> /aaa/222 second_aaa -> /aaa/333 third_aaa /prefixed -> /prefixed/group -> -> /prefixed/group/aa aa -> -> /prefixed/group/bb bb -> -> /prefixed/group/cc cc -> /prefixed root -> /prefixed/group -> -> /prefixed/group/dd dd -> -> /prefixed/group/ee ee -> -> /prefixed/group/ff ff EOF ], 'Group regardless of segments' => [ [ ['/aaa-111/', 'a1'], ['/aaa-222/', 'a2'], ['/aaa-333/', 'a3'], ['/group-aa/', 'g1'], ['/group-bb/', 'g2'], ['/group-cc/', 'g3'], ], << /aaa-111 a1 -> /aaa-222 a2 -> /aaa-333 a3 /group- -> /group-aa g1 -> /group-bb g2 -> /group-cc g3 EOF ], ]; } private function dumpCollection(StaticPrefixCollection $collection, $prefix = '') { $lines = []; foreach ($collection->getItems() as $item) { if ($item instanceof StaticPrefixCollection) { $lines[] = $prefix.$item->getPrefix(); $lines[] = $this->dumpCollection($item, $prefix.'-> '); } else { $lines[] = $prefix.implode(' ', $item); } } return implode("\n", $lines); } } PK~ZABB-Tests/Matcher/Dumper/PhpMatcherDumperTest.phpnu[ * * 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\Dumper\PhpMatcherDumper; use Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface; use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class PhpMatcherDumperTest extends TestCase { /** * @var string */ private $matcherClass; /** * @var string */ private $dumpPath; protected function setUp() { parent::setUp(); $this->matcherClass = uniqid('ProjectUrlMatcher'); $this->dumpPath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_matcher.'.$this->matcherClass.'.php'; } protected function tearDown() { parent::tearDown(); @unlink($this->dumpPath); } public function testDumpWhenSchemeIsUsedWithoutAProperDumper() { $this->expectException('LogicException'); $collection = new RouteCollection(); $collection->add('secure', new Route( '/secure', [], [], [], '', ['https'] )); $dumper = new PhpMatcherDumper($collection); $dumper->dump(); } public function testRedirectPreservesUrlEncoding() { $collection = new RouteCollection(); $collection->add('foo', new Route('/foo:bar/')); $class = $this->generateDumpedMatcher($collection, true); $matcher = $this->getMockBuilder($class) ->setMethods(['redirect']) ->setConstructorArgs([new RequestContext()]) ->getMock(); $matcher->expects($this->once())->method('redirect')->with('/foo%3Abar/', 'foo')->willReturn([]); $matcher->match('/foo%3Abar'); } /** * @dataProvider getRouteCollections */ public function testDump(RouteCollection $collection, $fixture, $options = []) { $basePath = __DIR__.'/../../Fixtures/dumper/'; $dumper = new PhpMatcherDumper($collection); $this->assertStringEqualsFile($basePath.$fixture, $dumper->dump($options), '->dump() correctly dumps routes as optimized PHP code.'); } 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}')); $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/')); $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'])); return [ [new RouteCollection(), 'url_matcher0.php', []], [$collection, 'url_matcher1.php', []], [$redirectCollection, 'url_matcher2.php', ['base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher']], [$rootprefixCollection, 'url_matcher3.php', []], [$headMatchCasesCollection, 'url_matcher4.php', []], [$groupOptimisedCollection, 'url_matcher5.php', ['base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher']], [$trailingSlashCollection, 'url_matcher6.php', []], [$trailingSlashCollection, 'url_matcher7.php', ['base_class' => 'Symfony\Component\Routing\Tests\Fixtures\RedirectableUrlMatcher']], ]; } private function generateDumpedMatcher(RouteCollection $collection, $redirectableStub = false) { $options = ['class' => $this->matcherClass]; if ($redirectableStub) { $options['base_class'] = '\Symfony\Component\Routing\Tests\Matcher\Dumper\RedirectableUrlMatcherStub'; } $dumper = new PhpMatcherDumper($collection); $code = $dumper->dump($options); file_put_contents($this->dumpPath, $code); include $this->dumpPath; return $this->matcherClass; } } abstract class RedirectableUrlMatcherStub extends UrlMatcher implements RedirectableUrlMatcherInterface { public function redirect($path, $route, $scheme = null) { } } PK~ZJ-Tests/Matcher/Dumper/DumperCollectionTest.phpnu[ * * 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\Dumper\DumperCollection; class DumperCollectionTest extends TestCase { public function testGetRoot() { $a = new DumperCollection(); $b = new DumperCollection(); $a->add($b); $c = new DumperCollection(); $b->add($c); $d = new DumperCollection(); $c->add($d); $this->assertSame($a, $c->getRoot()); } } PK~Z݃;;$Tests/RouteCollectionBuilderTest.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\Loader\YamlFileLoader; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\RouteCollectionBuilder; class RouteCollectionBuilderTest extends TestCase { public function testImport() { $resolvedLoader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); $resolver = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderResolverInterface')->getMock(); $resolver->expects($this->once()) ->method('resolve') ->with('admin_routing.yml', 'yaml') ->willReturn($resolvedLoader); $originalRoute = new Route('/foo/path'); $expectedCollection = new RouteCollection(); $expectedCollection->add('one_test_route', $originalRoute); $expectedCollection->addResource(new FileResource(__DIR__.'/Fixtures/file_resource.yml')); $resolvedLoader ->expects($this->once()) ->method('load') ->with('admin_routing.yml', 'yaml') ->willReturn($expectedCollection); $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); $loader->expects($this->any()) ->method('getResolver') ->willReturn($resolver); // import the file! $routes = new RouteCollectionBuilder($loader); $importedRoutes = $routes->import('admin_routing.yml', '/', 'yaml'); // we should get back a RouteCollectionBuilder $this->assertInstanceOf('Symfony\Component\Routing\RouteCollectionBuilder', $importedRoutes); // get the collection back so we can look at it $addedCollection = $importedRoutes->build(); $route = $addedCollection->get('one_test_route'); $this->assertSame($originalRoute, $route); // should return file_resource.yml, which is in the original collection $this->assertCount(1, $addedCollection->getResources()); // make sure the routes were imported into the top-level builder $routeCollection = $routes->build(); $this->assertCount(1, $routes->build()); $this->assertCount(1, $routeCollection->getResources()); } public function testImportAddResources() { $routeCollectionBuilder = new RouteCollectionBuilder(new YamlFileLoader(new FileLocator([__DIR__.'/Fixtures/']))); $routeCollectionBuilder->import('file_resource.yml'); $routeCollection = $routeCollectionBuilder->build(); $this->assertCount(1, $routeCollection->getResources()); } public function testImportWithoutLoaderThrowsException() { $this->expectException('BadMethodCallException'); $collectionBuilder = new RouteCollectionBuilder(); $collectionBuilder->import('routing.yml'); } public function testAdd() { $collectionBuilder = new RouteCollectionBuilder(); $addedRoute = $collectionBuilder->add('/checkout', 'AppBundle:Order:checkout'); $addedRoute2 = $collectionBuilder->add('/blogs', 'AppBundle:Blog:list', 'blog_list'); $this->assertInstanceOf('Symfony\Component\Routing\Route', $addedRoute); $this->assertEquals('AppBundle:Order:checkout', $addedRoute->getDefault('_controller')); $finalCollection = $collectionBuilder->build(); $this->assertSame($addedRoute2, $finalCollection->get('blog_list')); } public function testFlushOrdering() { $importedCollection = new RouteCollection(); $importedCollection->add('imported_route1', new Route('/imported/foo1')); $importedCollection->add('imported_route2', new Route('/imported/foo2')); $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); // make this loader able to do the import - keeps mocking simple $loader->expects($this->any()) ->method('supports') ->willReturn(true); $loader ->expects($this->once()) ->method('load') ->willReturn($importedCollection); $routes = new RouteCollectionBuilder($loader); // 1) Add a route $routes->add('/checkout', 'AppBundle:Order:checkout', 'checkout_route'); // 2) Import from a file $routes->mount('/', $routes->import('admin_routing.yml')); // 3) Add another route $routes->add('/', 'AppBundle:Default:homepage', 'homepage'); // 4) Add another route $routes->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); // set a default value $routes->setDefault('_locale', 'fr'); $actualCollection = $routes->build(); $this->assertCount(5, $actualCollection); $actualRouteNames = array_keys($actualCollection->all()); $this->assertEquals([ 'checkout_route', 'imported_route1', 'imported_route2', 'homepage', 'admin_dashboard', ], $actualRouteNames); // make sure the defaults were set $checkoutRoute = $actualCollection->get('checkout_route'); $defaults = $checkoutRoute->getDefaults(); $this->assertArrayHasKey('_locale', $defaults); $this->assertEquals('fr', $defaults['_locale']); } public function testFlushSetsRouteNames() { $collectionBuilder = new RouteCollectionBuilder(); // add a "named" route $collectionBuilder->add('/admin', 'AppBundle:Admin:dashboard', 'admin_dashboard'); // add an unnamed route $collectionBuilder->add('/blogs', 'AppBundle:Blog:list') ->setMethods(['GET']); // integer route names are allowed - they don't confuse things $collectionBuilder->add('/products', 'AppBundle:Product:list', 100); $actualCollection = $collectionBuilder->build(); $actualRouteNames = array_keys($actualCollection->all()); $this->assertEquals([ 'admin_dashboard', 'GET_blogs', '100', ], $actualRouteNames); } public function testFlushSetsDetailsOnChildrenRoutes() { $routes = new RouteCollectionBuilder(); $routes->add('/blogs/{page}', 'listAction', 'blog_list') // unique things for the route ->setDefault('page', 1) ->setRequirement('id', '\d+') ->setOption('expose', true) // things that the collection will try to override (but won't) ->setDefault('_format', 'html') ->setRequirement('_format', 'json|xml') ->setOption('fooBar', true) ->setHost('example.com') ->setCondition('request.isSecure()') ->setSchemes(['https']) ->setMethods(['POST']); // a simple route, nothing added to it $routes->add('/blogs/{id}', 'editAction', 'blog_edit'); // configure the collection itself $routes // things that will not override the child route ->setDefault('_format', 'json') ->setRequirement('_format', 'xml') ->setOption('fooBar', false) ->setHost('symfony.com') ->setCondition('request.query.get("page")==1') // some unique things that should be set on the child ->setDefault('_locale', 'fr') ->setRequirement('_locale', 'fr|en') ->setOption('niceRoute', true) ->setSchemes(['http']) ->setMethods(['GET', 'POST']); $collection = $routes->build(); $actualListRoute = $collection->get('blog_list'); $this->assertEquals(1, $actualListRoute->getDefault('page')); $this->assertEquals('\d+', $actualListRoute->getRequirement('id')); $this->assertTrue($actualListRoute->getOption('expose')); // none of these should be overridden $this->assertEquals('html', $actualListRoute->getDefault('_format')); $this->assertEquals('json|xml', $actualListRoute->getRequirement('_format')); $this->assertTrue($actualListRoute->getOption('fooBar')); $this->assertEquals('example.com', $actualListRoute->getHost()); $this->assertEquals('request.isSecure()', $actualListRoute->getCondition()); $this->assertEquals(['https'], $actualListRoute->getSchemes()); $this->assertEquals(['POST'], $actualListRoute->getMethods()); // inherited from the main collection $this->assertEquals('fr', $actualListRoute->getDefault('_locale')); $this->assertEquals('fr|en', $actualListRoute->getRequirement('_locale')); $this->assertTrue($actualListRoute->getOption('niceRoute')); $actualEditRoute = $collection->get('blog_edit'); // inherited from the collection $this->assertEquals('symfony.com', $actualEditRoute->getHost()); $this->assertEquals('request.query.get("page")==1', $actualEditRoute->getCondition()); $this->assertEquals(['http'], $actualEditRoute->getSchemes()); $this->assertEquals(['GET', 'POST'], $actualEditRoute->getMethods()); } /** * @dataProvider providePrefixTests */ public function testFlushPrefixesPaths($collectionPrefix, $routePath, $expectedPath) { $routes = new RouteCollectionBuilder(); $routes->add($routePath, 'someController', 'test_route'); $outerRoutes = new RouteCollectionBuilder(); $outerRoutes->mount($collectionPrefix, $routes); $collection = $outerRoutes->build(); $this->assertEquals($expectedPath, $collection->get('test_route')->getPath()); } public function providePrefixTests() { $tests = []; // empty prefix is of course ok $tests[] = ['', '/foo', '/foo']; // normal prefix - does not matter if it's a wildcard $tests[] = ['/{admin}', '/foo', '/{admin}/foo']; // shows that a prefix will always be given the starting slash $tests[] = ['0', '/foo', '/0/foo']; // spaces are ok, and double slashes at the end are cleaned $tests[] = ['/ /', '/foo', '/ /foo']; return $tests; } public function testFlushSetsPrefixedWithMultipleLevels() { $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); $routes = new RouteCollectionBuilder($loader); $routes->add('homepage', 'MainController::homepageAction', 'homepage'); $adminRoutes = $routes->createBuilder(); $adminRoutes->add('/dashboard', 'AdminController::dashboardAction', 'admin_dashboard'); // embedded collection under /admin $adminBlogRoutes = $routes->createBuilder(); $adminBlogRoutes->add('/new', 'BlogController::newAction', 'admin_blog_new'); // mount into admin, but before the parent collection has been mounted $adminRoutes->mount('/blog', $adminBlogRoutes); // now mount the /admin routes, above should all still be /blog/admin $routes->mount('/admin', $adminRoutes); // add a route after mounting $adminRoutes->add('/users', 'AdminController::userAction', 'admin_users'); // add another sub-collection after the mount $otherAdminRoutes = $routes->createBuilder(); $otherAdminRoutes->add('/sales', 'StatsController::indexAction', 'admin_stats_sales'); $adminRoutes->mount('/stats', $otherAdminRoutes); // add a normal collection and see that it is also prefixed $importedCollection = new RouteCollection(); $importedCollection->add('imported_route', new Route('/foo')); // make this loader able to do the import - keeps mocking simple $loader->expects($this->any()) ->method('supports') ->willReturn(true); $loader ->expects($this->any()) ->method('load') ->willReturn($importedCollection); // import this from the /admin route builder $adminRoutes->import('admin.yml', '/imported'); $collection = $routes->build(); $this->assertEquals('/admin/dashboard', $collection->get('admin_dashboard')->getPath(), 'Routes before mounting have the prefix'); $this->assertEquals('/admin/users', $collection->get('admin_users')->getPath(), 'Routes after mounting have the prefix'); $this->assertEquals('/admin/blog/new', $collection->get('admin_blog_new')->getPath(), 'Sub-collections receive prefix even if mounted before parent prefix'); $this->assertEquals('/admin/stats/sales', $collection->get('admin_stats_sales')->getPath(), 'Sub-collections receive prefix if mounted after parent prefix'); $this->assertEquals('/admin/imported/foo', $collection->get('imported_route')->getPath(), 'Normal RouteCollections are also prefixed properly'); } public function testAutomaticRouteNamesDoNotConflict() { $routes = new RouteCollectionBuilder(); $adminRoutes = $routes->createBuilder(); // route 1 $adminRoutes->add('/dashboard', ''); $accountRoutes = $routes->createBuilder(); // route 2 $accountRoutes->add('/dashboard', '') ->setMethods(['GET']); // route 3 $accountRoutes->add('/dashboard', '') ->setMethods(['POST']); $routes->mount('/admin', $adminRoutes); $routes->mount('/account', $accountRoutes); $collection = $routes->build(); // there are 2 routes (i.e. with non-conflicting names) $this->assertCount(3, $collection->all()); } public function testAddsThePrefixOnlyOnceWhenLoadingMultipleCollections() { $firstCollection = new RouteCollection(); $firstCollection->add('a', new Route('/a')); $secondCollection = new RouteCollection(); $secondCollection->add('b', new Route('/b')); $loader = $this->getMockBuilder('Symfony\Component\Config\Loader\LoaderInterface')->getMock(); $loader->expects($this->any()) ->method('supports') ->willReturn(true); $loader ->expects($this->any()) ->method('load') ->willReturn([$firstCollection, $secondCollection]); $routeCollectionBuilder = new RouteCollectionBuilder($loader); $routeCollectionBuilder->import('/directory/recurse/*', '/other/', 'glob'); $routes = $routeCollectionBuilder->build()->all(); $this->assertCount(2, $routes); $this->assertEquals('/other/a', $routes['a']->getPath()); $this->assertEquals('/other/b', $routes['b']->getPath()); } } PK~ZS$Tests/Generator/UrlGeneratorTest.phpnu[ * * 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; use PHPUnit\Framework\TestCase; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class UrlGeneratorTest extends TestCase { public function testAbsoluteUrlWithPort80() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL); $this->assertEquals('http://localhost/app.php/testing', $url); } public function testAbsoluteSecureUrlWithPort443() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes, ['scheme' => 'https'])->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL); $this->assertEquals('https://localhost/app.php/testing', $url); } public function testAbsoluteUrlWithNonStandardPort() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes, ['httpPort' => 8080])->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL); $this->assertEquals('http://localhost:8080/app.php/testing', $url); } public function testAbsoluteSecureUrlWithNonStandardPort() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes, ['httpsPort' => 8080, 'scheme' => 'https'])->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL); $this->assertEquals('https://localhost:8080/app.php/testing', $url); } public function testRelativeUrlWithoutParameters() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing', $url); } public function testRelativeUrlWithParameter() { $routes = $this->getRoutes('test', new Route('/testing/{foo}')); $url = $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing/bar', $url); } public function testRelativeUrlWithNullParameter() { $routes = $this->getRoutes('test', new Route('/testing.{format}', ['format' => null])); $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing', $url); } public function testRelativeUrlWithNullParameterButNotOptional() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/testing/{foo}/bar', ['foo' => null])); // This must raise an exception because the default requirement for "foo" is "[^/]+" which is not met with these params. // Generating path "/testing//bar" would be wrong as matching this route would fail. $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH); } public function testRelativeUrlWithOptionalZeroParameter() { $routes = $this->getRoutes('test', new Route('/testing/{page}')); $url = $this->getGenerator($routes)->generate('test', ['page' => 0], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing/0', $url); } public function testNotPassedOptionalParameterInBetween() { $routes = $this->getRoutes('test', new Route('/{slug}/{page}', ['slug' => 'index', 'page' => 0])); $this->assertSame('/app.php/index/1', $this->getGenerator($routes)->generate('test', ['page' => 1])); $this->assertSame('/app.php/', $this->getGenerator($routes)->generate('test')); } public function testRelativeUrlWithExtraParameters() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing?foo=bar', $url); } public function testAbsoluteUrlWithExtraParameters() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL); $this->assertEquals('http://localhost/app.php/testing?foo=bar', $url); } public function testUrlWithNullExtraParameters() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', ['foo' => null], UrlGeneratorInterface::ABSOLUTE_URL); $this->assertEquals('http://localhost/app.php/testing', $url); } public function testUrlWithExtraParametersFromGlobals() { $routes = $this->getRoutes('test', new Route('/testing')); $generator = $this->getGenerator($routes); $context = new RequestContext('/app.php'); $context->setParameter('bar', 'bar'); $generator->setContext($context); $url = $generator->generate('test', ['foo' => 'bar']); $this->assertEquals('/app.php/testing?foo=bar', $url); } public function testUrlWithGlobalParameter() { $routes = $this->getRoutes('test', new Route('/testing/{foo}')); $generator = $this->getGenerator($routes); $context = new RequestContext('/app.php'); $context->setParameter('foo', 'bar'); $generator->setContext($context); $url = $generator->generate('test', []); $this->assertEquals('/app.php/testing/bar', $url); } public function testGlobalParameterHasHigherPriorityThanDefault() { $routes = $this->getRoutes('test', new Route('/{_locale}', ['_locale' => 'en'])); $generator = $this->getGenerator($routes); $context = new RequestContext('/app.php'); $context->setParameter('_locale', 'de'); $generator->setContext($context); $url = $generator->generate('test', []); $this->assertSame('/app.php/de', $url); } public function testGenerateWithoutRoutes() { $this->expectException('Symfony\Component\Routing\Exception\RouteNotFoundException'); $routes = $this->getRoutes('foo', new Route('/testing/{foo}')); $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL); } public function testGenerateForRouteWithoutMandatoryParameter() { $this->expectException('Symfony\Component\Routing\Exception\MissingMandatoryParametersException'); $routes = $this->getRoutes('test', new Route('/testing/{foo}')); $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL); } public function testGenerateForRouteWithInvalidOptionalParameter() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+'])); $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL); } public function testGenerateForRouteWithInvalidParameter() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/testing/{foo}', [], ['foo' => '1|2'])); $this->getGenerator($routes)->generate('test', ['foo' => '0'], UrlGeneratorInterface::ABSOLUTE_URL); } public function testGenerateForRouteWithInvalidOptionalParameterNonStrict() { $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+'])); $generator = $this->getGenerator($routes); $generator->setStrictRequirements(false); $this->assertNull($generator->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testGenerateForRouteWithInvalidOptionalParameterNonStrictWithLogger() { $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+'])); $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock(); $logger->expects($this->once()) ->method('error'); $generator = $this->getGenerator($routes, [], $logger); $generator->setStrictRequirements(false); $this->assertNull($generator->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testGenerateForRouteWithInvalidParameterButDisabledRequirementsCheck() { $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+'])); $generator = $this->getGenerator($routes); $generator->setStrictRequirements(null); $this->assertSame('/app.php/testing/bar', $generator->generate('test', ['foo' => 'bar'])); } public function testGenerateForRouteWithInvalidMandatoryParameter() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/testing/{foo}', [], ['foo' => 'd+'])); $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL); } public function testGenerateForRouteWithInvalidUtf8Parameter() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/testing/{foo}', [], ['foo' => '\pL+'], ['utf8' => true])); $this->getGenerator($routes)->generate('test', ['foo' => 'abc123'], UrlGeneratorInterface::ABSOLUTE_URL); } public function testRequiredParamAndEmptyPassed() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/{slug}', [], ['slug' => '.+'])); $this->getGenerator($routes)->generate('test', ['slug' => '']); } public function testSchemeRequirementDoesNothingIfSameCurrentScheme() { $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['http'])); $this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test')); $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['https'])); $this->assertEquals('/app.php/', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test')); } public function testSchemeRequirementForcesAbsoluteUrl() { $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['https'])); $this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test')); $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['http'])); $this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test')); } public function testSchemeRequirementCreatesUrlForFirstRequiredScheme() { $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['Ftp', 'https'])); $this->assertEquals('ftp://localhost/app.php/', $this->getGenerator($routes)->generate('test')); } public function testPathWithTwoStartingSlashes() { $routes = $this->getRoutes('test', new Route('//path-and-not-domain')); // this must not generate '//path-and-not-domain' because that would be a network path $this->assertSame('/path-and-not-domain', $this->getGenerator($routes, ['BaseUrl' => ''])->generate('test')); } public function testNoTrailingSlashForMultipleOptionalParameters() { $routes = $this->getRoutes('test', new Route('/category/{slug1}/{slug2}/{slug3}', ['slug2' => null, 'slug3' => null])); $this->assertEquals('/app.php/category/foo', $this->getGenerator($routes)->generate('test', ['slug1' => 'foo'])); } public function testWithAnIntegerAsADefaultValue() { $routes = $this->getRoutes('test', new Route('/{default}', ['default' => 0])); $this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', ['default' => 'foo'])); } public function testNullForOptionalParameterIsIgnored() { $routes = $this->getRoutes('test', new Route('/test/{default}', ['default' => 0])); $this->assertEquals('/app.php/test', $this->getGenerator($routes)->generate('test', ['default' => null])); } public function testQueryParamSameAsDefault() { $routes = $this->getRoutes('test', new Route('/test', ['page' => 1])); $this->assertSame('/app.php/test?page=2', $this->getGenerator($routes)->generate('test', ['page' => 2])); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['page' => 1])); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['page' => '1'])); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test')); } public function testArrayQueryParamSameAsDefault() { $routes = $this->getRoutes('test', new Route('/test', ['array' => ['foo', 'bar']])); $this->assertSame('/app.php/test?array%5B0%5D=bar&array%5B1%5D=foo', $this->getGenerator($routes)->generate('test', ['array' => ['bar', 'foo']])); $this->assertSame('/app.php/test?array%5Ba%5D=foo&array%5Bb%5D=bar', $this->getGenerator($routes)->generate('test', ['array' => ['a' => 'foo', 'b' => 'bar']])); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['array' => ['foo', 'bar']])); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['array' => [1 => 'bar', 0 => 'foo']])); $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test')); } public function testGenerateWithSpecialRouteName() { $routes = $this->getRoutes('$péß^a|', new Route('/bar')); $this->assertSame('/app.php/bar', $this->getGenerator($routes)->generate('$péß^a|')); } public function testUrlEncoding() { $expectedPath = '/app.php/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id' .'/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id' .'?query=%40%3A%5B%5D/%28%29%2A%27%22%20%2B%2C%3B-._~%26%24%3C%3E%7C%7B%7D%25%5C%5E%60%21%3Ffoo%3Dbar%23id'; // This tests the encoding of reserved characters that are used for delimiting of URI components (defined in RFC 3986) // and other special ASCII chars. These chars are tested as static text path, variable path and query param. $chars = '@:[]/()*\'" +,;-._~&$<>|{}%\\^`!?foo=bar#id'; $routes = $this->getRoutes('test', new Route("/$chars/{varpath}", [], ['varpath' => '.+'])); $this->assertSame($expectedPath, $this->getGenerator($routes)->generate('test', [ 'varpath' => $chars, 'query' => $chars, ])); } public function testEncodingOfRelativePathSegments() { $routes = $this->getRoutes('test', new Route('/dir/../dir/..')); $this->assertSame('/app.php/dir/%2E%2E/dir/%2E%2E', $this->getGenerator($routes)->generate('test')); $routes = $this->getRoutes('test', new Route('/dir/./dir/.')); $this->assertSame('/app.php/dir/%2E/dir/%2E', $this->getGenerator($routes)->generate('test')); $routes = $this->getRoutes('test', new Route('/a./.a/a../..a/...')); $this->assertSame('/app.php/a./.a/a../..a/...', $this->getGenerator($routes)->generate('test')); } public function testAdjacentVariables() { $routes = $this->getRoutes('test', new Route('/{x}{y}{z}.{_format}', ['z' => 'default-z', '_format' => 'html'], ['y' => '\d+'])); $generator = $this->getGenerator($routes); $this->assertSame('/app.php/foo123', $generator->generate('test', ['x' => 'foo', 'y' => '123'])); $this->assertSame('/app.php/foo123bar.xml', $generator->generate('test', ['x' => 'foo', 'y' => '123', 'z' => 'bar', '_format' => 'xml'])); // The default requirement for 'x' should not allow the separator '.' in this case because it would otherwise match everything // and following optional variables like _format could never match. $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $generator->generate('test', ['x' => 'do.t', 'y' => '123', 'z' => 'bar', '_format' => 'xml']); } public function testOptionalVariableWithNoRealSeparator() { $routes = $this->getRoutes('test', new Route('/get{what}', ['what' => 'All'])); $generator = $this->getGenerator($routes); $this->assertSame('/app.php/get', $generator->generate('test')); $this->assertSame('/app.php/getSites', $generator->generate('test', ['what' => 'Sites'])); } public function testRequiredVariableWithNoRealSeparator() { $routes = $this->getRoutes('test', new Route('/get{what}Suffix')); $generator = $this->getGenerator($routes); $this->assertSame('/app.php/getSitesSuffix', $generator->generate('test', ['what' => 'Sites'])); } public function testDefaultRequirementOfVariable() { $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); $generator = $this->getGenerator($routes); $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', ['page' => 'index', '_format' => 'mobile.html'])); } public function testDefaultRequirementOfVariableDisallowsSlash() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); $this->getGenerator($routes)->generate('test', ['page' => 'index', '_format' => 'sl/ash']); } public function testDefaultRequirementOfVariableDisallowsNextSeparator() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/{page}.{_format}')); $this->getGenerator($routes)->generate('test', ['page' => 'do.t', '_format' => 'html']); } public function testWithHostDifferentFromContext() { $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com')); $this->assertEquals('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', ['name' => 'Fabien', 'locale' => 'fr'])); } public function testWithHostSameAsContext() { $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com')); $this->assertEquals('/app.php/Fabien', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test', ['name' => 'Fabien', 'locale' => 'fr'])); } public function testWithHostSameAsContextAndAbsolute() { $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com')); $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test', ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testUrlWithInvalidParameterInHost() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/', [], ['foo' => 'bar'], [], '{foo}.example.com')); $this->getGenerator($routes)->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH); } public function testUrlWithInvalidParameterInHostWhenParamHasADefaultValue() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/', ['foo' => 'bar'], ['foo' => 'bar'], [], '{foo}.example.com')); $this->getGenerator($routes)->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH); } public function testUrlWithInvalidParameterEqualsDefaultValueInHost() { $this->expectException('Symfony\Component\Routing\Exception\InvalidParameterException'); $routes = $this->getRoutes('test', new Route('/', ['foo' => 'baz'], ['foo' => 'bar'], [], '{foo}.example.com')); $this->getGenerator($routes)->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH); } public function testUrlWithInvalidParameterInHostInNonStrictMode() { $routes = $this->getRoutes('test', new Route('/', [], ['foo' => 'bar'], [], '{foo}.example.com')); $generator = $this->getGenerator($routes); $generator->setStrictRequirements(false); $this->assertNull($generator->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH)); } public function testHostIsCaseInsensitive() { $routes = $this->getRoutes('test', new Route('/', [], ['locale' => 'en|de|fr'], [], '{locale}.FooBar.com')); $generator = $this->getGenerator($routes); $this->assertSame('//EN.FooBar.com/app.php/', $generator->generate('test', ['locale' => 'EN'], UrlGeneratorInterface::NETWORK_PATH)); } public function testDefaultHostIsUsedWhenContextHostIsEmpty() { $routes = $this->getRoutes('test', new Route('/path', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}')); $generator = $this->getGenerator($routes); $generator->getContext()->setHost(''); $this->assertSame('http://my.fallback.host/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testDefaultHostIsUsedWhenContextHostIsEmptyAndPathReferenceType() { $routes = $this->getRoutes('test', new Route('/path', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}')); $generator = $this->getGenerator($routes); $generator->getContext()->setHost(''); $this->assertSame('//my.fallback.host/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH)); } public function testAbsoluteUrlFallbackToPathIfHostIsEmptyAndSchemeIsHttp() { $routes = $this->getRoutes('test', new Route('/route')); $generator = $this->getGenerator($routes); $generator->getContext()->setHost(''); $generator->getContext()->setScheme('https'); $this->assertSame('/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testAbsoluteUrlFallbackToNetworkIfSchemeIsEmptyAndHostIsNot() { $routes = $this->getRoutes('test', new Route('/path')); $generator = $this->getGenerator($routes); $generator->getContext()->setHost('example.com'); $generator->getContext()->setScheme(''); $this->assertSame('//example.com/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testAbsoluteUrlFallbackToPathIfSchemeAndHostAreEmpty() { $routes = $this->getRoutes('test', new Route('/path')); $generator = $this->getGenerator($routes); $generator->getContext()->setHost(''); $generator->getContext()->setScheme(''); $this->assertSame('/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testAbsoluteUrlWithNonHttpSchemeAndEmptyHost() { $routes = $this->getRoutes('test', new Route('/path', [], [], [], '', ['file'])); $generator = $this->getGenerator($routes); $generator->getContext()->setBaseUrl(''); $generator->getContext()->setHost(''); $this->assertSame('file:///path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL)); } public function testGenerateNetworkPath() { $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com', ['http'])); $this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::NETWORK_PATH), 'network path with different host' ); $this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test', ['name' => 'Fabien', 'locale' => 'fr', 'query' => 'string'], UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context' ); $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test', ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context' ); $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested' ); } public function testGenerateRelativePath() { $routes = new RouteCollection(); $routes->add('article', new Route('/{author}/{article}/')); $routes->add('comments', new Route('/{author}/{article}/comments')); $routes->add('host', new Route('/{article}', [], [], [], '{author}.example.com')); $routes->add('scheme', new Route('/{author}/blog', [], [], [], '', ['https'])); $routes->add('unrelated', new Route('/about')); $generator = $this->getGenerator($routes, ['host' => 'example.com', 'pathInfo' => '/fabien/symfony-is-great/']); $this->assertSame('comments', $generator->generate('comments', ['author' => 'fabien', 'article' => 'symfony-is-great'], UrlGeneratorInterface::RELATIVE_PATH) ); $this->assertSame('comments?page=2', $generator->generate('comments', ['author' => 'fabien', 'article' => 'symfony-is-great', 'page' => 2], UrlGeneratorInterface::RELATIVE_PATH) ); $this->assertSame('../twig-is-great/', $generator->generate('article', ['author' => 'fabien', 'article' => 'twig-is-great'], UrlGeneratorInterface::RELATIVE_PATH) ); $this->assertSame('../../bernhard/forms-are-great/', $generator->generate('article', ['author' => 'bernhard', 'article' => 'forms-are-great'], UrlGeneratorInterface::RELATIVE_PATH) ); $this->assertSame('//bernhard.example.com/app.php/forms-are-great', $generator->generate('host', ['author' => 'bernhard', 'article' => 'forms-are-great'], UrlGeneratorInterface::RELATIVE_PATH) ); $this->assertSame('https://example.com/app.php/bernhard/blog', $generator->generate('scheme', ['author' => 'bernhard'], UrlGeneratorInterface::RELATIVE_PATH) ); $this->assertSame('../../about', $generator->generate('unrelated', [], UrlGeneratorInterface::RELATIVE_PATH) ); } /** * @dataProvider provideRelativePaths */ public function testGetRelativePath($sourcePath, $targetPath, $expectedPath) { $this->assertSame($expectedPath, UrlGenerator::getRelativePath($sourcePath, $targetPath)); } public function provideRelativePaths() { return [ [ '/same/dir/', '/same/dir/', '', ], [ '/same/file', '/same/file', '', ], [ '/', '/file', 'file', ], [ '/', '/dir/file', 'dir/file', ], [ '/dir/file.html', '/dir/different-file.html', 'different-file.html', ], [ '/same/dir/extra-file', '/same/dir/', './', ], [ '/parent/dir/', '/parent/', '../', ], [ '/parent/dir/extra-file', '/parent/', '../', ], [ '/a/b/', '/x/y/z/', '../../x/y/z/', ], [ '/a/b/c/d/e', '/a/c/d', '../../../c/d', ], [ '/a/b/c//', '/a/b/c/', '../', ], [ '/a/b/c/', '/a/b/c//', './/', ], [ '/root/a/b/c/', '/root/x/b/c/', '../../../x/b/c/', ], [ '/a/b/c/d/', '/a', '../../../../a', ], [ '/special-chars/sp%20ce/1€/mäh/e=mc²', '/special-chars/sp%20ce/1€/<µ>/e=mc²', '../<µ>/e=mc²', ], [ 'not-rooted', 'dir/file', 'dir/file', ], [ '//dir/', '', '../../', ], [ '/dir/', '/dir/file:with-colon', './file:with-colon', ], [ '/dir/', '/dir/subdir/file:with-colon', 'subdir/file:with-colon', ], [ '/dir/', '/dir/:subdir/', './:subdir/', ], ]; } public function testFragmentsCanBeAppendedToUrls() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', ['_fragment' => 'frag ment'], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing#frag%20ment', $url); $url = $this->getGenerator($routes)->generate('test', ['_fragment' => '0'], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing#0', $url); } public function testFragmentsDoNotEscapeValidCharacters() { $routes = $this->getRoutes('test', new Route('/testing')); $url = $this->getGenerator($routes)->generate('test', ['_fragment' => '?/'], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing#?/', $url); } public function testFragmentsCanBeDefinedAsDefaults() { $routes = $this->getRoutes('test', new Route('/testing', ['_fragment' => 'fragment'])); $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH); $this->assertEquals('/app.php/testing#fragment', $url); } /** * @dataProvider provideLookAroundRequirementsInPath */ public function testLookRoundRequirementsInPath($expected, $path, $requirement) { $routes = $this->getRoutes('test', new Route($path, [], ['foo' => $requirement, 'baz' => '.+?'])); $this->assertSame($expected, $this->getGenerator($routes)->generate('test', ['foo' => 'a/b', 'baz' => 'c/d/e'])); } public function provideLookAroundRequirementsInPath() { yield ['/app.php/a/b/b%28ar/c/d/e', '/{foo}/b(ar/{baz}', '.+(?=/b\\(ar/)']; yield ['/app.php/a/b/bar/c/d/e', '/{foo}/bar/{baz}', '.+(?!$)']; yield ['/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?<=/bar/).+']; yield ['/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(? $value) { $method = 'set'.$key; $context->$method($value); } return new UrlGenerator($routes, $context, $logger); } protected function getRoutes($name, Route $route) { $routes = new RouteCollection(); $routes->add($name, $route); return $routes; } } PK~ZO+1Tests/Generator/Dumper/PhpGeneratorDumperTest.phpnu[ * * 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\Dumper\PhpGeneratorDumper; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; class PhpGeneratorDumperTest extends TestCase { /** * @var RouteCollection */ private $routeCollection; /** * @var PhpGeneratorDumper */ private $generatorDumper; /** * @var string */ private $testTmpFilepath; /** * @var string */ private $largeTestTmpFilepath; protected function setUp() { parent::setUp(); $this->routeCollection = new RouteCollection(); $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection); $this->testTmpFilepath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.php'; $this->largeTestTmpFilepath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'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()); include $this->testTmpFilepath; $projectUrlGenerator = new \ProjectUrlGenerator(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 testDumpWithTooManyRoutes() { if (\defined('HHVM_VERSION_ID')) { $this->markTestSkipped('HHVM consumes too much memory on this test.'); } $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([ 'class' => 'ProjectLargeUrlGenerator', ])); $this->routeCollection = $this->generatorDumper = null; include $this->largeTestTmpFilepath; $projectUrlGenerator = new \ProjectLargeUrlGenerator(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 testDumpWithoutRoutes() { $this->expectException('InvalidArgumentException'); file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'WithoutRoutesUrlGenerator'])); include $this->testTmpFilepath; $projectUrlGenerator = new \WithoutRoutesUrlGenerator(new RequestContext('/app.php')); $projectUrlGenerator->generate('Test', []); } public function testGenerateNonExistingRoute() { $this->expectException('Symfony\Component\Routing\Exception\RouteNotFoundException'); $this->routeCollection->add('Test', new Route('/test')); file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'NonExistingRoutesUrlGenerator'])); include $this->testTmpFilepath; $projectUrlGenerator = new \NonExistingRoutesUrlGenerator(new RequestContext()); $projectUrlGenerator->generate('NonExisting', []); } public function testDumpForRouteWithDefaults() { $this->routeCollection->add('Test', new Route('/testing/{foo}', ['foo' => 'bar'])); file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'DefaultRoutesUrlGenerator'])); include $this->testTmpFilepath; $projectUrlGenerator = new \DefaultRoutesUrlGenerator(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(['class' => 'SchemeUrlGenerator'])); include $this->testTmpFilepath; $projectUrlGenerator = new \SchemeUrlGenerator(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 \SchemeUrlGenerator(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); } } PK~ZQH&H&RouteCollectionBuilder.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing; use Symfony\Component\Config\Exception\FileLoaderLoadException; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\Config\Resource\ResourceInterface; /** * Helps add and import routes into a RouteCollection. * * @author Ryan Weaver */ class RouteCollectionBuilder { /** * @var Route[]|RouteCollectionBuilder[] */ private $routes = []; private $loader; private $defaults = []; private $prefix; private $host; private $condition; private $requirements = []; private $options = []; private $schemes; private $methods; private $resources = []; public function __construct(LoaderInterface $loader = null) { $this->loader = $loader; } /** * Import an external routing resource and returns the RouteCollectionBuilder. * * $routes->import('blog.yml', '/blog'); * * @param mixed $resource * @param string|null $prefix * @param string $type * * @return self * * @throws FileLoaderLoadException */ public function import($resource, $prefix = '/', $type = null) { /** @var RouteCollection[] $collection */ $collections = $this->load($resource, $type); // create a builder from the RouteCollection $builder = $this->createBuilder(); foreach ($collections as $collection) { if (null === $collection) { continue; } foreach ($collection->all() as $name => $route) { $builder->addRoute($route, $name); } foreach ($collection->getResources() as $resource) { $builder->addResource($resource); } } // mount into this builder $this->mount($prefix, $builder); return $builder; } /** * Adds a route and returns it for future modification. * * @param string $path The route path * @param string $controller The route's controller * @param string|null $name The name to give this route * * @return Route */ public function add($path, $controller, $name = null) { $route = new Route($path); $route->setDefault('_controller', $controller); $this->addRoute($route, $name); return $route; } /** * Returns a RouteCollectionBuilder that can be configured and then added with mount(). * * @return self */ public function createBuilder() { return new self($this->loader); } /** * Add a RouteCollectionBuilder. * * @param string $prefix * @param RouteCollectionBuilder $builder */ public function mount($prefix, self $builder) { $builder->prefix = trim(trim($prefix), '/'); $this->routes[] = $builder; } /** * Adds a Route object to the builder. * * @param string|null $name * * @return $this */ public function addRoute(Route $route, $name = null) { if (null === $name) { // used as a flag to know which routes will need a name later $name = '_unnamed_route_'.spl_object_hash($route); } $this->routes[$name] = $route; return $this; } /** * Sets the host on all embedded routes (unless already set). * * @param string $pattern * * @return $this */ public function setHost($pattern) { $this->host = $pattern; return $this; } /** * Sets a condition on all embedded routes (unless already set). * * @param string $condition * * @return $this */ public function setCondition($condition) { $this->condition = $condition; return $this; } /** * Sets a default value that will be added to all embedded routes (unless that * default value is already set). * * @param string $key * @param mixed $value * * @return $this */ public function setDefault($key, $value) { $this->defaults[$key] = $value; return $this; } /** * Sets a requirement that will be added to all embedded routes (unless that * requirement is already set). * * @param string $key * @param mixed $regex * * @return $this */ public function setRequirement($key, $regex) { $this->requirements[$key] = $regex; return $this; } /** * Sets an option that will be added to all embedded routes (unless that * option is already set). * * @param string $key * @param mixed $value * * @return $this */ public function setOption($key, $value) { $this->options[$key] = $value; return $this; } /** * Sets the schemes on all embedded routes (unless already set). * * @param array|string $schemes * * @return $this */ public function setSchemes($schemes) { $this->schemes = $schemes; return $this; } /** * Sets the methods on all embedded routes (unless already set). * * @param array|string $methods * * @return $this */ public function setMethods($methods) { $this->methods = $methods; return $this; } /** * Adds a resource for this collection. * * @return $this */ private function addResource(ResourceInterface $resource) { $this->resources[] = $resource; return $this; } /** * Creates the final RouteCollection and returns it. * * @return RouteCollection */ public function build() { $routeCollection = new RouteCollection(); foreach ($this->routes as $name => $route) { if ($route instanceof Route) { $route->setDefaults(array_merge($this->defaults, $route->getDefaults())); $route->setOptions(array_merge($this->options, $route->getOptions())); foreach ($this->requirements as $key => $val) { if (!$route->hasRequirement($key)) { $route->setRequirement($key, $val); } } if (null !== $this->prefix) { $route->setPath('/'.$this->prefix.$route->getPath()); } if (!$route->getHost()) { $route->setHost($this->host); } if (!$route->getCondition()) { $route->setCondition($this->condition); } if (!$route->getSchemes()) { $route->setSchemes($this->schemes); } if (!$route->getMethods()) { $route->setMethods($this->methods); } // auto-generate the route name if it's been marked if ('_unnamed_route_' === substr($name, 0, 15)) { $name = $this->generateRouteName($route); } $routeCollection->add($name, $route); } else { /* @var self $route */ $subCollection = $route->build(); $subCollection->addPrefix($this->prefix); $routeCollection->addCollection($subCollection); } } foreach ($this->resources as $resource) { $routeCollection->addResource($resource); } return $routeCollection; } /** * Generates a route name based on details of this route. * * @return string */ private function generateRouteName(Route $route) { $methods = implode('_', $route->getMethods()).'_'; $routeName = $methods.$route->getPath(); $routeName = str_replace(['/', ':', '|', '-'], '_', $routeName); $routeName = preg_replace('/[^a-z0-9A-Z_.]+/', '', $routeName); // Collapse consecutive underscores down into a single underscore. $routeName = preg_replace('/_+/', '_', $routeName); return $routeName; } /** * Finds a loader able to load an imported resource and loads it. * * @param mixed $resource A resource * @param string|null $type The resource type or null if unknown * * @return RouteCollection[] * * @throws FileLoaderLoadException If no loader is found */ private function load($resource, $type = null) { if (null === $this->loader) { throw new \BadMethodCallException('Cannot import other routing resources: you must pass a LoaderInterface when constructing RouteCollectionBuilder.'); } if ($this->loader->supports($resource, $type)) { $collections = $this->loader->load($resource, $type); return \is_array($collections) ? $collections : [$collections]; } if (null === $resolver = $this->loader->getResolver()) { throw new FileLoaderLoadException($resource, null, null, null, $type); } if (false === $loader = $resolver->resolve($resource, $type)) { throw new FileLoaderLoadException($resource, null, null, null, $type); } $collections = $loader->load($resource, $type); return \is_array($collections) ? $collections : [$collections]; } } PK~Ze^^2Loader/DependencyInjection/ServiceRouterLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader\DependencyInjection; use Psr\Container\ContainerInterface; use Symfony\Component\Routing\Loader\ObjectRouteLoader; /** * A route loader that executes a service to load the routes. * * @author Ryan Weaver */ class ServiceRouterLoader extends ObjectRouteLoader { /** * @var ContainerInterface */ private $container; public function __construct(ContainerInterface $container) { $this->container = $container; } protected function getServiceObject($id) { return $this->container->get($id); } } PK~Z>sXOW W Loader/ObjectRouteLoader.phpnu[ * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Loader; use Symfony\Component\Config\Loader\Loader; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Routing\RouteCollection; /** * A route loader that calls a method on an object to load the routes. * * @author Ryan Weaver */ abstract class ObjectRouteLoader extends Loader { /** * Returns the object that the method will be called on to load routes. * * For example, if your application uses a service container, * the $id may be a service id. * * @param string $id * * @return object */ abstract protected function getServiceObject($id); /** * Calls the service that will load the routes. * * @param mixed $resource Some value that will resolve to a callable * @param string|null $type The resource type * * @return RouteCollection */ public function load($resource, $type = null) { $parts = explode(':', $resource); if (2 != \count($parts)) { throw new \InvalidArgumentException(sprintf('Invalid resource "%s" passed to the "service" route loader: use the format "service_name:methodName".', $resource)); } $serviceString = $parts[0]; $method = $parts[1]; $loaderObject = $this->getServiceObject($serviceString); if (!\is_object($loaderObject)) { throw new \LogicException(sprintf('"%s:getServiceObject()" must return an object: "%s" returned.', static::class, \gettype($loaderObject))); } if (!method_exists($loaderObject, $method)) { throw new \BadMethodCallException(sprintf('Method "%s" not found on "%s" when importing routing resource "%s".', $method, \get_class($loaderObject), $resource)); } $routeCollection = \call_user_func([$loaderObject, $method], $this); if (!$routeCollection instanceof RouteCollection) { $type = \is_object($routeCollection) ? \get_class($routeCollection) : \gettype($routeCollection); throw new \LogicException(sprintf('The "%s::%s()" method must return a RouteCollection: "%s" returned.', \get_class($loaderObject), $method, $type)); } // make the service file tracked so that if it changes, the cache rebuilds $this->addClassResource(new \ReflectionClass($loaderObject), $routeCollection); return $routeCollection; } /** * {@inheritdoc} */ public function supports($resource, $type = null) { return 'service' === $type; } private function addClassResource(\ReflectionClass $class, RouteCollection $collection) { do { if (is_file($class->getFileName())) { $collection->addResource(new FileResource($class->getFileName())); } } while ($class = $class->getParentClass()); } } PK~Zb' #Matcher/Dumper/DumperCollection.phpnu[ * * 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 * * @internal */ class DumperCollection implements \IteratorAggregate { /** * @var DumperCollection|null */ private $parent; /** * @var DumperCollection[]|DumperRoute[] */ private $children = []; /** * @var array */ private $attributes = []; /** * 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(self $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; } } PK~Zno ; ;#Matcher/Dumper/PhpMatcherDumper.phpnu[ * * 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 * @author Tobias Schultze * @author Arnaud Le Blanc */ class PhpMatcherDumper extends MatcherDumper { private $expressionLanguage; /** * @var ExpressionFunctionProviderInterface[] */ private $expressionLanguageProviders = []; /** * 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 = []) { $options = array_replace([ '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 <<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 <<context; \$request = \$this->request ?: \$this->createRequest(\$pathinfo); \$requestMethod = \$canonicalMethod = \$context->getMethod(); if ('HEAD' === \$requestMethod) { \$canonicalMethod = 'GET'; } $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 = \$context->getHost();\n\n"; $fetchedHost = true; } $code .= sprintf(" if (preg_match(%s, \$host, \$hostMatches)) {\n", var_export($regex, true)); } $tree = $this->buildStaticPrefixCollection($collection); $groupCode = $this->compileStaticPrefixRoutes($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; } } // used to display the Welcome Page in apps that don't define a homepage $code .= " if ('/' === \$pathinfo && !\$allow) {\n"; $code .= " throw new Symfony\Component\Routing\Exception\NoConfigurationException();\n"; $code .= " }\n"; return $code; } private function buildStaticPrefixCollection(DumperCollection $collection) { $prefixCollection = new StaticPrefixCollection(); foreach ($collection as $dumperRoute) { $prefix = $dumperRoute->getRoute()->compile()->getStaticPrefix(); $prefixCollection->addRoute($prefix, $dumperRoute); } $prefixCollection->optimizeGroups(); return $prefixCollection; } /** * Generates PHP code to match a tree of routes. * * @param StaticPrefixCollection $collection A StaticPrefixCollection instance * @param bool $supportsRedirections Whether redirections are supported by the base class * @param string $ifOrElseIf either "if" or "elseif" to influence chaining * * @return string PHP code */ private function compileStaticPrefixRoutes(StaticPrefixCollection $collection, $supportsRedirections, $ifOrElseIf = 'if') { $code = ''; $prefix = $collection->getPrefix(); if (!empty($prefix) && '/' !== $prefix) { $code .= sprintf(" %s (0 === strpos(\$pathinfo, %s)) {\n", $ifOrElseIf, var_export($prefix, true)); } $ifOrElseIf = 'if'; foreach ($collection->getItems() as $route) { if ($route instanceof StaticPrefixCollection) { $code .= $this->compileStaticPrefixRoutes($route, $supportsRedirections, $ifOrElseIf); $ifOrElseIf = 'elseif'; } else { $code .= $this->compileRoute($route[1]->getRoute(), $route[1]->getName(), $supportsRedirections, $prefix)."\n"; $ifOrElseIf = 'if'; } } if (!empty($prefix) && '/' !== $prefix) { $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 = []; $hasTrailingSlash = false; $matches = false; $hostMatches = false; $methods = $route->getMethods(); $supportsTrailingSlash = $supportsRedirections && (!$methods || \in_array('GET', $methods)); $regex = $compiledRoute->getRegex(); if (!\count($compiledRoute->getPathVariables()) && false !== preg_match('#^(.)\^(?P.*?)\$\1#'.('u' === substr($regex, -1) ? 'u' : ''), $regex, $m)) { if ($supportsTrailingSlash && '/' === substr($m['url'], -1)) { $conditions[] = sprintf('%s === $trimmedPathinfo', 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)); } 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(), ['context', 'request']); } $conditions = implode(' && ', $conditions); $code .= <<mergeDefaults(array_replace(%s), %s);\n", implode(', ', $vars), str_replace("\n", '', var_export($route->getDefaults(), true)) ); } elseif ($route->getDefaults()) { $code .= sprintf(" \$ret = %s;\n", str_replace("\n", '', var_export(array_replace($route->getDefaults(), ['_route' => $name]), true))); } else { $code .= sprintf(" \$ret = ['_route' => '%s'];\n", $name); } if ($hasTrailingSlash) { $code .= <<redirect(\$rawPathinfo.'/', '$name')); } EOF; } if ($methods) { $methodVariable = \in_array('GET', $methods) ? '$canonicalMethod' : '$requestMethod'; $methods = implode("', '", $methods); } 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) { $code .= <<getScheme()]); if (!in_array($methodVariable, ['$methods'])) { if (\$hasRequiredScheme) { \$allow = array_merge(\$allow, ['$methods']); } goto $gotoname; } if (!\$hasRequiredScheme) { if ('GET' !== \$canonicalMethod) { goto $gotoname; } return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes))); } EOF; } else { $code .= <<getScheme()])) { if ('GET' !== \$canonicalMethod) { goto $gotoname; } return array_replace(\$ret, \$this->redirect(\$rawPathinfo, '$name', key(\$requiredSchemes))); } EOF; } } elseif ($methods) { $code .= <<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; } 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; } } PK~Zz2rMatcher/Dumper/DumperRoute.phpnu[ * * 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 * * @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; } } PK~ZМ 'Generator/Dumper/PhpGeneratorDumper.phpnu[ * * 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; /** * PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes. * * @author Fabien Potencier * @author Tobias Schultze */ class PhpGeneratorDumper extends GeneratorDumper { /** * 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 generator class */ public function dump(array $options = []) { $options = array_merge([ 'class' => 'ProjectUrlGenerator', 'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', ], $options); return <<context = \$context; \$this->logger = \$logger; if (null === self::\$declaredRoutes) { self::\$declaredRoutes = {$this->generateDeclaredRoutes()}; } } {$this->generateGenerateMethod()} } EOF; } /** * Generates PHP code representing an array of defined routes * together with the routes properties (e.g. requirements). * * @return string PHP code */ private function generateDeclaredRoutes() { $routes = "[\n"; foreach ($this->getRoutes()->all() as $name => $route) { $compiledRoute = $route->compile(); $properties = []; $properties[] = $compiledRoute->getVariables(); $properties[] = $route->getDefaults(); $properties[] = $route->getRequirements(); $properties[] = $compiledRoute->getTokens(); $properties[] = $compiledRoute->getHostTokens(); $properties[] = $route->getSchemes(); $routes .= sprintf(" '%s' => %s,\n", $name, str_replace("\n", '', var_export($properties, true))); } $routes .= ' ]'; return $routes; } /** * Generates PHP code representing the `generate` method that implements the UrlGeneratorInterface. * * @return string PHP code */ private function generateGenerateMethod() { return <<<'EOF' public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) { if (!isset(self::$declaredRoutes[$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) = self::$declaredRoutes[$name]; return $this->doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $referenceType, $hostTokens, $requiredSchemes); } EOF; } } PK[ZZcppRouteCollection.phpnu[PK[ZZ堻RequestContext.phpnu[PK[ZZv=R+~9DependencyInjection/RoutingResolverPass.phpnu[PK[ZZk  ?RouteCompilerInterface.phpnu[PK[ZZ^ Bcomposer.jsonnu[PK[ZZx$$ @ICHANGELOG.mdnu[PK[ZZDf..-nException/RouteCircularReferenceException.phpnu[PK[ZZ/nE$$'pException/ResourceNotFoundException.phpnu[PK[ZZ4221sException/MissingMandatoryParametersException.phpnu[PK[ZZ($>'uException/MethodNotAllowedException.phpnu[PK[ZZѺ7$zException/RouteNotFoundException.phpnu[PK[ZZds&c|Exception/InvalidArgumentException.phpnu[PK[ZZ";C :~Exception/ExceptionInterface.phpnu[PK[ZZ&%Exception/NoConfigurationException.phpnu[PK[ZZc1'LException/InvalidParameterException.phpnu[PK[ZZўqqException/RuntimeException.phpnu[PK[ZZ.. \RequestContextAwareInterface.phpnu[PK[ZZ,; ; ڈAnnotation/Route.phpnu[PK[ZZ RU  YAlias.phpnu[PK[ZZ8W55 Route.phpnu[PK[ZZ. README.mdnu[PK[ZZ{K}66RouteCompiler.phpnu[PK[ZZERouterInterface.phpnu[PK[ZZ*M CLoader/ContainerLoader.phpnu[PK[ZZ  $Loader/AnnotationDirectoryLoader.phpnu[PK[ZZ`% % %Loader/ObjectLoader.phpnu[PK[ZZva]]a1Loader/AnnotationFileLoader.phpnu[PK[ZZ X DLoader/DirectoryLoader.phpnu[PK[ZZU[ -))3JLoader/ClosureLoader.phpnu[PK[ZZ(Ck#k#NLoader/YamlFileLoader.phpnu[PK[ZZ%XrLoader/PhpFileLoader.phpnu[PK[ZZȒ4s4s4mzLoader/XmlFileLoader.phpnu[PK[ZZ'@i"" (Loader/AnnotationClassLoader.phpnu[PK[ZZfL%PLoader/schema/routing/routing-1.0.xsdnu[PK[ZZMn.'Loader/Configurator/CollectionConfigurator.phpnu[PK[ZZ hh)Loader/Configurator/RouteConfigurator.phpnu[PK[ZZ iG*Loader/Configurator/ImportConfigurator.phpnu[PK[ZZ{||+ Loader/Configurator/RoutingConfigurator.phpnu[PK[ZZA(Loader/Configurator/Traits/HostTrait.phpnu[PK[ZZk k *Loader/Configurator/Traits/PrefixTrait.phpnu[PK[ZZ}b )Loader/Configurator/Traits/RouteTrait.phpnu[PK[ZZYW\ 2Loader/Configurator/Traits/LocalizedRouteTrait.phpnu[PK[ZZ^x'W$Loader/Configurator/Traits/AddTrait.phpnu[PK[ZZ l))Loader/Configurator/AliasConfigurator.phpnu[PK[ZZO.Loader/GlobFileLoader.phpnu[PK[ZZ=))3LICENSEnu[PK[ZZ y7CompiledRoute.phpnu[PK[ZZڙi]]HMatcher/CompiledUrlMatcher.phpnu[PK[ZZGG+kLMatcher/RedirectableUrlMatcherInterface.phpnu[PK[ZZ璄## PMatcher/UrlMatcher.phpnu[PK[ZZ%&9tMatcher/ExpressionLanguageProvider.phpnu[PK[ZZh#6{Matcher/RequestMatcherInterface.phpnu[PK[ZZ Matcher/UrlMatcherInterface.phpnu[PK[ZZ[}"fMatcher/RedirectableUrlMatcher.phpnu[PK[ZZ6)Matcher/Dumper/StaticPrefixCollection.phpnu[PK[ZZ*Matcher/Dumper/CompiledUrlMatcherTrait.phpnu[PK[ZZ#R Matcher/Dumper/MatcherDumper.phpnu[PK[ZZtۃJJ+5Matcher/Dumper/CompiledUrlMatcherDumper.phpnu[PK[ZZg)Matcher/Dumper/MatcherDumperInterface.phpnu[PK[ZZw_SSCMatcher/TraceableUrlMatcher.phpnu[PK[ZZy "3Generator/CompiledUrlGenerator.phpnu[PK[ZZiO1l5l5(>Generator/UrlGenerator.phpnu[PK[ZZoff#sGenerator/UrlGeneratorInterface.phpnu[PK[ZZΑC  $Generator/Dumper/GeneratorDumper.phpnu[PK[ZZ-Generator/Dumper/GeneratorDumperInterface.phpnu[PK[ZZ#/.Generator/Dumper/CompiledUrlGeneratorDumper.phpnu[PK[ZZ!/%Generator/ConfigurableRequirementsInterface.phpnu[PK[ZZNI_}1}1 XRouter.phpnu[PK~Z BBphpunit.xml.distnu[PK~ZU"" .gitignorenu[PK~Zj5Tests/DependencyInjection/RoutingResolverPassTest.phpnu[PK~ZdOfI4I4Tests/RouteTest.phpnu[PK~ZTests/RequestContextTest.phpnu[PK~Z e6e6(Tests/RouteCompilerTest.phpnu[PK~ZsIS(!|_Tests/Fixtures/localized/utf8.xmlnu[PK~Z<\ ?aTests/Fixtures/map_defaults.xmlnu[PK~Z;,FSS,dTests/Fixtures/with_define_path_variable.phpnu[PK~Z{ keTests/Fixtures/validresource.phpnu[PK~ZgTests/Fixtures/foo1.xmlnu[PK~ZgTests/Fixtures/bar.xmlnu[PK~Z.ĉ6hTests/Fixtures/OtherAnnotatedClasses/VariadicClass.phpnu[PK~Z8jTests/Fixtures/OtherAnnotatedClasses/NoStartTagClass.phpnu[PK~Z˳>jTests/Fixtures/OtherAnnotatedClasses/AnonymousClassInTrait.phpnu[PK~Zx' 11lTests/Fixtures/withdoctype.xmlnu[PK~ZT,[mTests/Fixtures/AnnotatedClasses/BarClass.phpnu[PK~Z|rr,coTests/Fixtures/AnnotatedClasses/BazClass.phpnu[PK~Za``11qTests/Fixtures/AnnotatedClasses/EncodingClass.phpnu[PK~Z81&rTests/Fixtures/AnnotatedClasses/AbstractClass.phpnu[PK~Zq,vtTests/Fixtures/AnnotatedClasses/FooTrait.phpnu[PK~ZGG,uTests/Fixtures/AnnotatedClasses/FooClass.phpnu[PK~ZϮBB0wTests/Fixtures/incomplete.ymlnu[PK~Zcr  &wTests/Fixtures/map_in_map_defaults.xmlnu[PK~Z}2  {Tests/Fixtures/php_dsl.phpnu[PK~Zsf00}Tests/Fixtures/missing_id.xmlnu[PK~Z&,#Tests/Fixtures/list_null_values.xmlnu[PK~Zf,7Tests/Fixtures/directory/recurse/routes1.ymlnu[PK~ZTests/Fixtures/annotated.phpnu[PK~Zj`koo&Tests/Fixtures/CustomXmlFileLoader.phpnu[PK~Z Tests/Fixtures/file_resource.ymlnu[PK~Z}8Ɯ &Tests/Fixtures/dumper/url_matcher4.phpnu[PK~Z!^&Tests/Fixtures/dumper/url_matcher5.phpnu[PK~Zq4$$&ZTests/Fixtures/dumper/url_matcher6.phpnu[PK~Z J&Tests/Fixtures/dumper/url_matcher3.phpnu[PK~Z*?44&Tests/Fixtures/dumper/url_matcher2.phpnu[PK~ZU+U+&ATests/Fixtures/dumper/url_matcher7.phpnu[PK~Z &<Tests/Fixtures/dumper/url_matcher0.phpnu[PK~ZP++&ATests/Fixtures/dumper/url_matcher1.phpnu[PK~Zj??"mTests/Fixtures/namespaceprefix.xmlnu[PK~Zq992IqTests/Fixtures/nonesense_type_without_resource.ymlnu[PK~Z==qTests/Fixtures/nonvalidkeys.ymlnu[PK~ZaprTests/Fixtures/validpattern.ymlnu[PK~ZUStTests/Fixtures/null_values.xmlnu[PK~Z9v= vTests/Fixtures/validresource.xmlnu[PK~Z'yTests/Fixtures/map_in_list_defaults.xmlnu[PK~Zw|Tests/Fixtures/foo.xmlnu[PK~Z/$>>/|Tests/Fixtures/nonesense_resource_plus_path.ymlnu[PK~ZUAؿZ}Tests/Fixtures/validpattern.xmlnu[PK~ZE(hTests/Fixtures/list_in_list_defaults.xmlnu[PK~ZJ?%Tests/Fixtures/special_route_name.ymlnu[PK~Z B#'Tests/Fixtures/list_in_map_defaults.xmlnu[PK~Z)":Tests/Fixtures/scalar_defaults.xmlnu[PK~ZLNN)Tests/Fixtures/RedirectableUrlMatcher.phpnu[PK~Z$Vzz/ Tests/Fixtures/controller/override_defaults.ymlnu[PK~Z_y;%Tests/Fixtures/controller/routing.xmlnu[PK~Z:kk/?Tests/Fixtures/controller/import_controller.xmlnu[PK~ZEhh0 Tests/Fixtures/controller/import__controller.ymlnu[PK~Z@=L6єTests/Fixtures/controller/import_override_defaults.xmlnu[PK~ZP.0Tests/Fixtures/controller/import__controller.xmlnu[PK~Z}UU/Tests/Fixtures/controller/import_controller.ymlnu[PK~Z:T%Tests/Fixtures/controller/routing.ymlnu[PK~Z>܎6Tests/Fixtures/controller/import_override_defaults.ymlnu[PK~Zy/Tests/Fixtures/controller/override_defaults.xmlnu[PK~ZĮwTests/Fixtures/nonvalid.xmlnu[PK~ZHH"Tests/Fixtures/map_null_values.xmlnu[PK~Z%"Tests/Fixtures/glob/import_single.ymlnu[PK~Z y#Tests/Fixtures/glob/php_dsl_bar.phpnu[PK~ZkggTests/Fixtures/glob/bar.xmlnu[PK~Zt@@'Tests/Fixtures/glob/import_multiple.xmlnu[PK~Z:_PPPTests/Fixtures/glob/baz.ymlnu[PK~Z)Tests/Fixtures/glob/php_dsl.phpnu[PK~Z#ݩTests/Fixtures/glob/php_dsl_baz.phpnu[PK~Z`6/ggNTests/Fixtures/glob/baz.xmlnu[PK~ZӏD'Tests/Fixtures/glob/import_multiple.ymlnu[PK~ZbX@@%vTests/Fixtures/glob/import_single.xmlnu[PK~Z'dPP Tests/Fixtures/glob/bar.ymlnu[PK~Z&Tests/Fixtures/CustomCompiledRoute.phpnu[PK~ZPVTests/Fixtures/validpattern.phpnu[PK~ZjX Tests/Fixtures/validresource.ymlnu[PK~Z00ԴTests/Fixtures/missing_path.xmlnu[PK~Zdt WDD&STests/Fixtures/CustomRouteCompiler.phpnu[PK~Z}""Tests/Fixtures/nonvalidnode.xmlnu[PK~Z4" ^Tests/Fixtures/nonvalidroute.xmlnu[PK~Ze2~Tests/Fixtures/nonvalid.ymlnu[PK~ZMtTests/Fixtures/nonvalid2.ymlnu[PK~ZZ==*?Tests/Fixtures/directory_import/import.ymlnu[PK~ZtzֽTests/Fixtures/php_dsl_sub.phpnu[PK~Z Ny TTBTests/Fixtures/bad_format.ymlnu[PK~Z|( Tests/Fixtures/list_defaults.xmlnu[PK~ZTests/Fixtures/empty.ymlnu[PK~ZQ|ӰTests/Annotation/RouteTest.phpnu[PK~Z=v8 Tests/RouterTest.phpnu[PK~Z?;, #Tests/Loader/YamlFileLoaderTest.phpnu[PK~Z:o3o3*Tests/Loader/AnnotationClassLoaderTest.phpnu[PK~ZQqF"1Tests/Loader/PhpFileLoaderTest.phpnu[PK~Z"GTests/Loader/ClosureLoaderTest.phpnu[PK~Z vG G $MTests/Loader/DirectoryLoaderTest.phpnu[PK~Z4 .tWTests/Loader/AnnotationDirectoryLoaderTest.phpnu[PK~Zƥ#eTests/Loader/GlobFileLoaderTest.phpnu[PK~Zx==-kTests/Loader/AbstractAnnotationLoaderTest.phpnu[PK~Z=͎۬ )xoTests/Loader/AnnotationFileLoaderTest.phpnu[PK~Z"t t &}|Tests/Loader/ObjectRouteLoaderTest.phpnu[PK~Z3=ir:r:"GTests/Loader/XmlFileLoaderTest.phpnu[PK~Z^s.. Tests/CompiledRouteTest.phpnu[PK~ZF)S6S6Tests/RouteCollectionTest.phpnu[PK~Z!&zz2$Tests/Matcher/DumpedRedirectableUrlMatcherTest.phpnu[PK~Zl=,Tests/Matcher/RedirectableUrlMatcherTest.phpnu[PK~ZӔAaAa ATests/Matcher/UrlMatcherTest.phpnu[PK~Z::){Tests/Matcher/TraceableUrlMatcherTest.phpnu[PK~Zţ&eTests/Matcher/DumpedUrlMatcherTest.phpnu[PK~ZC3^Tests/Matcher/Dumper/StaticPrefixCollectionTest.phpnu[PK~ZABB-ԦTests/Matcher/Dumper/PhpMatcherDumperTest.phpnu[PK~ZJ-BTests/Matcher/Dumper/DumperCollectionTest.phpnu[PK~Z݃;;$Tests/RouteCollectionBuilderTest.phpnu[PK~ZS$'Tests/Generator/UrlGeneratorTest.phpnu[PK~ZO+1ITests/Generator/Dumper/PhpGeneratorDumperTest.phpnu[PK~ZQH&H&RouteCollectionBuilder.phpnu[PK~Ze^^2#Loader/DependencyInjection/ServiceRouterLoader.phpnu[PK~Z>sXOW W Loader/ObjectRouteLoader.phpnu[PK~Zb' #Matcher/Dumper/DumperCollection.phpnu[PK~Zno ; ;#a Matcher/Dumper/PhpMatcherDumper.phpnu[PK~Zz2rF Matcher/Dumper/DumperRoute.phpnu[PK~ZМ 'K Generator/Dumper/PhpGeneratorDumper.phpnu[PKJIY