Chris@0: routeProvider = $route_provider; Chris@0: $this->urlGenerator = $url_generator; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a route enhancer to the list of used route enhancers. Chris@0: * Chris@0: * @param \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface $route_enhancer Chris@0: * A route enhancer. Chris@0: * @param int $priority Chris@0: * (optional) The priority of the enhancer. Higher number enhancers will be Chris@0: * used first. Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function addRouteEnhancer(BaseRouteEnhancerInterface $route_enhancer, $priority = 0) { Chris@0: $this->enhancers[$priority][] = $route_enhancer; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Adds a route filter to the list of used route filters. Chris@0: * Chris@0: * @param \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface $route_filter Chris@0: * A route filter. Chris@0: * @param int $priority Chris@0: * (optional) The priority of the filter. Higher number filters will be used Chris@0: * first. Chris@0: * Chris@0: * @return $this Chris@0: */ Chris@0: public function addRouteFilter(BaseRouteFilterInterface $route_filter, $priority = 0) { Chris@0: $this->filters[$priority][] = $route_filter; Chris@0: Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function match($pathinfo) { Chris@0: $request = Request::create($pathinfo); Chris@0: Chris@0: return $this->matchRequest($request); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function matchRequest(Request $request) { Chris@0: $collection = $this->getInitialRouteCollection($request); Chris@0: $collection = $this->applyRouteFilters($collection, $request); Chris@0: Chris@0: if ($ret = $this->matchCollection(rawurldecode($this->currentPath->getPath($request)), $collection)) { Chris@0: return $this->applyRouteEnhancers($ret, $request); Chris@0: } Chris@0: Chris@0: throw 0 < count($this->allow) Chris@0: ? new MethodNotAllowedException(array_unique($this->allow)) Chris@0: : new ResourceNotFoundException(sprintf('No routes found for "%s".', $this->currentPath->getPath())); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tries to match a URL with a set of routes. Chris@0: * Chris@0: * @param string $pathinfo Chris@0: * The path info to be parsed Chris@0: * @param \Symfony\Component\Routing\RouteCollection $routes Chris@0: * The set of routes. Chris@0: * Chris@0: * @return array|null Chris@0: * An array of parameters. NULL when there is no match. Chris@0: */ Chris@0: protected function matchCollection($pathinfo, RouteCollection $routes) { Chris@0: // Try a case-sensitive match. Chris@0: $match = $this->doMatchCollection($pathinfo, $routes, TRUE); Chris@0: // Try a case-insensitive match. Chris@0: if ($match === NULL && $routes->count() > 0) { Chris@0: $match = $this->doMatchCollection($pathinfo, $routes, FALSE); Chris@0: } Chris@0: return $match; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tries to match a URL with a set of routes. Chris@0: * Chris@0: * This code is very similar to Symfony's UrlMatcher::matchCollection() but it Chris@0: * supports case-insensitive matching. The static prefix optimization is Chris@0: * removed as this duplicates work done by the query in Chris@0: * RouteProvider::getRoutesByPath(). Chris@0: * Chris@0: * @param string $pathinfo Chris@0: * The path info to be parsed Chris@0: * @param \Symfony\Component\Routing\RouteCollection $routes Chris@0: * The set of routes. Chris@0: * @param bool $case_sensitive Chris@0: * Determines if the match should be case-sensitive of not. Chris@0: * Chris@0: * @return array|null Chris@0: * An array of parameters. NULL when there is no match. Chris@0: * Chris@0: * @see \Symfony\Component\Routing\Matcher\UrlMatcher::matchCollection() Chris@0: * @see \Drupal\Core\Routing\RouteProvider::getRoutesByPath() Chris@0: */ Chris@0: protected function doMatchCollection($pathinfo, RouteCollection $routes, $case_sensitive) { Chris@0: foreach ($routes as $name => $route) { Chris@0: $compiledRoute = $route->compile(); Chris@0: Chris@0: // Set the regex to use UTF-8. Chris@0: $regex = $compiledRoute->getRegex() . 'u'; Chris@0: if (!$case_sensitive) { Chris@0: $regex = $regex . 'i'; Chris@0: } Chris@0: if (!preg_match($regex, $pathinfo, $matches)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $hostMatches = []; Chris@0: if ($compiledRoute->getHostRegex() && !preg_match($compiledRoute->getHostRegex(), $this->context->getHost(), $hostMatches)) { Chris@0: $routes->remove($name); Chris@0: continue; Chris@0: } Chris@0: Chris@0: // Check HTTP method requirement. Chris@0: if ($requiredMethods = $route->getMethods()) { Chris@0: // HEAD and GET are equivalent as per RFC. Chris@0: if ('HEAD' === $method = $this->context->getMethod()) { Chris@0: $method = 'GET'; Chris@0: } Chris@0: Chris@0: if (!in_array($method, $requiredMethods)) { Chris@0: $this->allow = array_merge($this->allow, $requiredMethods); Chris@0: $routes->remove($name); Chris@0: continue; Chris@0: } Chris@0: } Chris@0: Chris@0: $status = $this->handleRouteRequirements($pathinfo, $name, $route); Chris@0: Chris@0: if (self::ROUTE_MATCH === $status[0]) { Chris@0: return $status[1]; Chris@0: } Chris@0: Chris@0: if (self::REQUIREMENT_MISMATCH === $status[0]) { Chris@0: $routes->remove($name); Chris@0: continue; Chris@0: } Chris@0: Chris@0: return $this->getAttributes($route, $name, array_replace($matches, $hostMatches)); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns a collection of potential matching routes for a request. Chris@0: * Chris@0: * @param \Symfony\Component\HttpFoundation\Request $request Chris@0: * The current request. Chris@0: * Chris@0: * @return \Symfony\Component\Routing\RouteCollection Chris@0: * The initial fetched route collection. Chris@0: */ Chris@0: protected function getInitialRouteCollection(Request $request) { Chris@0: return $this->routeProvider->getRouteCollectionForRequest($request); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Apply the route enhancers to the defaults, according to priorities. Chris@0: * Chris@0: * @param array $defaults Chris@0: * The defaults coming from the final matched route. Chris@0: * @param \Symfony\Component\HttpFoundation\Request $request Chris@0: * The request. Chris@0: * Chris@0: * @return array Chris@0: * The request attributes after applying the enhancers. This might consist Chris@0: * raw values from the URL but also upcasted values, like entity objects, Chris@0: * from route enhancers. Chris@0: */ Chris@0: protected function applyRouteEnhancers($defaults, Request $request) { Chris@0: foreach ($this->getRouteEnhancers() as $enhancer) { Chris@0: $defaults = $enhancer->enhance($defaults, $request); Chris@0: } Chris@0: Chris@0: return $defaults; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sorts the enhancers and flattens them. Chris@0: * Chris@0: * @return \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[] Chris@0: * The enhancers ordered by priority. Chris@0: */ Chris@0: public function getRouteEnhancers() { Chris@0: if (!isset($this->sortedEnhancers)) { Chris@0: $this->sortedEnhancers = $this->sortRouteEnhancers(); Chris@0: } Chris@0: Chris@0: return $this->sortedEnhancers; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sort enhancers by priority. Chris@0: * Chris@0: * The highest priority number is the highest priority (reverse sorting). Chris@0: * Chris@0: * @return \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[] Chris@0: * The sorted enhancers. Chris@0: */ Chris@0: protected function sortRouteEnhancers() { Chris@0: $sortedEnhancers = []; Chris@0: krsort($this->enhancers); Chris@0: Chris@0: foreach ($this->enhancers as $enhancers) { Chris@0: $sortedEnhancers = array_merge($sortedEnhancers, $enhancers); Chris@0: } Chris@0: Chris@0: return $sortedEnhancers; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Applies all route filters to a given route collection. Chris@0: * Chris@0: * This method reduces the sets of routes further down, for example by Chris@0: * checking the HTTP method. Chris@0: * Chris@0: * @param \Symfony\Component\Routing\RouteCollection $collection Chris@0: * The route collection. Chris@0: * @param \Symfony\Component\HttpFoundation\Request $request Chris@0: * The request. Chris@0: * Chris@0: * @return \Symfony\Component\Routing\RouteCollection Chris@0: * The filtered/sorted route collection. Chris@0: */ Chris@0: protected function applyRouteFilters(RouteCollection $collection, Request $request) { Chris@0: // Route filters are expected to throw an exception themselves if they Chris@0: // end up filtering the list down to 0. Chris@0: foreach ($this->getRouteFilters() as $filter) { Chris@0: $collection = $filter->filter($collection, $request); Chris@0: } Chris@0: Chris@0: return $collection; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sorts the filters and flattens them. Chris@0: * Chris@0: * @return \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface[] Chris@0: * The filters ordered by priority Chris@0: */ Chris@0: public function getRouteFilters() { Chris@0: if (!isset($this->sortedFilters)) { Chris@0: $this->sortedFilters = $this->sortFilters(); Chris@0: } Chris@0: Chris@0: return $this->sortedFilters; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Sort filters by priority. Chris@0: * Chris@0: * The highest priority number is the highest priority (reverse sorting). Chris@0: * Chris@0: * @return \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface[] Chris@0: * The sorted filters. Chris@0: */ Chris@0: protected function sortFilters() { Chris@0: $sortedFilters = []; Chris@0: krsort($this->filters); Chris@0: Chris@0: foreach ($this->filters as $filters) { Chris@0: $sortedFilters = array_merge($sortedFilters, $filters); Chris@0: } Chris@0: Chris@0: return $sortedFilters; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getRouteCollection() { Chris@0: return new LazyRouteCollection($this->routeProvider); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) { Chris@0: @trigger_error('Use the \Drupal\Core\Url object instead', E_USER_DEPRECATED); Chris@0: return $this->urlGenerator->generate($name, $parameters, $referenceType); Chris@0: } Chris@0: Chris@0: }