Further Drupal 8.6.4 changes. Some core files were not committed before a commit...
[yaffs-website] / web / core / lib / Drupal / Core / Routing / Router.php
index e949c5efb862a166d555b046d0cedb75fa346ef6..c6c5dc0aa7662d805ee1688f0c80adc8f800d017 100644 (file)
@@ -3,9 +3,9 @@
 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;
@@ -53,31 +53,17 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
   /**
    * 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.
    *
@@ -102,36 +88,23 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
   }
 
   /**
-   * 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;
   }
 
   /**
@@ -148,7 +121,11 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
    */
   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);
@@ -276,46 +253,16 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
    *   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.
    *
@@ -333,7 +280,7 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
   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);
     }
 
@@ -341,36 +288,41 @@ class Router extends UrlMatcher implements RequestMatcherInterface, RouterInterf
   }
 
   /**
-   * 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;
   }
 
   /**