Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
25.00% |
2 / 8 |
CRAP | |
78.26% |
54 / 69 |
AliasManager | |
0.00% |
0 / 1 |
|
25.00% |
2 / 8 |
49.31 | |
78.26% |
54 / 69 |
__construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 5 |
|||
setCacheKey | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
writeCache | |
100.00% |
1 / 1 |
6 | |
100.00% |
9 / 9 |
|||
getPathByAlias | |
100.00% |
1 / 1 |
7 | |
100.00% |
10 / 10 |
|||
getAliasByPath | |
0.00% |
0 / 1 |
13 | |
96.15% |
25 / 26 |
|||
cacheClear | |
0.00% |
0 / 1 |
3.01 | |
90.91% |
10 / 11 |
|||
pathAliasWhitelistRebuild | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 5 |
|||
getRequestTime | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 1 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\Core\Path\AliasManager. | |
*/ | |
namespace Drupal\Core\Path; | |
use Drupal\Core\Cache\CacheBackendInterface; | |
use Drupal\Core\CacheDecorator\CacheDecoratorInterface; | |
use Drupal\Core\Language\LanguageInterface; | |
use Drupal\Core\Language\LanguageManagerInterface; | |
/** | |
* The default alias manager implementation. | |
*/ | |
class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { | |
/** | |
* The alias storage service. | |
* | |
* @var \Drupal\Core\Path\AliasStorageInterface | |
*/ | |
protected $storage; | |
/** | |
* Cache backend service. | |
* | |
* @var \Drupal\Core\Cache\CacheBackendInterface; | |
*/ | |
protected $cache; | |
/** | |
* The cache key to use when caching paths. | |
* | |
* @var string | |
*/ | |
protected $cacheKey; | |
/** | |
* Whether the cache needs to be written. | |
* | |
* @var bool | |
*/ | |
protected $cacheNeedsWriting = FALSE; | |
/** | |
* Language manager for retrieving the default langcode when none is specified. | |
* | |
* @var \Drupal\Core\Language\LanguageManagerInterface | |
*/ | |
protected $languageManager; | |
/** | |
* Holds the map of path lookups per language. | |
* | |
* @var array | |
*/ | |
protected $lookupMap = array(); | |
/** | |
* Holds an array of aliases for which no path was found. | |
* | |
* @var array | |
*/ | |
protected $noPath = array(); | |
/** | |
* Holds the array of whitelisted path aliases. | |
* | |
* @var \Drupal\Core\Path\AliasWhitelistInterface | |
*/ | |
protected $whitelist; | |
/** | |
* Holds an array of paths that have no alias. | |
* | |
* @var array | |
*/ | |
protected $noAlias = array(); | |
/** | |
* Whether preloaded path lookups has already been loaded. | |
* | |
* @var array | |
*/ | |
protected $langcodePreloaded = array(); | |
/** | |
* Holds an array of previously looked up paths for the current request path. | |
* | |
* This will only get populated if a cache key has been set, which for example | |
* happens if the alias manager is used in the context of a request. | |
* | |
* @var array | |
*/ | |
protected $preloadedPathLookups = FALSE; | |
/** | |
* Constructs an AliasManager. | |
* | |
* @param \Drupal\Core\Path\AliasStorageInterface $storage | |
* The alias storage service. | |
* @param \Drupal\Core\Path\AliasWhitelistInterface $whitelist | |
* The whitelist implementation to use. | |
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager | |
* The language manager. | |
* @param \Drupal\Core\Cache\CacheBackendInterface $cache | |
* Cache backend. | |
*/ | |
public function __construct(AliasStorageInterface $storage, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) { | |
$this->storage = $storage; | |
$this->languageManager = $language_manager; | |
$this->whitelist = $whitelist; | |
$this->cache = $cache; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setCacheKey($key) { | |
// Prefix the cache key to avoid clashes with other caches. | |
$this->cacheKey = 'preload-paths:' . $key; | |
} | |
/** | |
* {@inheritdoc} | |
* | |
* Cache an array of the paths available on each page. We assume that aliases | |
* will be needed for the majority of these paths during subsequent requests, | |
* and load them in a single query during path alias lookup. | |
*/ | |
public function writeCache() { | |
// Check if the paths for this page were loaded from cache in this request | |
// to avoid writing to cache on every request. | |
if ($this->cacheNeedsWriting && !empty($this->cacheKey)) { | |
// Start with the preloaded path lookups, so that cached entries for other | |
// languages will not be lost. | |
$path_lookups = $this->preloadedPathLookups ?: array(); | |
foreach ($this->lookupMap as $langcode => $lookups) { | |
$path_lookups[$langcode] = array_keys($lookups); | |
if (!empty($this->noAlias[$langcode])) { | |
$path_lookups[$langcode] = array_merge($path_lookups[$langcode], array_keys($this->noAlias[$langcode])); | |
} | |
} | |
$twenty_four_hours = 60 * 60 * 24; | |
$this->cache->set($this->cacheKey, $path_lookups, $this->getRequestTime() + $twenty_four_hours); | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getPathByAlias($alias, $langcode = NULL) { | |
// If no language is explicitly specified we default to the current URL | |
// language. If we used a language different from the one conveyed by the | |
// requested URL, we might end up being unable to check if there is a path | |
// alias matching the URL path. | |
$langcode = $langcode ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(); | |
// If we already know that there are no paths for this alias simply return. | |
if (empty($alias) || !empty($this->noPath[$langcode][$alias])) { | |
return $alias; | |
} | |
// Look for the alias within the cached map. | |
if (isset($this->lookupMap[$langcode]) && ($path = array_search($alias, $this->lookupMap[$langcode]))) { | |
return $path; | |
} | |
// Look for path in storage. | |
if ($path = $this->storage->lookupPathSource($alias, $langcode)) { | |
$this->lookupMap[$langcode][$path] = $alias; | |
return $path; | |
} | |
// We can't record anything into $this->lookupMap because we didn't find any | |
// paths for this alias. Thus cache to $this->noPath. | |
$this->noPath[$langcode][$alias] = TRUE; | |
return $alias; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getAliasByPath($path, $langcode = NULL) { | |
if ($path[0] !== '/') { | |
throw new \InvalidArgumentException(sprintf('Source path %s has to start with a slash.', $path)); | |
} | |
// If no language is explicitly specified we default to the current URL | |
// language. If we used a language different from the one conveyed by the | |
// requested URL, we might end up being unable to check if there is a path | |
// alias matching the URL path. | |
$langcode = $langcode ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(); | |
// Check the path whitelist, if the top-level part before the first / | |
// is not in the list, then there is no need to do anything further, | |
// it is not in the database. | |
if ($path === '/' || !$this->whitelist->get(strtok(trim($path, '/'), '/'))) { | |
return $path; | |
} | |
// During the first call to this method per language, load the expected | |
// paths for the page from cache. | |
if (empty($this->langcodePreloaded[$langcode])) { | |
$this->langcodePreloaded[$langcode] = TRUE; | |
$this->lookupMap[$langcode] = array(); | |
// Load the cached paths that should be used for preloading. This only | |
// happens if a cache key has been set. | |
if ($this->preloadedPathLookups === FALSE) { | |
$this->preloadedPathLookups = array(); | |
if ($this->cacheKey) { | |
if ($cached = $this->cache->get($this->cacheKey)) { | |
$this->preloadedPathLookups = $cached->data; | |
} | |
else { | |
$this->cacheNeedsWriting = TRUE; | |
} | |
} | |
} | |
// Load paths from cache. | |
if (!empty($this->preloadedPathLookups[$langcode])) { | |
$this->lookupMap[$langcode] = $this->storage->preloadPathAlias($this->preloadedPathLookups[$langcode], $langcode); | |
// Keep a record of paths with no alias to avoid querying twice. | |
$this->noAlias[$langcode] = array_flip(array_diff_key($this->preloadedPathLookups[$langcode], array_keys($this->lookupMap[$langcode]))); | |
} | |
} | |
// If we already know that there are no aliases for this path simply return. | |
if (!empty($this->noAlias[$langcode][$path])) { | |
return $path; | |
} | |
// If the alias has already been loaded, return it from static cache. | |
if (isset($this->lookupMap[$langcode][$path])) { | |
return $this->lookupMap[$langcode][$path]; | |
} | |
// Try to load alias from storage. | |
if ($alias = $this->storage->lookupPathAlias($path, $langcode)) { | |
$this->lookupMap[$langcode][$path] = $alias; | |
return $alias; | |
} | |
// We can't record anything into $this->lookupMap because we didn't find any | |
// aliases for this path. Thus cache to $this->noAlias. | |
$this->noAlias[$langcode][$path] = TRUE; | |
return $path; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function cacheClear($source = NULL) { | |
if ($source) { | |
foreach (array_keys($this->lookupMap) as $lang) { | |
unset($this->lookupMap[$lang][$source]); | |
} | |
} | |
else { | |
$this->lookupMap = array(); | |
} | |
$this->noPath = array(); | |
$this->noAlias = array(); | |
$this->langcodePreloaded = array(); | |
$this->preloadedPathLookups = array(); | |
$this->cache->delete($this->cacheKey); | |
$this->pathAliasWhitelistRebuild($source); | |
} | |
/** | |
* Rebuild the path alias white list. | |
* | |
* @param string $path | |
* An optional path for which an alias is being inserted. | |
* | |
* @return | |
* An array containing a white list of path aliases. | |
*/ | |
protected function pathAliasWhitelistRebuild($path = NULL) { | |
// When paths are inserted, only rebuild the whitelist if the path has a top | |
// level component which is not already in the whitelist. | |
if (!empty($path)) { | |
if ($this->whitelist->get(strtok($path, '/'))) { | |
return; | |
} | |
} | |
$this->whitelist->clear(); | |
} | |
/** | |
* Wrapper method for REQUEST_TIME constant. | |
* | |
* @return int | |
*/ | |
protected function getRequestTime() { | |
return defined('REQUEST_TIME') ? REQUEST_TIME : (int) $_SERVER['REQUEST_TIME']; | |
} | |
} |