Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Routing / RouteProvider.php
index 5d92e77e0f1908527b21c26d1da5090c13b43f51..d31f293e18c411c660c4c96989e0fef7625f66c9 100644 (file)
@@ -2,10 +2,11 @@
 
 namespace Drupal\Core\Routing;
 
-use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Path\CurrentPathStack;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\State\StateInterface;
@@ -15,7 +16,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Routing\RouteCollection;
-use \Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Connection;
 
 /**
  * A Route Provider front-end for all Drupal-stored routes.
@@ -85,6 +86,13 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
    */
   protected $pathProcessor;
 
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
   /**
    * Cache ID prefix used to load routes.
    */
@@ -107,8 +115,10 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
    *   The cache tag invalidator.
    * @param string $table
    *   (Optional) The table in the database to use for matching. Defaults to 'router'
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   (Optional) The language manager.
    */
-  public function __construct(Connection $connection, StateInterface $state, CurrentPathStack $current_path, CacheBackendInterface $cache_backend, InboundPathProcessorInterface $path_processor, CacheTagsInvalidatorInterface $cache_tag_invalidator, $table = 'router') {
+  public function __construct(Connection $connection, StateInterface $state, CurrentPathStack $current_path, CacheBackendInterface $cache_backend, InboundPathProcessorInterface $path_processor, CacheTagsInvalidatorInterface $cache_tag_invalidator, $table = 'router', LanguageManagerInterface $language_manager = NULL) {
     $this->connection = $connection;
     $this->state = $state;
     $this->currentPath = $current_path;
@@ -116,6 +126,7 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
     $this->cacheTagInvalidator = $cache_tag_invalidator;
     $this->pathProcessor = $path_processor;
     $this->tableName = $table;
+    $this->languageManager = $language_manager ?: \Drupal::languageManager();
   }
 
   /**
@@ -135,19 +146,19 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
    * very large route sets to be filtered down to likely candidates, which
    * may then be filtered in memory more completely.
    *
-   * @param Request $request
+   * @param \Symfony\Component\HttpFoundation\Request $request
    *   A request against which to match.
    *
-   * @return \Symfony\Component\Routing\RouteCollection with all urls that
-   *      could potentially match $request. Empty collection if nothing can
-   *      match. The collection will be sorted from highest to lowest fit (match
-   *      of path parts) and then in ascending order by route name for routes
-   *      with the same fit.
+   * @return \Symfony\Component\Routing\RouteCollection
+   *   RouteCollection with all urls that could potentially match $request.
+   *   Empty collection if nothing can match. The collection will be sorted from
+   *   highest to lowest fit (match of path parts) and then in ascending order
+   *   by route name for routes with the same fit.
    */
   public function getRouteCollectionForRequest(Request $request) {
     // Cache both the system path as well as route parameters and matching
     // routes.
-    $cid = 'route:' . $request->getPathInfo() . ':' . $request->getQueryString();
+    $cid = $this->getRouteCollectionCacheId($request);
     if ($cached = $this->cache->get($cid)) {
       $this->currentPath->setPath($cached->data['path'], $request);
       $request->query->replace($cached->data['query']);
@@ -333,7 +344,7 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
     // have a case-insensitive match from the incoming path to the lower case
     // pattern outlines from \Drupal\Core\Routing\RouteCompiler::compile().
     // @see \Drupal\Core\Routing\CompiledRoute::__construct()
-    $parts = preg_split('@/+@', Unicode::strtolower($path), NULL, PREG_SPLIT_NO_EMPTY);
+    $parts = preg_split('@/+@', mb_strtolower($path), NULL, PREG_SPLIT_NO_EMPTY);
 
     $collection = new RouteCollection();
 
@@ -347,7 +358,8 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
     // dump the route pattern without those optional parts.
     try {
       $routes = $this->connection->query("SELECT name, route, fit FROM {" . $this->connection->escapeTable($this->tableName) . "} WHERE pattern_outline IN ( :patterns[] ) AND number_parts >= :count_parts", [
-        ':patterns[]' => $ancestors, ':count_parts' => count($parts),
+        ':patterns[]' => $ancestors,
+        ':count_parts' => count($parts),
       ])
         ->fetchAll(\PDO::FETCH_ASSOC);
     }
@@ -389,7 +401,7 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
    * {@inheritdoc}
    */
   public function reset() {
-    $this->routes  = [];
+    $this->routes = [];
     $this->serializedRoutes = [];
     $this->cacheTagInvalidator->invalidateTags(['routes']);
   }
@@ -430,4 +442,35 @@ class RouteProvider implements PreloadableRouteProviderInterface, PagedRouteProv
     return $this->connection->query("SELECT COUNT(*) FROM {" . $this->connection->escapeTable($this->tableName) . "}")->fetchField();
   }
 
+  /**
+   * Returns the cache ID for the route collection cache.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object.
+   *
+   * @return string
+   *   The cache ID.
+   */
+  protected function getRouteCollectionCacheId(Request $request) {
+    // Include the current language code in the cache identifier as
+    // the language information can be elsewhere than in the path, for example
+    // based on the domain.
+    $language_part = $this->getCurrentLanguageCacheIdPart();
+    return 'route:' . $language_part . ':' . $request->getPathInfo() . ':' . $request->getQueryString();
+  }
+
+  /**
+   * Returns the language identifier for the route collection cache.
+   *
+   * @return string
+   *   The language identifier.
+   */
+  protected function getCurrentLanguageCacheIdPart() {
+    // This must be in sync with the language logic in
+    // \Drupal\Core\PathProcessor\PathProcessorAlias::processInbound() and
+    // \Drupal\Core\Path\AliasManager::getPathByAlias().
+    // @todo Update this if necessary in https://www.drupal.org/node/1125428.
+    return $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId();
+  }
+
 }