<?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;
}
/**
* {@inheritdoc}
*/
public function setContext(RequestContext $context)
{
$this->context = $context;
}
/**
* {@inheritdoc}
*/
public function getContext(): RequestContext
{
return $this->context;
}
/**
* {@inheritdoc}
*/
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));
}
/**
* {@inheritdoc}
*/
public function matchRequest(Request $request): array
{
$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
*
* @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+\}/?$#', $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;
}
$status = $this->handleRouteRequirements($pathinfo, $name, $route);
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 $this->getAttributes($route, $name, array_replace($matches, $hostMatches, $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
{
// expression condition
if ($route->getCondition() && !$this->getExpressionLanguage()->evaluate($route->getCondition(), ['context' => $this->context, 'request' => $this->request ?: $this->createRequest($pathinfo)])) {
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;
}
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.');
}
$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(),
]);
}
}
How To Payment
Making a payment on our website is quick and secure. Start by logging in or creating an account. Select your preferred payment method, input the required details, and review the information. Once you confirm everything is correct, click on the "Submit Payment" button. You’ll receive instant confirmation and can track your payment status through your account dashboard. It’s an easy and secure process.