One Hat Cyber Team
Your IP:
216.73.216.30
Server IP:
198.54.114.155
Server:
Linux server71.web-hosting.com 4.18.0-513.18.1.lve.el8.x86_64 #1 SMP Thu Feb 22 12:55:50 UTC 2024 x86_64
Server Software:
LiteSpeed
PHP Version:
5.6.40
Create File
|
Create Folder
Execute
Dir :
~
/
proc
/
self
/
cwd
/
View File Name :
Matcher.tar
TraceableUrlMatcher.php 0000644 00000015465 15111166024 0011136 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\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 <fabien@symfony.com> */ class TraceableUrlMatcher extends UrlMatcher { public const ROUTE_DOES_NOT_MATCH = 0; public const ROUTE_ALMOST_MATCHES = 1; public const ROUTE_MATCHES = 2; protected $traces; /** * @return array */ public function getTraces(string $pathinfo) { $this->traces = []; try { $this->match($pathinfo); } catch (ExceptionInterface) { } return $this->traces; } /** * @return array */ public function getTracesForRequest(Request $request) { $this->request = $request; $traces = $this->getTraces($request->getPathInfo()); $this->request = null; return $traces; } protected function matchCollection(string $pathinfo, RouteCollection $routes): array { // HEAD and GET are equivalent as per RFC if ('HEAD' === $method = $this->context->getMethod()) { $method = 'GET'; } $supportsTrailingSlash = 'GET' === $method && $this instanceof RedirectableUrlMatcherInterface; $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/'; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); $staticPrefix = rtrim($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 && !str_starts_with($trimmedPathinfo, $staticPrefix)) { $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route); continue; } $regex = $compiledRoute->getRegex(); $pos = strrpos($regex, '$'); $hasTrailingSlash = '/' === $regex[$pos - 1]; $regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash); 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; } $hasTrailingVar = $trimmedPathinfo !== $pathinfo && preg_match('#\{[\w\x80-\xFF]+\}/?$#', $route->getPath()); if ($hasTrailingVar && ($hasTrailingSlash || (null === $m = $matches[\count($compiledRoute->getPathVariables())] ?? null) || '/' !== ($m[-1] ?? '/')) && preg_match($regex, $trimmedPathinfo, $m)) { if ($hasTrailingSlash) { $matches = $m; } else { $hasTrailingVar = false; } } $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; } $attributes = $this->getAttributes($route, $name, array_replace($matches, $hostMatches)); $status = $this->handleRouteRequirements($pathinfo, $name, $route, $attributes); if (self::REQUIREMENT_MISMATCH === $status[0]) { $this->addTrace(sprintf('Condition "%s" does not evaluate to "true"', $route->getCondition()), self::ROUTE_ALMOST_MATCHES, $name, $route); continue; } if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) { if ($supportsTrailingSlash && (!$requiredMethods || \in_array('GET', $requiredMethods))) { $this->addTrace('Route matches!', self::ROUTE_MATCHES, $name, $route); return $this->allow = $this->allowSchemes = []; } $this->addTrace(sprintf('Path "%s" does not match', $route->getPath()), self::ROUTE_DOES_NOT_MATCH, $name, $route); continue; } if ($route->getSchemes() && !$route->hasScheme($this->context->getScheme())) { $this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes()); $this->addTrace(sprintf('Scheme "%s" does not match any of the required schemes (%s)', $this->context->getScheme(), implode(', ', $route->getSchemes())), self::ROUTE_ALMOST_MATCHES, $name, $route); continue; } if ($requiredMethods && !\in_array($method, $requiredMethods)) { $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 array_replace($attributes, $status[1] ?? []); } return []; } private function addTrace(string $log, int $level = self::ROUTE_DOES_NOT_MATCH, string $name = null, Route $route = null): void { $this->traces[] = [ 'log' => $log, 'name' => $name, 'level' => $level, 'path' => $route?->getPath(), ]; } } UrlMatcherInterface.php 0000644 00000002532 15111166025 0011144 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\Routing\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 <fabien@symfony.com> */ interface UrlMatcherInterface extends RequestContextAwareInterface { /** * Tries to match a URL path with a set of routes. * * If the matcher cannot 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) * * @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(string $pathinfo): array; } CompiledUrlMatcher.php 0000644 00000001535 15111166025 0011002 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherTrait; use Symfony\Component\Routing\RequestContext; /** * Matches URLs based on rules dumped by CompiledUrlMatcherDumper. * * @author Nicolas Grekas <p@tchwork.com> */ class CompiledUrlMatcher extends UrlMatcher { use CompiledUrlMatcherTrait; public function __construct(array $compiledRoutes, RequestContext $context) { $this->context = $context; [$this->matchHost, $this->staticRoutes, $this->regexpList, $this->dynamicRoutes, $this->checkCondition] = $compiledRoutes; } } RequestMatcherInterface.php 0000644 00000002341 15111166025 0012030 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\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 <fabien@symfony.com> */ interface RequestMatcherInterface { /** * Tries to match a request with a set of routes. * * If the matcher cannot find information, it must throw one of the exceptions documented * below. * * @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): array; } error_log 0000644 00000015420 15111166025 0006461 0 ustar 00 [20-Nov-2025 02:46:38 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php on line 24 [20-Nov-2025 02:47:07 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Routing\RequestContextAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php on line 24 [20-Nov-2025 02:47:09 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php on line 20 [20-Nov-2025 02:47:14 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/ExpressionLanguageProvider.php:23 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/ExpressionLanguageProvider.php on line 23 [20-Nov-2025 03:01:37 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php on line 22 [20-Nov-2025 03:09:10 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/ExpressionLanguageProvider.php:23 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/ExpressionLanguageProvider.php on line 23 [20-Nov-2025 03:10:12 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Routing\RequestContextAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php on line 24 [20-Nov-2025 03:11:16 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php on line 20 [20-Nov-2025 03:13:22 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php on line 22 [20-Nov-2025 03:15:21 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php on line 24 [20-Nov-2025 06:47:15 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php on line 24 [20-Nov-2025 06:47:34 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Routing\RequestContextAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php on line 24 [20-Nov-2025 06:51:28 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php on line 20 [20-Nov-2025 07:02:42 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php on line 22 [20-Nov-2025 07:13:46 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php:20 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php on line 20 [20-Nov-2025 07:15:24 UTC] PHP Fatal error: Uncaught Error: Interface "Symfony\Component\Routing\RequestContextAwareInterface" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/UrlMatcherInterface.php on line 24 [20-Nov-2025 07:16:46 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php:22 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/CompiledUrlMatcher.php on line 22 [20-Nov-2025 07:17:33 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php on line 24 [20-Nov-2025 07:26:50 UTC] PHP Fatal error: Uncaught Error: Class "Symfony\Component\Routing\Matcher\UrlMatcher" not found in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php:24 Stack trace: #0 {main} thrown in /home/fluxyjvi/public_html/project/vendor/symfony/routing/Matcher/TraceableUrlMatcher.php on line 24 RedirectableUrlMatcher.php 0000644 00000004014 15111166025 0011626 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\Routing\Exception\ExceptionInterface; use Symfony\Component\Routing\Exception\ResourceNotFoundException; /** * @author Fabien Potencier <fabien@symfony.com> */ abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface { public function match(string $pathinfo): array { try { return parent::match($pathinfo); } catch (ResourceNotFoundException $e) { if (!\in_array($this->context->getMethod(), ['HEAD', 'GET'], true)) { throw $e; } if ($this->allowSchemes) { redirect_scheme: $scheme = $this->context->getScheme(); $this->context->setScheme(current($this->allowSchemes)); try { $ret = parent::match($pathinfo); return $this->redirect($pathinfo, $ret['_route'] ?? null, $this->context->getScheme()) + $ret; } catch (ExceptionInterface) { throw $e; } finally { $this->context->setScheme($scheme); } } elseif ('/' === $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/') { throw $e; } else { try { $pathinfo = $trimmedPathinfo === $pathinfo ? $pathinfo.'/' : $trimmedPathinfo; $ret = parent::match($pathinfo); return $this->redirect($pathinfo, $ret['_route'] ?? null) + $ret; } catch (ExceptionInterface) { if ($this->allowSchemes) { goto redirect_scheme; } throw $e; } } } } } UrlMatcher.php 0000644 00000023355 15111166026 0007332 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\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 <fabien@symfony.com> */ class UrlMatcher implements UrlMatcherInterface, RequestMatcherInterface { public const REQUIREMENT_MATCH = 0; public const REQUIREMENT_MISMATCH = 1; public const ROUTE_MATCH = 2; /** @var RequestContext */ protected $context; /** * Collects HTTP methods that would be allowed for the request. */ protected $allow = []; /** * Collects URI schemes that would be allowed for the request. * * @internal */ protected array $allowSchemes = []; protected $routes; protected $request; protected $expressionLanguage; /** * @var ExpressionFunctionProviderInterface[] */ protected $expressionLanguageProviders = []; public function __construct(RouteCollection $routes, RequestContext $context) { $this->routes = $routes; $this->context = $context; } /** * @return void */ public function setContext(RequestContext $context) { $this->context = $context; } public function getContext(): RequestContext { return $this->context; } public function match(string $pathinfo): array { $this->allow = $this->allowSchemes = []; if ($ret = $this->matchCollection(rawurldecode($pathinfo) ?: '/', $this->routes)) { return $ret; } if ('/' === $pathinfo && !$this->allow && !$this->allowSchemes) { throw new NoConfigurationException(); } throw 0 < \count($this->allow) ? new MethodNotAllowedException(array_unique($this->allow)) : new ResourceNotFoundException(sprintf('No routes found for "%s".', $pathinfo)); } public function matchRequest(Request $request): array { $this->request = $request; $ret = $this->match($request->getPathInfo()); $this->request = null; return $ret; } /** * @return void */ 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 * * @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(string $pathinfo, RouteCollection $routes): array { // HEAD and GET are equivalent as per RFC if ('HEAD' === $method = $this->context->getMethod()) { $method = 'GET'; } $supportsTrailingSlash = 'GET' === $method && $this instanceof RedirectableUrlMatcherInterface; $trimmedPathinfo = rtrim($pathinfo, '/') ?: '/'; foreach ($routes as $name => $route) { $compiledRoute = $route->compile(); $staticPrefix = rtrim($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 && !str_starts_with($trimmedPathinfo, $staticPrefix)) { continue; } $regex = $compiledRoute->getRegex(); $pos = strrpos($regex, '$'); $hasTrailingSlash = '/' === $regex[$pos - 1]; $regex = substr_replace($regex, '/?$', $pos - $hasTrailingSlash, 1 + $hasTrailingSlash); if (!preg_match($regex, $pathinfo, $matches)) { continue; } $hasTrailingVar = $trimmedPathinfo !== $pathinfo && preg_match('#\{[\w\x80-\xFF]+\}/?$#', $route->getPath()); if ($hasTrailingVar && ($hasTrailingSlash || (null === $m = $matches[\count($compiledRoute->getPathVariables())] ?? null) || '/' !== ($m[-1] ?? '/')) && preg_match($regex, $trimmedPathinfo, $m)) { if ($hasTrailingSlash) { $matches = $m; } else { $hasTrailingVar = false; } } $hostMatches = []; if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { continue; } $attributes = $this->getAttributes($route, $name, array_replace($matches, $hostMatches)); $status = $this->handleRouteRequirements($pathinfo, $name, $route, $attributes); if (self::REQUIREMENT_MISMATCH === $status[0]) { continue; } if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) { if ($supportsTrailingSlash && (!$requiredMethods || \in_array('GET', $requiredMethods))) { return $this->allow = $this->allowSchemes = []; } continue; } if ($route->getSchemes() && !$route->hasScheme($this->context->getScheme())) { $this->allowSchemes = array_merge($this->allowSchemes, $route->getSchemes()); continue; } if ($requiredMethods && !\in_array($method, $requiredMethods)) { $this->allow = array_merge($this->allow, $requiredMethods); continue; } return array_replace($attributes, $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). */ protected function getAttributes(Route $route, string $name, array $attributes): array { $defaults = $route->getDefaults(); if (isset($defaults['_canonical_route'])) { $name = $defaults['_canonical_route']; unset($defaults['_canonical_route']); } $attributes['_route'] = $name; return $this->mergeDefaults($attributes, $defaults); } /** * Handles specific route requirements. * * @return array The first element represents the status, the second contains additional information */ protected function handleRouteRequirements(string $pathinfo, string $name, Route $route/* , array $routeParameters */): array { if (\func_num_args() < 4) { trigger_deprecation('symfony/routing', '6.1', 'The "%s()" method will have a new "array $routeParameters" argument in version 7.0, not defining it is deprecated.', __METHOD__); $routeParameters = []; } else { $routeParameters = func_get_arg(3); if (!\is_array($routeParameters)) { throw new \TypeError(sprintf('"%s": Argument $routeParameters is expected to be an array, got "%s".', __METHOD__, get_debug_type($routeParameters))); } } // expression condition if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), [ 'context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo), 'params' => $routeParameters, ])) { return [self::REQUIREMENT_MISMATCH, null]; } return [self::REQUIREMENT_MATCH, null]; } /** * Get merged default parameters. */ protected function mergeDefaults(array $params, array $defaults): array { foreach ($params as $key => $value) { if (!\is_int($key) && null !== $value) { $defaults[$key] = $value; } } return $defaults; } /** * @return ExpressionLanguage */ protected function getExpressionLanguage() { if (null === $this->expressionLanguage) { if (!class_exists(ExpressionLanguage::class)) { throw new \LogicException('Unable to use expressions as the Symfony ExpressionLanguage component is not installed. Try running "composer require symfony/expression-language".'); } $this->expressionLanguage = new ExpressionLanguage(null, $this->expressionLanguageProviders); } return $this->expressionLanguage; } /** * @internal */ protected function createRequest(string $pathinfo): ?Request { if (!class_exists(Request::class)) { 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(), ]); } } RedirectableUrlMatcherInterface.php 0000644 00000001537 15111166026 0013457 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; /** * RedirectableUrlMatcherInterface knows how to redirect the user. * * @author Fabien Potencier <fabien@symfony.com> */ interface RedirectableUrlMatcherInterface { /** * Redirects the user to another URL and returns the parameters for the redirection. * * @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) */ public function redirect(string $path, string $route, string $scheme = null): array; } ExpressionLanguageProvider.php 0000644 00000003025 15111166026 0012572 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher; use Symfony\Component\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 <ahmed.tailouloute@gmail.com> */ class ExpressionLanguageProvider implements ExpressionFunctionProviderInterface { private ServiceProviderInterface $functions; public function __construct(ServiceProviderInterface $functions) { $this->functions = $functions; } public function getFunctions(): array { $functions = []; foreach ($this->functions->getProvidedServices() as $function => $type) { $functions[] = new ExpressionFunction( $function, static fn (...$args) => sprintf('($context->getParameter(\'_functions\')->get(%s)(%s))', var_export($function, true), implode(', ', $args)), fn ($values, ...$args) => $values['context']->getParameter('_functions')->get($function)(...$args) ); } return $functions; } public function get(string $function): callable { return $this->functions->get($function); } } Dumper/MatcherDumperInterface.php 0000644 00000001512 15111166026 0013070 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher\Dumper; use Symfony\Component\Routing\RouteCollection; /** * MatcherDumperInterface is the interface that all matcher dumper classes must implement. * * @author Fabien Potencier <fabien@symfony.com> */ interface MatcherDumperInterface { /** * Dumps a set of routes to a string representation of executable code * that can then be used to match a request against these routes. */ public function dump(array $options = []): string; /** * Gets the routes to dump. */ public function getRoutes(): RouteCollection; } Dumper/StaticPrefixCollection.php 0000644 00000015616 15111166027 0013143 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher\Dumper; use Symfony\Component\Routing\RouteCollection; /** * Prefix tree of routes preserving routes order. * * @author Frank de Jonge <info@frankdejonge.nl> * @author Nicolas Grekas <p@tchwork.com> * * @internal */ class StaticPrefixCollection { private string $prefix; /** * @var string[] */ private array $staticPrefixes = []; /** * @var string[] */ private array $prefixes = []; /** * @var array[]|self[] */ private array $items = []; public function __construct(string $prefix = '/') { $this->prefix = $prefix; } public function getPrefix(): string { return $this->prefix; } /** * @return array[]|self[] */ public function getRoutes(): array { return $this->items; } /** * Adds a route to a group. */ public function addRoute(string $prefix, array|StaticPrefixCollection $route): void { [$prefix, $staticPrefix] = $this->getCommonPrefix($prefix, $prefix); for ($i = \count($this->items) - 1; 0 <= $i; --$i) { $item = $this->items[$i]; [$commonPrefix, $commonStaticPrefix] = $this->getCommonPrefix($prefix, $this->prefixes[$i]); if ($this->prefix === $commonPrefix) { // the new route and a previous one have no common prefix, let's see if they are exclusive to each others if ($this->prefix !== $staticPrefix && $this->prefix !== $this->staticPrefixes[$i]) { // the new route and the previous one have exclusive static prefixes continue; } if ($this->prefix === $staticPrefix && $this->prefix === $this->staticPrefixes[$i]) { // the new route and the previous one have no static prefix break; } if ($this->prefixes[$i] !== $this->staticPrefixes[$i] && $this->prefix === $this->staticPrefixes[$i]) { // the previous route is non-static and has no static prefix break; } if ($prefix !== $staticPrefix && $this->prefix === $staticPrefix) { // the new route is non-static and has no static prefix break; } continue; } if ($item instanceof self && $this->prefixes[$i] === $commonPrefix) { // the new route is a child of a previous one, let's nest it $item->addRoute($prefix, $route); } else { // the new route and a previous one have a common prefix, let's merge them $child = new self($commonPrefix); [$child->prefixes[0], $child->staticPrefixes[0]] = $child->getCommonPrefix($this->prefixes[$i], $this->prefixes[$i]); [$child->prefixes[1], $child->staticPrefixes[1]] = $child->getCommonPrefix($prefix, $prefix); $child->items = [$this->items[$i], $route]; $this->staticPrefixes[$i] = $commonStaticPrefix; $this->prefixes[$i] = $commonPrefix; $this->items[$i] = $child; } return; } // No optimised case was found, in this case we simple add the route for possible // grouping when new routes are added. $this->staticPrefixes[] = $staticPrefix; $this->prefixes[] = $prefix; $this->items[] = $route; } /** * Linearizes back a set of nested routes into a collection. */ public function populateCollection(RouteCollection $routes): RouteCollection { foreach ($this->items as $route) { if ($route instanceof self) { $route->populateCollection($routes); } else { $routes->add(...$route); } } return $routes; } /** * Gets the full and static common prefixes between two route patterns. * * The static prefix stops at last at the first opening bracket. */ private function getCommonPrefix(string $prefix, string $anotherPrefix): array { $baseLength = \strlen($this->prefix); $end = min(\strlen($prefix), \strlen($anotherPrefix)); $staticLength = null; set_error_handler([__CLASS__, 'handleError']); try { for ($i = $baseLength; $i < $end && $prefix[$i] === $anotherPrefix[$i]; ++$i) { if ('(' === $prefix[$i]) { $staticLength ??= $i; for ($j = 1 + $i, $n = 1; $j < $end && 0 < $n; ++$j) { if ($prefix[$j] !== $anotherPrefix[$j]) { break 2; } if ('(' === $prefix[$j]) { ++$n; } elseif (')' === $prefix[$j]) { --$n; } elseif ('\\' === $prefix[$j] && (++$j === $end || $prefix[$j] !== $anotherPrefix[$j])) { --$j; break; } } if (0 < $n) { break; } if (('?' === ($prefix[$j] ?? '') || '?' === ($anotherPrefix[$j] ?? '')) && ($prefix[$j] ?? '') !== ($anotherPrefix[$j] ?? '')) { break; } $subPattern = substr($prefix, $i, $j - $i); if ($prefix !== $anotherPrefix && !preg_match('/^\(\[[^\]]++\]\+\+\)$/', $subPattern) && !preg_match('{(?<!'.$subPattern.')}', '')) { // sub-patterns of variable length are not considered as common prefixes because their greediness would break in-order matching break; } $i = $j - 1; } elseif ('\\' === $prefix[$i] && (++$i === $end || $prefix[$i] !== $anotherPrefix[$i])) { --$i; break; } } } finally { restore_error_handler(); } if ($i < $end && 0b10 === (\ord($prefix[$i]) >> 6) && preg_match('//u', $prefix.' '.$anotherPrefix)) { do { // Prevent cutting in the middle of an UTF-8 characters --$i; } while (0b10 === (\ord($prefix[$i]) >> 6)); } return [substr($prefix, 0, $i), substr($prefix, 0, $staticLength ?? $i)]; } public static function handleError(int $type, string $msg): bool { return str_contains($msg, 'Compilation failed: lookbehind assertion is not fixed length'); } } Dumper/MatcherDumper.php 0000644 00000001403 15111166027 0011247 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher\Dumper; use Symfony\Component\Routing\RouteCollection; /** * MatcherDumper is the abstract class for all built-in matcher dumpers. * * @author Fabien Potencier <fabien@symfony.com> */ abstract class MatcherDumper implements MatcherDumperInterface { private RouteCollection $routes; public function __construct(RouteCollection $routes) { $this->routes = $routes; } public function getRoutes(): RouteCollection { return $this->routes; } } Dumper/CompiledUrlMatcherDumper.php 0000644 00000045323 15111166027 0013420 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher\Dumper; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; /** * CompiledUrlMatcherDumper creates PHP arrays to be used with CompiledUrlMatcher. * * @author Fabien Potencier <fabien@symfony.com> * @author Tobias Schultze <http://tobion.de> * @author Arnaud Le Blanc <arnaud.lb@gmail.com> * @author Nicolas Grekas <p@tchwork.com> */ class CompiledUrlMatcherDumper extends MatcherDumper { private ExpressionLanguage $expressionLanguage; private ?\Exception $signalingException = null; /** * @var ExpressionFunctionProviderInterface[] */ private array $expressionLanguageProviders = []; public function dump(array $options = []): string { return <<<EOF <?php /** * This file has been auto-generated * by the Symfony Routing Component. */ return [ {$this->generateCompiledRoutes()}]; EOF; } /** * @return void */ 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 = <<<EOF static function (\$condition, \$context, \$request, \$params) { // \$checkCondition switch (\$condition) { {$this->indent(implode("\n", $conditions), 3)} } } EOF; $compiledRoutes[4] = $forDump ? $checkConditionCode.",\n" : eval('return '.$checkConditionCode.';'); } else { $compiledRoutes[4] = $forDump ? " null, // \$checkCondition\n" : null; } return $compiledRoutes; } private function generateCompiledRoutes(): string { [$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 .= vsprintf(" [%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 .= vsprintf(" [%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\x80-\xFF]+\}/?$#', $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', 'params']); $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. Try running "composer require symfony/expression-language".'); } $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); } } Dumper/CompiledUrlMatcherTrait.php 0000644 00000016577 15111166030 0013252 0 ustar 00 <?php /* * This file is part of the Symfony package. * * (c) Fabien Potencier <fabien@symfony.com> * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\Routing\Matcher\Dumper; use Symfony\Component\Routing\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 <p@tchwork.com> * * @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 ($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 ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ??= $this->request ?: $this->createRequest($pathinfo) : null, $ret)) { continue; } 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 (0 === $condition) { // marks the last route in the regexp continue 3; } $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; } } foreach ($vars as $i => $v) { if (isset($matches[1 + $i])) { $ret[$v] = $matches[1 + $i]; } } if ($condition && !($this->checkCondition)($condition, $context, 0 < $condition ? $request ??= $this->request ?: $this->createRequest($pathinfo) : null, $ret)) { continue; } if ('/' !== $pathinfo && !$hasTrailingVar && $hasTrailingSlash === ($trimmedPathinfo === $pathinfo)) { if ($supportsRedirections && (!$requiredMethods || isset($requiredMethods['GET']))) { return $allow = $allowSchemes = []; } continue; } 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 []; } }