namespace Drupal\Core\Routing;
use Drupal\Core\Path\CurrentPathStack;
-use Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface as BaseRouteEnhancerInterface;
+use Drupal\Core\Routing\Enhancer\RouteEnhancerInterface;
use Symfony\Cmf\Component\Routing\LazyRouteCollection;
-use Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface as BaseRouteFilterInterface;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Cmf\Component\Routing\RouteProviderInterface as BaseRouteProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
/**
* The list of available enhancers.
*
- * @var \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]
+ * @var \Drupal\Core\Routing\EnhancerInterface[]
*/
protected $enhancers = [];
- /**
- * Cached sorted list of enhancers.
- *
- * @var \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]
- */
- protected $sortedEnhancers;
-
/**
* The list of available route filters.
*
- * @var \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface[]
+ * @var \Drupal\Core\Routing\FilterInterface[]
*/
protected $filters = [];
- /**
- * Cached sorted list route filters.
- *
- * @var \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface[]
- */
- protected $sortedFilters;
-
/**
* The URL generator.
*
}
/**
- * Adds a route enhancer to the list of used route enhancers.
- *
- * @param \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface $route_enhancer
- * A route enhancer.
- * @param int $priority
- * (optional) The priority of the enhancer. Higher number enhancers will be
- * used first.
+ * Adds a route filter.
*
- * @return $this
+ * @param \Drupal\Core\Routing\FilterInterface $route_filter
+ * The route filter.
*/
- public function addRouteEnhancer(BaseRouteEnhancerInterface $route_enhancer, $priority = 0) {
- $this->enhancers[$priority][] = $route_enhancer;
- return $this;
+ public function addRouteFilter(FilterInterface $route_filter) {
+ $this->filters[] = $route_filter;
}
/**
- * Adds a route filter to the list of used route filters.
+ * Adds a route enhancer.
*
- * @param \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface $route_filter
- * A route filter.
- * @param int $priority
- * (optional) The priority of the filter. Higher number filters will be used
- * first.
- *
- * @return $this
+ * @param \Drupal\Core\Routing\EnhancerInterface $route_enhancer
+ * The route enhancer.
*/
- public function addRouteFilter(BaseRouteFilterInterface $route_filter, $priority = 0) {
- $this->filters[$priority][] = $route_filter;
-
- return $this;
+ public function addRouteEnhancer(EnhancerInterface $route_enhancer) {
+ $this->enhancers[] = $route_enhancer;
}
/**
*/
public function matchRequest(Request $request) {
$collection = $this->getInitialRouteCollection($request);
+ if ($collection->count() === 0) {
+ throw new ResourceNotFoundException(sprintf('No routes found for "%s".', $this->currentPath->getPath()));
+ }
$collection = $this->applyRouteFilters($collection, $request);
+ $collection = $this->applyFitOrder($collection);
if ($ret = $this->matchCollection(rawurldecode($this->currentPath->getPath($request)), $collection)) {
return $this->applyRouteEnhancers($ret, $request);
* from route enhancers.
*/
protected function applyRouteEnhancers($defaults, Request $request) {
- foreach ($this->getRouteEnhancers() as $enhancer) {
+ foreach ($this->enhancers as $enhancer) {
+ if ($enhancer instanceof RouteEnhancerInterface && !$enhancer->applies($defaults[RouteObjectInterface::ROUTE_OBJECT])) {
+ continue;
+ }
$defaults = $enhancer->enhance($defaults, $request);
}
return $defaults;
}
- /**
- * Sorts the enhancers and flattens them.
- *
- * @return \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]
- * The enhancers ordered by priority.
- */
- public function getRouteEnhancers() {
- if (!isset($this->sortedEnhancers)) {
- $this->sortedEnhancers = $this->sortRouteEnhancers();
- }
-
- return $this->sortedEnhancers;
- }
-
- /**
- * Sort enhancers by priority.
- *
- * The highest priority number is the highest priority (reverse sorting).
- *
- * @return \Symfony\Cmf\Component\Routing\Enhancer\RouteEnhancerInterface[]
- * The sorted enhancers.
- */
- protected function sortRouteEnhancers() {
- $sortedEnhancers = [];
- krsort($this->enhancers);
-
- foreach ($this->enhancers as $enhancers) {
- $sortedEnhancers = array_merge($sortedEnhancers, $enhancers);
- }
-
- return $sortedEnhancers;
- }
-
/**
* Applies all route filters to a given route collection.
*
protected function applyRouteFilters(RouteCollection $collection, Request $request) {
// Route filters are expected to throw an exception themselves if they
// end up filtering the list down to 0.
- foreach ($this->getRouteFilters() as $filter) {
+ foreach ($this->filters as $filter) {
$collection = $filter->filter($collection, $request);
}
}
/**
- * Sorts the filters and flattens them.
+ * Reapplies the fit order to a RouteCollection object.
*
- * @return \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface[]
- * The filters ordered by priority
- */
- public function getRouteFilters() {
- if (!isset($this->sortedFilters)) {
- $this->sortedFilters = $this->sortFilters();
- }
-
- return $this->sortedFilters;
- }
-
- /**
- * Sort filters by priority.
+ * Route filters can reorder route collections. For example, routes with an
+ * explicit _format requirement will be preferred. This can result in a less
+ * fit route being used. For example, as a result of filtering /user/% comes
+ * before /user/login. In order to not break this fundamental property of
+ * routes, we need to reapply the fit order. We also need to ensure that order
+ * within each group of the same fit is preserved.
*
- * The highest priority number is the highest priority (reverse sorting).
+ * @param \Symfony\Component\Routing\RouteCollection $collection
+ * The route collection.
*
- * @return \Symfony\Cmf\Component\Routing\NestedMatcher\RouteFilterInterface[]
- * The sorted filters.
+ * @return \Symfony\Component\Routing\RouteCollection
+ * The reordered route collection.
*/
- protected function sortFilters() {
- $sortedFilters = [];
- krsort($this->filters);
-
- foreach ($this->filters as $filters) {
- $sortedFilters = array_merge($sortedFilters, $filters);
+ protected function applyFitOrder(RouteCollection $collection) {
+ $buckets = [];
+ // Sort all the routes by fit descending.
+ foreach ($collection->all() as $name => $route) {
+ $fit = $route->compile()->getFit();
+ $buckets += [$fit => []];
+ $buckets[$fit][] = [$name, $route];
}
+ krsort($buckets);
+
+ $flattened = array_reduce($buckets, 'array_merge', []);
- return $sortedFilters;
+ // Add them back onto a new route collection.
+ $collection = new RouteCollection();
+ foreach ($flattened as $pair) {
+ $name = $pair[0];
+ $route = $pair[1];
+ $collection->add($name, $route);
+ }
+ return $collection;
}
/**