Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
261
core/lib/Drupal/Core/Cache/ApcuBackend.php
Normal file
261
core/lib/Drupal/Core/Cache/ApcuBackend.php
Normal file
|
@ -0,0 +1,261 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\ApcuBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Stores cache items in the Alternative PHP Cache User Cache (APCu).
|
||||
*/
|
||||
class ApcuBackend implements CacheBackendInterface {
|
||||
|
||||
/**
|
||||
* The name of the cache bin to use.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $bin;
|
||||
|
||||
/**
|
||||
* Prefix for all keys in the storage that belong to this site.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sitePrefix;
|
||||
|
||||
/**
|
||||
* Prefix for all keys in this cache bin.
|
||||
*
|
||||
* Includes the site-specific prefix in $sitePrefix.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $binPrefix;
|
||||
|
||||
/**
|
||||
* The cache tags checksum provider.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsChecksumInterface
|
||||
*/
|
||||
protected $checksumProvider;
|
||||
|
||||
/**
|
||||
* Constructs a new ApcuBackend instance.
|
||||
*
|
||||
* @param string $bin
|
||||
* The name of the cache bin.
|
||||
* @param string $site_prefix
|
||||
* The prefix to use for all keys in the storage that belong to this site.
|
||||
* @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
|
||||
* The cache tags checksum provider.
|
||||
*/
|
||||
public function __construct($bin, $site_prefix, CacheTagsChecksumInterface $checksum_provider) {
|
||||
$this->bin = $bin;
|
||||
$this->sitePrefix = $site_prefix;
|
||||
$this->checksumProvider = $checksum_provider;
|
||||
$this->binPrefix = $this->sitePrefix . '::' . $this->bin . '::';
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends the APC user variable prefix for this bin to a cache item ID.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache item ID to prefix.
|
||||
*
|
||||
* @return string
|
||||
* The APCu key for the cache item ID.
|
||||
*/
|
||||
protected function getApcuKey($cid) {
|
||||
return $this->binPrefix . $cid;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
$cache = apc_fetch($this->getApcuKey($cid));
|
||||
return $this->prepareItem($cache, $allow_invalid);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
// Translate the requested cache item IDs to APCu keys.
|
||||
$map = array();
|
||||
foreach ($cids as $cid) {
|
||||
$map[$this->getApcuKey($cid)] = $cid;
|
||||
}
|
||||
|
||||
$result = apc_fetch(array_keys($map));
|
||||
$cache = array();
|
||||
if ($result) {
|
||||
foreach ($result as $key => $item) {
|
||||
$item = $this->prepareItem($item, $allow_invalid);
|
||||
if ($item) {
|
||||
$cache[$map[$key]] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
unset($result);
|
||||
|
||||
$cids = array_diff($cids, array_keys($cache));
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cached items, optionally limited by a cache ID prefix.
|
||||
*
|
||||
* APCu is a memory cache, shared across all server processes. To prevent
|
||||
* cache item clashes with other applications/installations, every cache item
|
||||
* is prefixed with a unique string for this site. Therefore, functions like
|
||||
* apc_clear_cache() cannot be used, and instead, a list of all cache items
|
||||
* belonging to this application need to be retrieved through this method
|
||||
* instead.
|
||||
*
|
||||
* @param string $prefix
|
||||
* (optional) A cache ID prefix to limit the result to.
|
||||
*
|
||||
* @return \APCIterator
|
||||
* An APCIterator containing matched items.
|
||||
*/
|
||||
protected function getAll($prefix = '') {
|
||||
return new \APCIterator('user', '/^' . preg_quote($this->getApcuKey($prefix), '/') . '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a cached item.
|
||||
*
|
||||
* Checks that the item is either permanent or did not expire.
|
||||
*
|
||||
* @param \stdClass $cache
|
||||
* An item loaded from cache_get() or cache_get_multiple().
|
||||
* @param bool $allow_invalid
|
||||
* If TRUE, a cache item may be returned even if it is expired or has been
|
||||
* invalidated. See ::get().
|
||||
*
|
||||
* @return mixed
|
||||
* The cache item or FALSE if the item expired.
|
||||
*/
|
||||
protected function prepareItem($cache, $allow_invalid) {
|
||||
if (!isset($cache->data)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$cache->tags = $cache->tags ? explode(' ', $cache->tags) : array();
|
||||
|
||||
// Check expire time.
|
||||
$cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
|
||||
|
||||
// Check if invalidateTags() has been called with any of the entry's tags.
|
||||
if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
|
||||
$cache->valid = FALSE;
|
||||
}
|
||||
|
||||
if (!$allow_invalid && !$cache->valid) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
$tags = array_unique($tags);
|
||||
$cache = new \stdClass();
|
||||
$cache->cid = $cid;
|
||||
$cache->created = round(microtime(TRUE), 3);
|
||||
$cache->expire = $expire;
|
||||
$cache->tags = implode(' ', $tags);
|
||||
$cache->checksum = $this->checksumProvider->getCurrentChecksum($tags);
|
||||
// APC serializes/unserializes any structure itself.
|
||||
$cache->serialized = 0;
|
||||
$cache->data = $data;
|
||||
|
||||
// apc_store()'s $ttl argument can be omitted but also set to 0 (zero),
|
||||
// in which case the value will persist until it's removed from the cache or
|
||||
// until the next cache clear, restart, etc. This is what we want to do
|
||||
// when $expire equals CacheBackendInterface::CACHE_PERMANENT.
|
||||
if ($expire === CacheBackendInterface::CACHE_PERMANENT) {
|
||||
$expire = 0;
|
||||
}
|
||||
apc_store($this->getApcuKey($cid), $cache, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items = array()) {
|
||||
foreach ($items as $cid => $item) {
|
||||
$this->set($cid, $item['data'], isset($item['expire']) ? $item['expire'] : CacheBackendInterface::CACHE_PERMANENT, isset($item['tags']) ? $item['tags'] : array());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($cid) {
|
||||
apc_delete($this->getApcuKey($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
apc_delete(array_map(array($this, 'getApcuKey'), $cids));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll() {
|
||||
apc_delete(new \APCIterator('user', '/^' . preg_quote($this->binPrefix, '/') . '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
// APC performs garbage collection automatically.
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
apc_delete(new \APCIterator('user', '/^' . preg_quote($this->binPrefix, '/') . '/'));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
$this->invalidateMultiple(array($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
foreach ($this->getMultiple($cids) as $cache) {
|
||||
$this->set($cache->cid, $cache, REQUEST_TIME - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
foreach ($this->getAll() as $data) {
|
||||
$cid = str_replace($this->binPrefix, '', $data['key']);
|
||||
$this->set($cid, $data['value'], REQUEST_TIME - 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
56
core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
Normal file
56
core/lib/Drupal/Core/Cache/ApcuBackendFactory.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\ApcuBackendFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\Site\Settings;
|
||||
|
||||
class ApcuBackendFactory implements CacheFactoryInterface {
|
||||
|
||||
/**
|
||||
* The site prefix string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $sitePrefix;
|
||||
|
||||
/**
|
||||
* The cache tags checksum provider.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsChecksumInterface
|
||||
*/
|
||||
protected $checksumProvider;
|
||||
|
||||
/**
|
||||
* Constructs an ApcuBackendFactory object.
|
||||
*
|
||||
* @param string $root
|
||||
* The app root.
|
||||
* @param string $site_path
|
||||
* The site path.
|
||||
* @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
|
||||
* The cache tags checksum provider.
|
||||
*/
|
||||
public function __construct($root, $site_path, CacheTagsChecksumInterface $checksum_provider) {
|
||||
$this->sitePrefix = Settings::getApcuPrefix('apcu_backend', $root, $site_path);
|
||||
$this->checksumProvider = $checksum_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets ApcuBackend for the specified cache bin.
|
||||
*
|
||||
* @param $bin
|
||||
* The cache bin for which the object is created.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\ApcuBackend
|
||||
* The cache backend object for the specified cache bin.
|
||||
*/
|
||||
public function get($bin) {
|
||||
return new ApcuBackend($bin, $this->sitePrefix, $this->checksumProvider);
|
||||
}
|
||||
|
||||
}
|
226
core/lib/Drupal/Core/Cache/BackendChain.php
Normal file
226
core/lib/Drupal/Core/Cache/BackendChain.php
Normal file
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\BackendChain.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
/**
|
||||
* Defines a chained cache implementation for combining multiple cache backends.
|
||||
*
|
||||
* Can be used to combine two or more backends together to behave as if they
|
||||
* were a single backend.
|
||||
*
|
||||
* For example a slower, persistent storage engine could be combined with a
|
||||
* faster, volatile storage engine. When retrieving items from cache, they will
|
||||
* be fetched from the volatile backend first, only falling back to the
|
||||
* persistent backend if an item is not available. An item not present in the
|
||||
* volatile backend but found in the persistent one will be propagated back up
|
||||
* to ensure fast retrieval on the next request. On cache sets and deletes, both
|
||||
* backends will be invoked to ensure consistency.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
|
||||
class BackendChain implements CacheBackendInterface, CacheTagsInvalidatorInterface {
|
||||
|
||||
/**
|
||||
* Ordered list of CacheBackendInterface instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $backends = array();
|
||||
|
||||
/**
|
||||
* Constructs a DatabaseBackend object.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which the object is created.
|
||||
*/
|
||||
public function __construct($bin) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a cache backend to the cache chain.
|
||||
*
|
||||
* @param CacheBackendInterface $backend
|
||||
* The cache backend to be appended to the cache chain.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\BackendChain
|
||||
* The called object.
|
||||
*/
|
||||
public function appendBackend(CacheBackendInterface $backend) {
|
||||
$this->backends[] = $backend;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepends a cache backend to the cache chain.
|
||||
*
|
||||
* @param CacheBackendInterface $backend
|
||||
* The backend to be prepended to the cache chain.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\BackendChain
|
||||
* The called object.
|
||||
*/
|
||||
public function prependBackend(CacheBackendInterface $backend) {
|
||||
array_unshift($this->backends, $backend);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::get().
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
foreach ($this->backends as $index => $backend) {
|
||||
if (($return = $backend->get($cid, $allow_invalid)) !== FALSE) {
|
||||
// We found a result, propagate it to all missed backends.
|
||||
if ($index > 0) {
|
||||
for ($i = ($index - 1); 0 <= $i; --$i) {
|
||||
$this->backends[$i]->set($cid, $return->data, $return->expire, $return->tags);
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$return = array();
|
||||
|
||||
foreach ($this->backends as $index => $backend) {
|
||||
$items = $backend->getMultiple($cids, $allow_invalid);
|
||||
|
||||
// Propagate the values that could be retrieved from the current cache
|
||||
// backend to all missed backends.
|
||||
if ($index > 0 && !empty($items)) {
|
||||
for ($i = ($index - 1); 0 <= $i; --$i) {
|
||||
foreach ($items as $cached) {
|
||||
$this->backends[$i]->set($cached->cid, $cached->data, $cached->expire, $cached->tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Append the values to the previously retrieved ones.
|
||||
$return += $items;
|
||||
|
||||
if (empty($cids)) {
|
||||
// No need to go further if we don't have any cid to fetch left.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->set($cid, $data, $expire, $tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->setMultiple($items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::delete().
|
||||
*/
|
||||
public function delete($cid) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->delete($cid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->deleteMultiple($cids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
|
||||
*/
|
||||
public function deleteAll() {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidate().
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->invalidate($cid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple().
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->invalidateMultiple($cids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
foreach ($this->backends as $backend) {
|
||||
if ($backend instanceof CacheTagsInvalidatorInterface) {
|
||||
$backend->invalidateTags($tags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->invalidateAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->garbageCollection();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
foreach ($this->backends as $backend) {
|
||||
$backend->removeBin();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
196
core/lib/Drupal/Core/Cache/Cache.php
Normal file
196
core/lib/Drupal/Core/Cache/Cache.php
Normal file
|
@ -0,0 +1,196 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Cache.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
|
||||
/**
|
||||
* Helper methods for cache.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class Cache {
|
||||
|
||||
/**
|
||||
* Indicates that the item should never be removed unless explicitly deleted.
|
||||
*/
|
||||
const PERMANENT = CacheBackendInterface::CACHE_PERMANENT;
|
||||
|
||||
/**
|
||||
* Merges arrays of cache contexts and removes duplicates.
|
||||
*
|
||||
* @param string[] …
|
||||
* Arrays of cache contexts to merge.
|
||||
*
|
||||
* @return string[]
|
||||
* The merged array of cache contexts.
|
||||
*/
|
||||
public static function mergeContexts() {
|
||||
$cache_context_arrays = func_get_args();
|
||||
$cache_contexts = [];
|
||||
foreach ($cache_context_arrays as $contexts) {
|
||||
$cache_contexts = array_merge($cache_contexts, $contexts);
|
||||
}
|
||||
$cache_contexts = array_unique($cache_contexts);
|
||||
\Drupal::service('cache_contexts_manager')->validateTokens($cache_contexts);
|
||||
sort($cache_contexts);
|
||||
return $cache_contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges arrays of cache tags and removes duplicates.
|
||||
*
|
||||
* The cache tags array is returned in a format that is valid for
|
||||
* \Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*
|
||||
* When caching elements, it is necessary to collect all cache tags into a
|
||||
* single array, from both the element itself and all child elements. This
|
||||
* allows items to be invalidated based on all tags attached to the content
|
||||
* they're constituted from.
|
||||
*
|
||||
* @param string[] …
|
||||
* Arrays of cache tags to merge.
|
||||
*
|
||||
* @return string[]
|
||||
* The merged array of cache tags.
|
||||
*/
|
||||
public static function mergeTags() {
|
||||
$cache_tag_arrays = func_get_args();
|
||||
$cache_tags = [];
|
||||
foreach ($cache_tag_arrays as $tags) {
|
||||
$cache_tags = array_merge($cache_tags, $tags);
|
||||
}
|
||||
$cache_tags = array_unique($cache_tags);
|
||||
static::validateTags($cache_tags);
|
||||
sort($cache_tags);
|
||||
return $cache_tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges max-age values (expressed in seconds), finds the lowest max-age.
|
||||
*
|
||||
* Ensures infinite max-age (Cache::PERMANENT) is taken into account.
|
||||
*
|
||||
* @param int …
|
||||
* Max-age values.
|
||||
*
|
||||
* @return int
|
||||
* The minimum max-age value.
|
||||
*/
|
||||
public static function mergeMaxAges() {
|
||||
$max_ages = func_get_args();
|
||||
|
||||
// Filter out all max-age values set to cache permanently.
|
||||
if (in_array(Cache::PERMANENT, $max_ages)) {
|
||||
$max_ages = array_filter($max_ages, function ($max_age) {
|
||||
return $max_age !== Cache::PERMANENT;
|
||||
});
|
||||
|
||||
// If nothing is left, then all max-age values were set to cache
|
||||
// permanently, and then that is the result.
|
||||
if (empty($max_ages)) {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
}
|
||||
|
||||
return min($max_ages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an array of cache tags.
|
||||
*
|
||||
* Can be called before using cache tags in operations, to ensure validity.
|
||||
*
|
||||
* @param string[] $tags
|
||||
* An array of cache tags.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public static function validateTags(array $tags) {
|
||||
if (empty($tags)) {
|
||||
return;
|
||||
}
|
||||
foreach ($tags as $value) {
|
||||
if (!is_string($value)) {
|
||||
throw new \LogicException('Cache tags must be strings, ' . gettype($value) . ' given.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an array of cache tags from a given prefix and an array of suffixes.
|
||||
*
|
||||
* Each suffix will be converted to a cache tag by appending it to the prefix,
|
||||
* with a colon between them.
|
||||
*
|
||||
* @param string $prefix
|
||||
* A prefix string.
|
||||
* @param array $suffixes
|
||||
* An array of suffixes. Will be cast to strings.
|
||||
* @param string $glue
|
||||
* A string to be used as glue for concatenation. Defaults to a colon.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of cache tags.
|
||||
*/
|
||||
public static function buildTags($prefix, array $suffixes, $glue = ':') {
|
||||
$tags = [];
|
||||
foreach ($suffixes as $suffix) {
|
||||
$tags[] = $prefix . $glue . $suffix;
|
||||
}
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks cache items from all bins with any of the specified tags as invalid.
|
||||
*
|
||||
* @param string[] $tags
|
||||
* The list of tags to invalidate cache items for.
|
||||
*/
|
||||
public static function invalidateTags(array $tags) {
|
||||
\Drupal::service('cache_tags.invalidator')->invalidateTags($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all cache bin services.
|
||||
*
|
||||
* @return array
|
||||
* An array of cache backend objects keyed by cache bins.
|
||||
*/
|
||||
public static function getBins() {
|
||||
$bins = array();
|
||||
$container = \Drupal::getContainer();
|
||||
foreach ($container->getParameter('cache_bins') as $service_id => $bin) {
|
||||
$bins[$bin] = $container->get($service_id);
|
||||
}
|
||||
return $bins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a hash from a query object, to be used as part of the cache key.
|
||||
*
|
||||
* This smart caching strategy saves Drupal from querying and rendering to
|
||||
* HTML when the underlying query is unchanged.
|
||||
*
|
||||
* Expensive queries should use the query builder to create the query and then
|
||||
* call this function. Executing the query and formatting results should
|
||||
* happen in a #pre_render callback.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Query\SelectInterface $query
|
||||
* A select query object.
|
||||
*
|
||||
* @return string
|
||||
* A hash of the query arguments.
|
||||
*/
|
||||
public static function keyFromQuery(SelectInterface $query) {
|
||||
$query->preExecute();
|
||||
$keys = array((string) $query, $query->getArguments());
|
||||
return hash('sha256', serialize($keys));
|
||||
}
|
||||
|
||||
}
|
223
core/lib/Drupal/Core/Cache/CacheBackendInterface.php
Normal file
223
core/lib/Drupal/Core/Cache/CacheBackendInterface.php
Normal file
|
@ -0,0 +1,223 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheBackendInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines an interface for cache implementations.
|
||||
*
|
||||
* All cache implementations have to implement this interface.
|
||||
* Drupal\Core\Cache\DatabaseBackend provides the default implementation, which
|
||||
* can be consulted as an example.
|
||||
*
|
||||
* The cache indentifiers are case sensitive.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
interface CacheBackendInterface {
|
||||
|
||||
/**
|
||||
* Indicates that the item should never be removed unless explicitly deleted.
|
||||
*/
|
||||
const CACHE_PERMANENT = -1;
|
||||
|
||||
/**
|
||||
* Returns data from the persistent cache.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache ID of the data to retrieve.
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, a cache item may be returned even if it is expired or
|
||||
* has been invalidated. Such items may sometimes be preferred, if the
|
||||
* alternative is recalculating the value stored in the cache, especially
|
||||
* if another concurrent request is already recalculating the same value.
|
||||
* The "valid" property of the returned object indicates whether the item is
|
||||
* valid or not. Defaults to FALSE.
|
||||
*
|
||||
* @return object|false
|
||||
* The cache item or FALSE on failure.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE);
|
||||
|
||||
/**
|
||||
* Returns data from the persistent cache when given an array of cache IDs.
|
||||
*
|
||||
* @param array $cids
|
||||
* An array of cache IDs for the data to retrieve. This is passed by
|
||||
* reference, and will have the IDs successfully returned from cache
|
||||
* removed.
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, cache items may be returned even if they have expired
|
||||
* or been invalidated. Such items may sometimes be preferred, if the
|
||||
* alternative is recalculating the value stored in the cache, especially
|
||||
* if another concurrent thread is already recalculating the same value. The
|
||||
* "valid" property of the returned objects indicates whether the items are
|
||||
* valid or not. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of cache item objects indexed by cache ID.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::get()
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE);
|
||||
|
||||
/**
|
||||
* Stores data in the persistent cache.
|
||||
*
|
||||
* Core cache implementations set the created time on cache item with
|
||||
* microtime(TRUE) rather than REQUEST_TIME_FLOAT, because the created time
|
||||
* of cache items should match when they are created, not when the request
|
||||
* started. Apart from being more accurate, this increases the chance an
|
||||
* item will legitimately be considered valid.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache ID of the data to store.
|
||||
* @param mixed $data
|
||||
* The data to store in the cache.
|
||||
* Some storage engines only allow objects up to a maximum of 1MB in size to
|
||||
* be stored by default. When caching large arrays or similar, take care to
|
||||
* ensure $data does not exceed this size.
|
||||
* @param int $expire
|
||||
* One of the following values:
|
||||
* - CacheBackendInterface::CACHE_PERMANENT: Indicates that the item should
|
||||
* not be removed unless it is deleted explicitly.
|
||||
* - A Unix timestamp: Indicates that the item will be considered invalid
|
||||
* after this time, i.e. it will not be returned by get() unless
|
||||
* $allow_invalid has been set to TRUE. When the item has expired, it may
|
||||
* be permanently deleted by the garbage collector at any time.
|
||||
* @param array $tags
|
||||
* An array of tags to be stored with the cache item. These should normally
|
||||
* identify objects used to build the cache item, which should trigger
|
||||
* cache invalidation when updated. For example if a cached item represents
|
||||
* a node, both the node ID and the author's user ID might be passed in as
|
||||
* tags. For example array('node' => array(123), 'user' => array(92)).
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::get()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array());
|
||||
|
||||
/**
|
||||
* Store multiple items in the persistent cache.
|
||||
*
|
||||
* @param array $items
|
||||
* An array of cache items, keyed by cid. In the form:
|
||||
* @code
|
||||
* $items = array(
|
||||
* $cid => array(
|
||||
* // Required, will be automatically serialized if not a string.
|
||||
* 'data' => $data,
|
||||
* // Optional, defaults to CacheBackendInterface::CACHE_PERMANENT.
|
||||
* 'expire' => CacheBackendInterface::CACHE_PERMANENT,
|
||||
* // (optional) The cache tags for this item, see CacheBackendInterface::set().
|
||||
* 'tags' => array(),
|
||||
* ),
|
||||
* );
|
||||
* @endcode
|
||||
*/
|
||||
public function setMultiple(array $items);
|
||||
|
||||
/**
|
||||
* Deletes an item from the cache.
|
||||
*
|
||||
* If the cache item is being deleted because it is no longer "fresh", you may
|
||||
* consider using invalidate() instead. This allows callers to retrieve the
|
||||
* invalid item by calling get() with $allow_invalid set to TRUE. In some cases
|
||||
* an invalid item may be acceptable rather than having to rebuild the cache.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache ID to delete.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidate()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll()
|
||||
*/
|
||||
public function delete($cid);
|
||||
|
||||
/**
|
||||
* Deletes multiple items from the cache.
|
||||
*
|
||||
* If the cache items are being deleted because they are no longer "fresh",
|
||||
* you may consider using invalidateMultiple() instead. This allows callers to
|
||||
* retrieve the invalid items by calling get() with $allow_invalid set to TRUE.
|
||||
* In some cases an invalid item may be acceptable rather than having to
|
||||
* rebuild the cache.
|
||||
*
|
||||
* @param array $cids
|
||||
* An array of cache IDs to delete.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::delete()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll()
|
||||
*/
|
||||
public function deleteMultiple(array $cids);
|
||||
|
||||
/**
|
||||
* Deletes all cache items in a bin.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::delete()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
|
||||
*/
|
||||
public function deleteAll();
|
||||
|
||||
/**
|
||||
* Marks a cache item as invalid.
|
||||
*
|
||||
* Invalid items may be returned in later calls to get(), if the $allow_invalid
|
||||
* argument is TRUE.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache ID to invalidate.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::delete()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll()
|
||||
*/
|
||||
public function invalidate($cid);
|
||||
|
||||
/**
|
||||
* Marks cache items as invalid.
|
||||
*
|
||||
* Invalid items may be returned in later calls to get(), if the $allow_invalid
|
||||
* argument is TRUE.
|
||||
*
|
||||
* @param string[] $cids
|
||||
* An array of cache IDs to invalidate.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::deleteMultiple()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidate()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidateAll()
|
||||
*/
|
||||
public function invalidateMultiple(array $cids);
|
||||
|
||||
/**
|
||||
* Marks all cache items as invalid.
|
||||
*
|
||||
* Invalid items may be returned in later calls to get(), if the $allow_invalid
|
||||
* argument is TRUE.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::deleteAll()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidate()
|
||||
* @see \Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple()
|
||||
*/
|
||||
public function invalidateAll();
|
||||
|
||||
/**
|
||||
* Performs garbage collection on a cache bin.
|
||||
*
|
||||
* The backend may choose to delete expired or invalidated items.
|
||||
*/
|
||||
public function garbageCollection();
|
||||
|
||||
/**
|
||||
* Remove a cache bin.
|
||||
*/
|
||||
public function removeBin();
|
||||
}
|
323
core/lib/Drupal/Core/Cache/CacheCollector.php
Normal file
323
core/lib/Drupal/Core/Cache/CacheCollector.php
Normal file
|
@ -0,0 +1,323 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheCollector.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\DestructableInterface;
|
||||
use Drupal\Core\Lock\LockBackendInterface;
|
||||
|
||||
/**
|
||||
* Default implementation for CacheCollectorInterface.
|
||||
*
|
||||
* By default, the class accounts for caches where calling functions might
|
||||
* request keys that won't exist even after a cache rebuild. This prevents
|
||||
* situations where a cache rebuild would be triggered over and over due to a
|
||||
* 'missing' item. These cases are stored internally as a value of NULL. This
|
||||
* means that the CacheCollector::get() method must be overridden if caching
|
||||
* data where the values can legitimately be NULL, and where
|
||||
* CacheCollector->has() needs to correctly return (equivalent to
|
||||
* array_key_exists() vs. isset()). This should not be necessary in the majority
|
||||
* of cases.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
abstract class CacheCollector implements CacheCollectorInterface, DestructableInterface {
|
||||
|
||||
/**
|
||||
* The cache id that is used for the cache entry.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $cid;
|
||||
|
||||
/**
|
||||
* A list of tags that are used for the cache entry.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tags;
|
||||
|
||||
/**
|
||||
* The cache backend that should be used.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* The lock backend that should be used.
|
||||
*
|
||||
* @var \Drupal\Core\Lock\LockBackendInterface
|
||||
*/
|
||||
protected $lock;
|
||||
|
||||
/**
|
||||
* An array of keys to add to the cache on service termination.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $keysToPersist = array();
|
||||
|
||||
/**
|
||||
* An array of keys to remove from the cache on service termination.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $keysToRemove = array();
|
||||
|
||||
/**
|
||||
* Storage for the data itself.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $storage = array();
|
||||
|
||||
/**
|
||||
* Stores the cache creation time.
|
||||
*
|
||||
* This is used to check if an invalidated cache item has been overwritten in
|
||||
* the meantime.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $cacheCreated;
|
||||
|
||||
/**
|
||||
* Flag that indicates of the cache has been invalidated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $cacheInvalidated = FALSE;
|
||||
|
||||
/**
|
||||
* Indicates if the collected cache was already loaded.
|
||||
*
|
||||
* The collected cache is lazy loaded when an entry is set, get or deleted.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $cacheLoaded = FALSE;
|
||||
|
||||
/**
|
||||
* Constructs a CacheCollector object.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cid for the array being cached.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
|
||||
* The cache backend.
|
||||
* @param \Drupal\Core\Lock\LockBackendInterface $lock
|
||||
* The lock backend.
|
||||
* @param array $tags
|
||||
* (optional) The tags to specify for the cache item.
|
||||
*/
|
||||
public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
$this->cid = $cid;
|
||||
$this->cache = $cache;
|
||||
$this->tags = $tags;
|
||||
$this->lock = $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache ID.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCid() {
|
||||
return $this->cid;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function has($key) {
|
||||
// Make sure the value is loaded.
|
||||
$this->get($key);
|
||||
return isset($this->storage[$key]) || array_key_exists($key, $this->storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key) {
|
||||
$this->lazyLoadCache();
|
||||
if (isset($this->storage[$key]) || array_key_exists($key, $this->storage)) {
|
||||
return $this->storage[$key];
|
||||
}
|
||||
else {
|
||||
return $this->resolveCacheMiss($key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Cache\CacheCollectorInterface::set().
|
||||
*
|
||||
* This is not persisted by default. In practice this means that setting a
|
||||
* value will only apply while the object is in scope and will not be written
|
||||
* back to the persistent cache. This follows a similar pattern to static vs.
|
||||
* persistent caching in procedural code. Extending classes may wish to alter
|
||||
* this behavior, for example by adding a call to persist().
|
||||
*/
|
||||
public function set($key, $value) {
|
||||
$this->lazyLoadCache();
|
||||
$this->storage[$key] = $value;
|
||||
// The key might have been marked for deletion.
|
||||
unset($this->keysToRemove[$key]);
|
||||
$this->invalidateCache();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($key) {
|
||||
$this->lazyLoadCache();
|
||||
unset($this->storage[$key]);
|
||||
$this->keysToRemove[$key] = $key;
|
||||
// The key might have been marked for persisting.
|
||||
unset($this->keysToPersist[$key]);
|
||||
$this->invalidateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags an offset value to be written to the persistent cache.
|
||||
*
|
||||
* @param string $key
|
||||
* The key that was requested.
|
||||
* @param bool $persist
|
||||
* (optional) Whether the offset should be persisted or not, defaults to
|
||||
* TRUE. When called with $persist = FALSE the offset will be unflagged so
|
||||
* that it will not be written at the end of the request.
|
||||
*/
|
||||
protected function persist($key, $persist = TRUE) {
|
||||
$this->keysToPersist[$key] = $persist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a cache miss.
|
||||
*
|
||||
* When an offset is not found in the object, this is treated as a cache
|
||||
* miss. This method allows classes using this implementation to look up the
|
||||
* actual value and allow it to be cached.
|
||||
*
|
||||
* @param string $key
|
||||
* The offset that was requested.
|
||||
*
|
||||
* @return mixed
|
||||
* The value of the offset, or NULL if no value was found.
|
||||
*/
|
||||
abstract protected function resolveCacheMiss($key);
|
||||
|
||||
/**
|
||||
* Writes a value to the persistent cache immediately.
|
||||
*
|
||||
* @param bool $lock
|
||||
* (optional) Whether to acquire a lock before writing to cache. Defaults to
|
||||
* TRUE.
|
||||
*/
|
||||
protected function updateCache($lock = TRUE) {
|
||||
$data = array();
|
||||
foreach ($this->keysToPersist as $offset => $persist) {
|
||||
if ($persist) {
|
||||
$data[$offset] = $this->storage[$offset];
|
||||
}
|
||||
}
|
||||
if (empty($data) && empty($this->keysToRemove)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Lock cache writes to help avoid stampedes.
|
||||
$cid = $this->getCid();
|
||||
$lock_name = $cid . ':' . __CLASS__;
|
||||
if (!$lock || $this->lock->acquire($lock_name)) {
|
||||
// Set and delete operations invalidate the cache item. Try to also load
|
||||
// an eventually invalidated cache entry, only update an invalidated cache
|
||||
// entry if the creation date did not change as this could result in an
|
||||
// inconsistent cache.
|
||||
if ($cache = $this->cache->get($cid, $this->cacheInvalidated)) {
|
||||
if ($this->cacheInvalidated && $cache->created != $this->cacheCreated) {
|
||||
// We have invalidated the cache in this request and got a different
|
||||
// cache entry. Do not attempt to overwrite data that might have been
|
||||
// changed in a different request. We'll let the cache rebuild in
|
||||
// later requests.
|
||||
$this->cache->delete($cid);
|
||||
$this->lock->release($lock_name);
|
||||
return;
|
||||
}
|
||||
$data = array_merge($cache->data, $data);
|
||||
}
|
||||
// Remove keys marked for deletion.
|
||||
foreach ($this->keysToRemove as $delete_key) {
|
||||
unset($data[$delete_key]);
|
||||
}
|
||||
$this->cache->set($cid, $data, Cache::PERMANENT, $this->tags);
|
||||
if ($lock) {
|
||||
$this->lock->release($lock_name);
|
||||
}
|
||||
}
|
||||
|
||||
$this->keysToPersist = array();
|
||||
$this->keysToRemove = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset() {
|
||||
$this->storage = array();
|
||||
$this->keysToPersist = array();
|
||||
$this->keysToRemove = array();
|
||||
$this->cacheLoaded = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clear() {
|
||||
$this->reset();
|
||||
if ($this->tags) {
|
||||
Cache::invalidateTags($this->tags);
|
||||
}
|
||||
else {
|
||||
$this->cache->delete($this->getCid());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destruct() {
|
||||
$this->updateCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the cache if not already done.
|
||||
*/
|
||||
protected function lazyLoadCache() {
|
||||
if ($this->cacheLoaded) {
|
||||
return;
|
||||
}
|
||||
// The cache was not yet loaded, set flag to TRUE.
|
||||
$this->cacheLoaded = TRUE;
|
||||
|
||||
if ($cache = $this->cache->get($this->getCid())) {
|
||||
$this->cacheCreated = $cache->created;
|
||||
$this->storage = $cache->data;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate the cache.
|
||||
*/
|
||||
protected function invalidateCache() {
|
||||
// Invalidate the cache to make sure that other requests immediately see the
|
||||
// deletion before this request is terminated.
|
||||
$this->cache->invalidate($this->getCid());
|
||||
$this->cacheInvalidated = TRUE;
|
||||
}
|
||||
|
||||
}
|
81
core/lib/Drupal/Core/Cache/CacheCollectorInterface.php
Normal file
81
core/lib/Drupal/Core/Cache/CacheCollectorInterface.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheCollectorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Provides a caching wrapper to be used in place of large structures.
|
||||
*
|
||||
* This should be extended by systems that need to cache large amounts of data
|
||||
* to calling functions. These structures can become very large, so this
|
||||
* class is used to allow different strategies to be used for caching internally
|
||||
* (lazy loading, building caches over time etc.). This can dramatically reduce
|
||||
* the amount of data that needs to be loaded from cache backends on each
|
||||
* request, and memory usage from static caches of that same data.
|
||||
*
|
||||
* The default implementation is \Drupal\Core\Cache\CacheCollector.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
interface CacheCollectorInterface {
|
||||
|
||||
/**
|
||||
* Gets value from the cache.
|
||||
*
|
||||
* @param string $key
|
||||
* Key that identifies the data.
|
||||
*
|
||||
* @return mixed
|
||||
* The corresponding cache data.
|
||||
*/
|
||||
public function get($key);
|
||||
|
||||
/**
|
||||
* Sets cache data.
|
||||
*
|
||||
* It depends on the specific case and implementation whether this has a
|
||||
* permanent effect or if it just affects the current request.
|
||||
*
|
||||
* @param string $key
|
||||
* Key that identifies the data.
|
||||
* @param mixed $value
|
||||
* The data to be set.
|
||||
*/
|
||||
public function set($key, $value);
|
||||
|
||||
/**
|
||||
* Deletes the element.
|
||||
*
|
||||
* It depends on the specific case and implementation whether this has a
|
||||
* permanent effect or if it just affects the current request.
|
||||
*
|
||||
* @param string $key
|
||||
* Key that identifies the data.
|
||||
*/
|
||||
public function delete($key);
|
||||
|
||||
/**
|
||||
* Returns whether data exists for this key.
|
||||
*
|
||||
* @param string $key
|
||||
* Key that identifies the data.
|
||||
*/
|
||||
public function has($key);
|
||||
|
||||
/**
|
||||
* Resets the local cache.
|
||||
*
|
||||
* Does not clear the persistent cache.
|
||||
*/
|
||||
public function reset();
|
||||
|
||||
/**
|
||||
* Clears the collected cache entry.
|
||||
*/
|
||||
public function clear();
|
||||
|
||||
}
|
88
core/lib/Drupal/Core/Cache/CacheFactory.php
Normal file
88
core/lib/Drupal/Core/Cache/CacheFactory.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines the cache backend factory.
|
||||
*/
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
class CacheFactory implements CacheFactoryInterface, ContainerAwareInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* The settings array.
|
||||
*
|
||||
* @var \Drupal\Core\Site\Settings
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* A map of cache bin to default cache backend service name.
|
||||
*
|
||||
* All mappings in $settings takes precedence over this, but this can be used
|
||||
* to optimize cache storage for a Drupal installation without cache
|
||||
* customizations in settings.php. For example, this can be used to map the
|
||||
* 'bootstrap' bin to 'cache.backend.chainedfast', while allowing other bins
|
||||
* to fall back to the global default of 'cache.backend.database'.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultBinBackends;
|
||||
|
||||
/**
|
||||
* Constructs CacheFactory object.
|
||||
*
|
||||
* @param \Drupal\Core\Site\Settings $settings
|
||||
* The settings array.
|
||||
* @param array $default_bin_backends
|
||||
* (optional) A mapping of bin to backend service name. Mappings in
|
||||
* $settings take precedence over this.
|
||||
*/
|
||||
public function __construct(Settings $settings, array $default_bin_backends = array()) {
|
||||
$this->settings = $settings;
|
||||
$this->defaultBinBackends = $default_bin_backends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a cache backend class for a given cache bin.
|
||||
*
|
||||
* By default, this returns an instance of the
|
||||
* Drupal\Core\Cache\DatabaseBackend class.
|
||||
*
|
||||
* Classes implementing Drupal\Core\Cache\CacheBackendInterface can register
|
||||
* themselves both as a default implementation and for specific bins.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which a cache backend object should be returned.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheBackendInterface
|
||||
* The cache backend object associated with the specified bin.
|
||||
*/
|
||||
public function get($bin) {
|
||||
$cache_settings = $this->settings->get('cache');
|
||||
if (isset($cache_settings['bins'][$bin])) {
|
||||
$service_name = $cache_settings['bins'][$bin];
|
||||
}
|
||||
elseif (isset($cache_settings['default'])) {
|
||||
$service_name = $cache_settings['default'];
|
||||
}
|
||||
elseif (isset($this->defaultBinBackends[$bin])) {
|
||||
$service_name = $this->defaultBinBackends[$bin];
|
||||
}
|
||||
else {
|
||||
$service_name = 'cache.backend.database';
|
||||
}
|
||||
return $this->container->get($service_name)->get($bin);
|
||||
}
|
||||
|
||||
}
|
26
core/lib/Drupal/Core/Cache/CacheFactoryInterface.php
Normal file
26
core/lib/Drupal/Core/Cache/CacheFactoryInterface.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheFactoryInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* An interface defining cache factory classes.
|
||||
*/
|
||||
interface CacheFactoryInterface {
|
||||
|
||||
/**
|
||||
* Gets a cache backend class for a given cache bin.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which a cache backend object should be returned.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheBackendInterface
|
||||
* The cache backend object associated with the specified bin.
|
||||
*/
|
||||
public function get($bin);
|
||||
|
||||
}
|
62
core/lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php
Normal file
62
core/lib/Drupal/Core/Cache/CacheTagsChecksumInterface.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheTagsChecksumInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Provides checksums for cache tag invalidations.
|
||||
*
|
||||
* Cache backends can use this to check if any cache tag invalidations happened
|
||||
* for a stored cache item.
|
||||
*
|
||||
* To do so, they can inject the cache_tags.invalidator.checksum service, and
|
||||
* when a cache item is written, store cache tags together with the current
|
||||
* checksum, calculated by getCurrentChecksum(). When a cache item is fetched,
|
||||
* the checksum can be validated with isValid(). The service will return FALSE
|
||||
* if any of those cache tags were invalidated in the meantime.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
interface CacheTagsChecksumInterface {
|
||||
|
||||
/**
|
||||
* Returns the sum total of validations for a given set of tags.
|
||||
*
|
||||
* Called by a backend when storing a cache item.
|
||||
*
|
||||
* @param string[] $tags
|
||||
* Array of cache tags.
|
||||
*
|
||||
* @return string
|
||||
* Cache tag invalidations checksum.
|
||||
*/
|
||||
public function getCurrentChecksum(array $tags);
|
||||
|
||||
/**
|
||||
* Returns whether the checksum is valid for the given cache tags.
|
||||
*
|
||||
* Used when retrieving a cache item in a cache backend, to verify that no
|
||||
* cache tag based invalidation happened.
|
||||
*
|
||||
* @param int $checksum
|
||||
* The checksum that was stored together with the cache item.
|
||||
* @param string[] $tags
|
||||
* The cache tags that were stored together with the cache item.
|
||||
*
|
||||
* @return bool
|
||||
* FALSE if cache tag invalidations happened for the passed in tags since
|
||||
* the cache item was stored, TRUE otherwise.
|
||||
*/
|
||||
public function isValid($checksum, array $tags);
|
||||
|
||||
/**
|
||||
* Reset statically cached tags.
|
||||
*
|
||||
* This is only used by tests.
|
||||
*/
|
||||
public function reset();
|
||||
}
|
85
core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
Normal file
85
core/lib/Drupal/Core/Cache/CacheTagsInvalidator.php
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheTagsInvalidator.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* Passes cache tag events to classes that wish to respond to them.
|
||||
*/
|
||||
class CacheTagsInvalidator implements CacheTagsInvalidatorInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* Holds an array of cache tags invalidators.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface[]
|
||||
*/
|
||||
protected $invalidators = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
// Validate the tags.
|
||||
Cache::validateTags($tags);
|
||||
|
||||
// Notify all added cache tags invalidators.
|
||||
foreach ($this->invalidators as $invalidator) {
|
||||
$invalidator->invalidateTags($tags);
|
||||
}
|
||||
|
||||
// Additionally, notify each cache bin if it implements the service.
|
||||
foreach ($this->getInvalidatorCacheBins() as $bin) {
|
||||
$bin->invalidateTags($tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset statically cached tags in all cache tag checksum services.
|
||||
*
|
||||
* This is only used by tests.
|
||||
*/
|
||||
public function resetChecksums() {
|
||||
foreach ($this->invalidators as $invalidator) {
|
||||
if ($invalidator instanceof CacheTagsChecksumInterface) {
|
||||
$invalidator->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a cache tags invalidator.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheTagsInvalidatorInterface $invalidator
|
||||
* A cache invalidator.
|
||||
*/
|
||||
public function addInvalidator(CacheTagsInvalidatorInterface $invalidator) {
|
||||
$this->invalidators[] = $invalidator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cache bins that need to be notified about invalidations.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheTagsInvalidatorInterface[]
|
||||
* An array of cache backend objects that implement the invalidator
|
||||
* interface, keyed by their cache bin.
|
||||
*/
|
||||
protected function getInvalidatorCacheBins() {
|
||||
$bins = array();
|
||||
foreach ($this->container->getParameter('cache_bins') as $service_id => $bin) {
|
||||
$service = $this->container->get($service_id);
|
||||
if ($service instanceof CacheTagsInvalidatorInterface) {
|
||||
$bins[$bin] = $service;
|
||||
}
|
||||
}
|
||||
return $bins;
|
||||
}
|
||||
|
||||
}
|
29
core/lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php
Normal file
29
core/lib/Drupal/Core/Cache/CacheTagsInvalidatorInterface.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheTagsInvalidatorInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines required methods for classes wanting to handle cache tag changes.
|
||||
*
|
||||
* Services that implement this interface must add the cache_tags_invalidator
|
||||
* tag to be notified. Cache backends may implement this interface as well, they
|
||||
* will be notified automatically.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
interface CacheTagsInvalidatorInterface {
|
||||
|
||||
/**
|
||||
* Marks cache items with any of the specified tags as invalid.
|
||||
*
|
||||
* @param string[] $tags
|
||||
* The list of tags for which to invalidate cache items.
|
||||
*/
|
||||
public function invalidateTags(array $tags);
|
||||
|
||||
}
|
56
core/lib/Drupal/Core/Cache/CacheableDependencyInterface.php
Normal file
56
core/lib/Drupal/Core/Cache/CacheableDependencyInterface.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheableDependencyInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines an interface for objects which may be used by other cached objects.
|
||||
*
|
||||
* All cacheability metadata exposed in this interface is bubbled to parent
|
||||
* objects when they are cached: if a child object needs to be varied by certain
|
||||
* cache contexts, invalidated by certain cache tags, expire after a certain
|
||||
* maximum age, then so should any parent object.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
interface CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* The cache contexts associated with this object.
|
||||
*
|
||||
* These identify a specific variation/representation of the object.
|
||||
*
|
||||
* Cache contexts are tokens: placeholders that are converted to cache keys by
|
||||
* the @cache_contexts_manager service. The replacement value depends on the
|
||||
* request context (the current URL, language, and so on). They're converted
|
||||
* before storing an object in cache.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of cache context tokens, used to generate a cache ID.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\Context\CacheContextsManager::convertTokensToKeys()
|
||||
*/
|
||||
public function getCacheContexts();
|
||||
|
||||
/**
|
||||
* The cache tags associated with this object.
|
||||
*
|
||||
* When this object is modified, these cache tags will be invalidated.
|
||||
*
|
||||
* @return string[]
|
||||
* A set of cache tags.
|
||||
*/
|
||||
public function getCacheTags();
|
||||
|
||||
/**
|
||||
* The maximum age for which this object may be cached.
|
||||
*
|
||||
* @return int
|
||||
* The maximum time in seconds that this object may be cached.
|
||||
*/
|
||||
public function getCacheMaxAge();
|
||||
|
||||
}
|
233
core/lib/Drupal/Core/Cache/CacheableMetadata.php
Normal file
233
core/lib/Drupal/Core/Cache/CacheableMetadata.php
Normal file
|
@ -0,0 +1,233 @@
|
|||
<?php
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheableMetadata.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines a generic class for passing cacheability metadata.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class CacheableMetadata implements CacheableDependencyInterface {
|
||||
|
||||
/**
|
||||
* Cache contexts.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $contexts = [];
|
||||
|
||||
/**
|
||||
* Cache tags.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $tags = [];
|
||||
|
||||
/**
|
||||
* Cache max-age.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $maxAge = Cache::PERMANENT;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds cache tags.
|
||||
*
|
||||
* @param string[] $cache_tags
|
||||
* The cache tags to be added.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCacheTags(array $cache_tags) {
|
||||
$this->tags = Cache::mergeTags($this->tags, $cache_tags);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cache tags.
|
||||
*
|
||||
* @param string[] $cache_tags
|
||||
* The cache tags to be associated.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCacheTags(array $cache_tags) {
|
||||
$this->tags = $cache_tags;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return $this->contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds cache contexts.
|
||||
*
|
||||
* @param string[] $cache_contexts
|
||||
* The cache contexts to be added.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addCacheContexts(array $cache_contexts) {
|
||||
$this->contexts = Cache::mergeContexts($this->contexts, $cache_contexts);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cache contexts.
|
||||
*
|
||||
* @param string[] $cache_contexts
|
||||
* The cache contexts to be associated.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCacheContexts(array $cache_contexts) {
|
||||
$this->contexts = $cache_contexts;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return $this->maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum age (in seconds).
|
||||
*
|
||||
* Defaults to Cache::PERMANENT
|
||||
*
|
||||
* @param int $max_age
|
||||
* The max age to associate.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* If a non-integer value is supplied.
|
||||
*/
|
||||
public function setCacheMaxAge($max_age) {
|
||||
if (!is_int($max_age)) {
|
||||
throw new \InvalidArgumentException('$max_age must be an integer');
|
||||
}
|
||||
|
||||
$this->maxAge = $max_age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the values of another CacheableMetadata object with this one.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableMetadata $other
|
||||
* The other CacheableMetadata object.
|
||||
*
|
||||
* @return static
|
||||
* A new CacheableMetadata object, with the merged data.
|
||||
*/
|
||||
public function merge(CacheableMetadata $other) {
|
||||
$result = new static();
|
||||
|
||||
// This is called many times per request, so avoid merging unless absolutely
|
||||
// necessary.
|
||||
if (empty($this->contexts)) {
|
||||
$result->contexts = $other->contexts;
|
||||
}
|
||||
elseif (empty($other->contexts)) {
|
||||
$result->contexts = $this->contexts;
|
||||
}
|
||||
else {
|
||||
$result->contexts = Cache::mergeContexts($this->contexts, $other->contexts);
|
||||
}
|
||||
|
||||
if (empty($this->tags)) {
|
||||
$result->tags = $other->tags;
|
||||
}
|
||||
elseif (empty($other->tags)) {
|
||||
$result->tags = $this->tags;
|
||||
}
|
||||
else {
|
||||
$result->tags = Cache::mergeTags($this->tags, $other->tags);
|
||||
}
|
||||
|
||||
if ($this->maxAge === Cache::PERMANENT) {
|
||||
$result->maxAge = $other->maxAge;
|
||||
}
|
||||
elseif ($other->maxAge === Cache::PERMANENT) {
|
||||
$result->maxAge = $this->maxAge;
|
||||
}
|
||||
else {
|
||||
$result->maxAge = Cache::mergeMaxAges($this->maxAge, $other->maxAge);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the values of this CacheableMetadata object to a render array.
|
||||
*
|
||||
* @param array &$build
|
||||
* A render array.
|
||||
*/
|
||||
public function applyTo(array &$build) {
|
||||
$build['#cache']['contexts'] = $this->contexts;
|
||||
$build['#cache']['tags'] = $this->tags;
|
||||
$build['#cache']['max-age'] = $this->maxAge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CacheableMetadata object with values taken from a render array.
|
||||
*
|
||||
* @param array $build
|
||||
* A render array.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromRenderArray(array $build) {
|
||||
$meta = new static();
|
||||
$meta->contexts = (isset($build['#cache']['contexts'])) ? $build['#cache']['contexts'] : [];
|
||||
$meta->tags = (isset($build['#cache']['tags'])) ? $build['#cache']['tags'] : [];
|
||||
$meta->maxAge = (isset($build['#cache']['max-age'])) ? $build['#cache']['max-age'] : Cache::PERMANENT;
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a CacheableMetadata object from a depended object.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $object
|
||||
* The object whose cacheability metadata to retrieve. If it implements
|
||||
* CacheableDependencyInterface, its cacheability metadata will be used,
|
||||
* otherwise, the passed in object must be assumed to be uncacheable, so
|
||||
* max-age 0 is set.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function createFromObject($object) {
|
||||
if ($object instanceof CacheableDependencyInterface) {
|
||||
$meta = new static();
|
||||
$meta->contexts = $object->getCacheContexts();
|
||||
$meta->tags = $object->getCacheTags();
|
||||
$meta->maxAge = $object->getCacheMaxAge();
|
||||
return $meta;
|
||||
}
|
||||
|
||||
// Objects that don't implement CacheableDependencyInterface must be assumed
|
||||
// to be uncacheable, so set max-age 0.
|
||||
$meta = new static();
|
||||
$meta->maxAge = 0;
|
||||
return $meta;
|
||||
}
|
||||
|
||||
}
|
26
core/lib/Drupal/Core/Cache/CacheableResponse.php
Normal file
26
core/lib/Drupal/Core/Cache/CacheableResponse.php
Normal file
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheableResponse.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* A response that contains and can expose cacheability metadata.
|
||||
*
|
||||
* Supports Drupal's caching concepts: cache tags for invalidation and cache
|
||||
* contexts for variations.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\Cache
|
||||
* @see \Drupal\Core\Cache\CacheableMetadata
|
||||
* @see \Drupal\Core\Cache\CacheableResponseTrait
|
||||
*/
|
||||
class CacheableResponse extends Response implements CacheableResponseInterface {
|
||||
|
||||
use CacheableResponseTrait;
|
||||
|
||||
}
|
42
core/lib/Drupal/Core/Cache/CacheableResponseInterface.php
Normal file
42
core/lib/Drupal/Core/Cache/CacheableResponseInterface.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheableResponseInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines an interface for responses that can expose cacheability metadata.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheableResponseTrait
|
||||
*/
|
||||
interface CacheableResponseInterface {
|
||||
|
||||
/**
|
||||
* Adds a dependency on an object: merges its cacheability metadata.
|
||||
*
|
||||
* E.g. when a response depends on some configuration, an entity, or an access
|
||||
* result, we must make sure their cacheability metadata is present on the
|
||||
* response. This method makes doing that simple.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheableDependencyInterface|mixed $dependency
|
||||
* The dependency. If the object implements CacheableDependencyInterface,
|
||||
* then its cacheability metadata will be used. Otherwise, the passed in
|
||||
* object must be assumed to be uncacheable, so max-age 0 is set.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheableMetadata::createFromObject()
|
||||
*/
|
||||
public function addCacheableDependency($dependency);
|
||||
|
||||
/**
|
||||
* Returns the cacheability metadata for this response.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheableMetadata
|
||||
*/
|
||||
public function getCacheableMetadata();
|
||||
|
||||
}
|
52
core/lib/Drupal/Core/Cache/CacheableResponseTrait.php
Normal file
52
core/lib/Drupal/Core/Cache/CacheableResponseTrait.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\CacheableResponseTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Provides an implementation of CacheableResponseInterface.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheableResponseInterface
|
||||
*/
|
||||
trait CacheableResponseTrait {
|
||||
|
||||
/**
|
||||
* The cacheability metadata.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheableMetadata
|
||||
*/
|
||||
protected $cacheabilityMetadata;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addCacheableDependency($dependency) {
|
||||
// A trait doesn't have a constructor, so initialize the cacheability
|
||||
// metadata if that hasn't happened yet.
|
||||
if (!isset($this->cacheabilityMetadata)) {
|
||||
$this->cacheabilityMetadata = new CacheableMetadata();
|
||||
}
|
||||
|
||||
$this->cacheabilityMetadata = $this->cacheabilityMetadata->merge(CacheableMetadata::createFromObject($dependency));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheableMetadata() {
|
||||
// A trait doesn't have a constructor, so initialize the cacheability
|
||||
// metadata if that hasn't happened yet.
|
||||
if (!isset($this->cacheabilityMetadata)) {
|
||||
$this->cacheabilityMetadata = new CacheableMetadata();
|
||||
}
|
||||
|
||||
return $this->cacheabilityMetadata;
|
||||
}
|
||||
|
||||
}
|
306
core/lib/Drupal/Core/Cache/ChainedFastBackend.php
Normal file
306
core/lib/Drupal/Core/Cache/ChainedFastBackend.php
Normal file
|
@ -0,0 +1,306 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\ChainedFastBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines a backend with a fast and a consistent backend chain.
|
||||
*
|
||||
* In order to mitigate a network roundtrip for each cache get operation, this
|
||||
* cache allows a fast backend to be put in front of a slow(er) backend.
|
||||
* Typically the fast backend will be something like APCu, and be bound to a
|
||||
* single web node, and will not require a network round trip to fetch a cache
|
||||
* item. The fast backend will also typically be inconsistent (will only see
|
||||
* changes from one web node). The slower backend will be something like Mysql,
|
||||
* Memcached or Redis, and will be used by all web nodes, thus making it
|
||||
* consistent, but also require a network round trip for each cache get.
|
||||
*
|
||||
* In addition to being useful for sites running on multiple web nodes, this
|
||||
* backend can also be useful for sites running on a single web node where the
|
||||
* fast backend (e.g., APCu) isn't shareable between the web and CLI processes.
|
||||
* Single-node configurations that don't have that limitation can just use the
|
||||
* fast cache backend directly.
|
||||
*
|
||||
* We always use the fast backend when reading (get()) entries from cache, but
|
||||
* check whether they were created before the last write (set()) to this
|
||||
* (chained) cache backend. Those cache entries that were created before the
|
||||
* last write are discarded, but we use their cache IDs to then read them from
|
||||
* the consistent (slower) cache backend instead; at the same time we update
|
||||
* the fast cache backend so that the next read will hit the faster backend
|
||||
* again. Hence we can guarantee that the cache entries we return are all
|
||||
* up-to-date, and maximally exploit the faster cache backend. This cache
|
||||
* backend uses and maintains a "last write timestamp" to determine which cache
|
||||
* entries should be discarded.
|
||||
*
|
||||
* Because this backend will mark all the cache entries in a bin as out-dated
|
||||
* for each write to a bin, it is best suited to bins with fewer changes.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class ChainedFastBackend implements CacheBackendInterface, CacheTagsInvalidatorInterface {
|
||||
|
||||
/**
|
||||
* Cache key prefix for the bin-specific entry to track the last write.
|
||||
*/
|
||||
const LAST_WRITE_TIMESTAMP_PREFIX = 'last_write_timestamp_';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bin;
|
||||
|
||||
/**
|
||||
* The consistent cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $consistentBackend;
|
||||
|
||||
/**
|
||||
* The fast cache backend.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheBackendInterface
|
||||
*/
|
||||
protected $fastBackend;
|
||||
|
||||
/**
|
||||
* The time at which the last write to this cache bin happened.
|
||||
*
|
||||
* @var float
|
||||
*/
|
||||
protected $lastWriteTimestamp;
|
||||
|
||||
/**
|
||||
* Constructs a ChainedFastBackend object.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $consistent_backend
|
||||
* The consistent cache backend.
|
||||
* @param \Drupal\Core\Cache\CacheBackendInterface $fast_backend
|
||||
* The fast cache backend.
|
||||
* @param string $bin
|
||||
* The cache bin for which the object is created.
|
||||
*/
|
||||
public function __construct(CacheBackendInterface $consistent_backend, CacheBackendInterface $fast_backend, $bin) {
|
||||
$this->consistentBackend = $consistent_backend;
|
||||
$this->fastBackend = $fast_backend;
|
||||
$this->bin = 'cache_' . $bin;
|
||||
$this->lastWriteTimestamp = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
$cids = array($cid);
|
||||
$cache = $this->getMultiple($cids, $allow_invalid);
|
||||
return reset($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$cids_copy = $cids;
|
||||
$cache = array();
|
||||
|
||||
// If we can determine the time at which the last write to the consistent
|
||||
// backend occurred (we might not be able to if it has been recently
|
||||
// flushed/restarted), then we can use that to validate items from the fast
|
||||
// backend, so try to get those first. Otherwise, we can't assume that
|
||||
// anything in the fast backend is valid, so don't even bother fetching
|
||||
// from there.
|
||||
$last_write_timestamp = $this->getLastWriteTimestamp();
|
||||
if ($last_write_timestamp) {
|
||||
// Items in the fast backend might be invalid based on their timestamp,
|
||||
// but we can't check the timestamp prior to getting the item, which
|
||||
// includes unserializing it. However, unserializing an invalid item can
|
||||
// throw an exception. For example, a __wakeup() implementation that
|
||||
// receives object properties containing references to code or data that
|
||||
// no longer exists in the application's current state.
|
||||
//
|
||||
// Unserializing invalid data, whether it throws an exception or not, is
|
||||
// a waste of time, but we only incur it while a cache invalidation has
|
||||
// not yet finished propagating to all the fast backend instances.
|
||||
//
|
||||
// Most cache backend implementations should not wrap their internal
|
||||
// get() implementations with a try/catch, because they have no reason to
|
||||
// assume that their data is invalid, and doing so would mask
|
||||
// unserialization errors of valid data. We do so here, only because the
|
||||
// fast backend is non-authoritative, and after discarding its
|
||||
// exceptions, we proceed to check the consistent (authoritative) backend
|
||||
// and allow exceptions from that to bubble up.
|
||||
try {
|
||||
$items = $this->fastBackend->getMultiple($cids, $allow_invalid);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$cids = $cids_copy;
|
||||
$items = array();
|
||||
}
|
||||
|
||||
// Even if items were successfully fetched from the fast backend, they
|
||||
// are potentially invalid if older than the last time the bin was
|
||||
// written to in the consistent backend, so only keep ones that aren't.
|
||||
foreach ($items as $item) {
|
||||
if ($item->created < $last_write_timestamp) {
|
||||
$cids[array_search($item->cid, $cids_copy)] = $item->cid;
|
||||
}
|
||||
else {
|
||||
$cache[$item->cid] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there were any cache entries that were not available in the fast
|
||||
// backend, retrieve them from the consistent backend and store them in the
|
||||
// fast one.
|
||||
if ($cids) {
|
||||
foreach ($this->consistentBackend->getMultiple($cids, $allow_invalid) as $item) {
|
||||
$cache[$item->cid] = $item;
|
||||
// Don't write the cache tags to the fast backend as any cache tag
|
||||
// invalidation results in an invalidation of the whole fast backend.
|
||||
$this->fastBackend->set($item->cid, $item->data, $item->expire);
|
||||
}
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
$this->consistentBackend->set($cid, $data, $expire, $tags);
|
||||
$this->markAsOutdated();
|
||||
// Don't write the cache tags to the fast backend as any cache tag
|
||||
// invalidation results in an invalidation of the whole fast backend.
|
||||
$this->fastBackend->set($cid, $data, $expire);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items) {
|
||||
$this->consistentBackend->setMultiple($items);
|
||||
$this->markAsOutdated();
|
||||
// Don't write the cache tags to the fast backend as any cache tag
|
||||
// invalidation results in an invalidation of the whole fast backend.
|
||||
foreach ($items as &$item) {
|
||||
unset($item['tags']);
|
||||
}
|
||||
$this->fastBackend->setMultiple($items);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($cid) {
|
||||
$this->consistentBackend->deleteMultiple(array($cid));
|
||||
$this->markAsOutdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
$this->consistentBackend->deleteMultiple($cids);
|
||||
$this->markAsOutdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll() {
|
||||
$this->consistentBackend->deleteAll();
|
||||
$this->markAsOutdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
$this->invalidateMultiple(array($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
$this->consistentBackend->invalidateMultiple($cids);
|
||||
$this->markAsOutdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
if ($this->consistentBackend instanceof CacheTagsInvalidatorInterface) {
|
||||
$this->consistentBackend->invalidateTags($tags);
|
||||
}
|
||||
$this->markAsOutdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
$this->consistentBackend->invalidateAll();
|
||||
$this->markAsOutdated();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
$this->consistentBackend->garbageCollection();
|
||||
$this->fastBackend->garbageCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
$this->consistentBackend->removeBin();
|
||||
$this->fastBackend->removeBin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo Document in https://www.drupal.org/node/2311945.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->lastWriteTimestamp = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last write timestamp.
|
||||
*/
|
||||
protected function getLastWriteTimestamp() {
|
||||
if ($this->lastWriteTimestamp === NULL) {
|
||||
$cache = $this->consistentBackend->get(self::LAST_WRITE_TIMESTAMP_PREFIX . $this->bin);
|
||||
$this->lastWriteTimestamp = $cache ? $cache->data : 0;
|
||||
}
|
||||
return $this->lastWriteTimestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks the fast cache bin as outdated because of a write.
|
||||
*/
|
||||
protected function markAsOutdated() {
|
||||
// Clocks on a single server can drift. Multiple servers may have slightly
|
||||
// differing opinions about the current time. Given that, do not assume
|
||||
// 'now' on this server is always later than our stored timestamp.
|
||||
// Also add 1 millisecond, to ensure that caches written earlier in the same
|
||||
// millisecond are invalidated. It is possible that caches will be later in
|
||||
// the same millisecond and are then incorrectly invalidated, but that only
|
||||
// costs one additional roundtrip to the persistent cache.
|
||||
$now = round(microtime(TRUE) + .001, 3);
|
||||
if ($now > $this->getLastWriteTimestamp()) {
|
||||
$this->lastWriteTimestamp = $now;
|
||||
$this->consistentBackend->set(self::LAST_WRITE_TIMESTAMP_PREFIX . $this->bin, $this->lastWriteTimestamp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
94
core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
Normal file
94
core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php
Normal file
|
@ -0,0 +1,94 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\ChainedFastBackendFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* Defines the chained fast cache backend factory.
|
||||
*/
|
||||
class ChainedFastBackendFactory implements CacheFactoryInterface {
|
||||
|
||||
use ContainerAwareTrait;
|
||||
|
||||
/**
|
||||
* The service name of the consistent backend factory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $consistentServiceName;
|
||||
|
||||
/**
|
||||
* The service name of the fast backend factory.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fastServiceName;
|
||||
|
||||
/**
|
||||
* Constructs ChainedFastBackendFactory object.
|
||||
*
|
||||
* @param \Drupal\Core\Site\Settings|NULL $settings
|
||||
* (optional) The settings object.
|
||||
* @param string|NULL $consistent_service_name
|
||||
* (optional) The service name of the consistent backend factory. Defaults
|
||||
* to:
|
||||
* - $settings->get('cache')['default'] (if specified)
|
||||
* - 'cache.backend.database' (if the above isn't specified)
|
||||
* @param string|NULL $fast_service_name
|
||||
* (optional) The service name of the fast backend factory. Defaults to:
|
||||
* - 'cache.backend.apcu' (if the PHP process has APCu enabled)
|
||||
* - NULL (if the PHP process doesn't have APCu enabled)
|
||||
*/
|
||||
public function __construct(Settings $settings = NULL, $consistent_service_name = NULL, $fast_service_name = NULL) {
|
||||
// Default the consistent backend to the site's default backend.
|
||||
if (!isset($consistent_service_name)) {
|
||||
$cache_settings = isset($settings) ? $settings->get('cache') : array();
|
||||
$consistent_service_name = isset($cache_settings['default']) ? $cache_settings['default'] : 'cache.backend.database';
|
||||
}
|
||||
|
||||
// Default the fast backend to APCu if it's available.
|
||||
if (!isset($fast_service_name) && function_exists('apc_fetch')) {
|
||||
$fast_service_name = 'cache.backend.apcu';
|
||||
}
|
||||
|
||||
$this->consistentServiceName = $consistent_service_name;
|
||||
|
||||
// Do not use the fast chained backend during installation. In those cases,
|
||||
// we expect many cache invalidations and writes, the fast chained cache
|
||||
// backend performs badly in such a scenario.
|
||||
if (!drupal_installation_attempted()) {
|
||||
$this->fastServiceName = $fast_service_name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a chained, fast cache backend class for a given cache bin.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which a cache backend object should be returned.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\CacheBackendInterface
|
||||
* The cache backend object associated with the specified bin.
|
||||
*/
|
||||
public function get($bin) {
|
||||
// Use the chained backend only if there is a fast backend available;
|
||||
// otherwise, just return the consistent backend directly.
|
||||
if (isset($this->fastServiceName)) {
|
||||
return new ChainedFastBackend(
|
||||
$this->container->get($this->consistentServiceName)->get($bin),
|
||||
$this->container->get($this->fastServiceName)->get($bin),
|
||||
$bin
|
||||
);
|
||||
}
|
||||
else {
|
||||
return $this->container->get($this->consistentServiceName)->get($bin);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\AccountPermissionsCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\Session\PermissionsHashGeneratorInterface;
|
||||
|
||||
/**
|
||||
* Defines the AccountPermissionsCacheContext service, for "per permission" caching.
|
||||
*/
|
||||
class AccountPermissionsCacheContext extends UserCacheContext {
|
||||
|
||||
/**
|
||||
* The permissions hash generator.
|
||||
*
|
||||
* @var \Drupal\user\PermissionsHashInterface
|
||||
*/
|
||||
protected $permissionsHashGenerator;
|
||||
|
||||
/**
|
||||
* Constructs a new UserCacheContext service.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* The current user.
|
||||
* @param \Drupal\user\PermissionsHashInterface $permissions_hash_generator
|
||||
* The permissions hash generator.
|
||||
*/
|
||||
public function __construct(AccountInterface $user, PermissionsHashGeneratorInterface $permissions_hash_generator) {
|
||||
$this->user = $user;
|
||||
$this->permissionsHashGenerator = $permissions_hash_generator;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t("Account's permissions");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return 'ph.' . $this->permissionsHashGenerator->generate($this->user);
|
||||
}
|
||||
|
||||
}
|
34
core/lib/Drupal/Core/Cache/Context/CacheContextInterface.php
Normal file
34
core/lib/Drupal/Core/Cache/Context/CacheContextInterface.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\CacheContextInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Provides an interface for defining a cache context service.
|
||||
*/
|
||||
interface CacheContextInterface {
|
||||
|
||||
/**
|
||||
* Returns the label of the cache context.
|
||||
*
|
||||
* @return string
|
||||
* The label of the cache context.
|
||||
*/
|
||||
public static function getLabel();
|
||||
|
||||
/**
|
||||
* Returns the string representation of the cache context.
|
||||
*
|
||||
* A cache context service's name is used as a token (placeholder) cache key,
|
||||
* and is then replaced with the string returned by this method.
|
||||
*
|
||||
* @return string
|
||||
* The string representation of the cache context.
|
||||
*/
|
||||
public function getContext();
|
||||
|
||||
}
|
274
core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php
Normal file
274
core/lib/Drupal/Core/Cache/Context/CacheContextsManager.php
Normal file
|
@ -0,0 +1,274 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\CacheContextsManager.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
/**
|
||||
* Converts cache context tokens into cache keys.
|
||||
*
|
||||
* Uses cache context services (services tagged with 'cache.context', and whose
|
||||
* service ID has the 'cache_context.' prefix) to dynamically generate cache
|
||||
* keys based on the request context, thus allowing developers to express the
|
||||
* state by which should varied (the current URL, language, and so on).
|
||||
*
|
||||
* Note that this maps exactly to HTTP's Vary header semantics:
|
||||
* @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
|
||||
*
|
||||
* @see \Drupal\Core\Cache\Context\CacheContextInterface
|
||||
* @see \Drupal\Core\Cache\Context\CalculatedCacheContextInterface
|
||||
* @see \Drupal\Core\Cache\Context\CacheContextsPass
|
||||
*/
|
||||
class CacheContextsManager {
|
||||
|
||||
/**
|
||||
* The service container.
|
||||
*
|
||||
* @var \Symfony\Component\DependencyInjection\ContainerInterface
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Available cache context IDs and corresponding labels.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $contexts;
|
||||
|
||||
/**
|
||||
* Constructs a CacheContextsManager object.
|
||||
*
|
||||
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
|
||||
* The current service container.
|
||||
* @param string[] $contexts
|
||||
* An array of the available cache context IDs.
|
||||
*/
|
||||
public function __construct(ContainerInterface $container, array $contexts) {
|
||||
$this->container = $container;
|
||||
$this->contexts = $contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an array of available cache contexts.
|
||||
*
|
||||
* @return string[]
|
||||
* An array of available cache context IDs.
|
||||
*/
|
||||
public function getAll() {
|
||||
return $this->contexts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an array of available cache context labels.
|
||||
*
|
||||
* To be used in cache configuration forms.
|
||||
*
|
||||
* @param bool $include_calculated_cache_contexts
|
||||
* Whether to also return calculated cache contexts. Default to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* An array of available cache contexts and corresponding labels.
|
||||
*/
|
||||
public function getLabels($include_calculated_cache_contexts = FALSE) {
|
||||
$with_labels = array();
|
||||
foreach ($this->contexts as $context) {
|
||||
$service = $this->getService($context);
|
||||
if (!$include_calculated_cache_contexts && $service instanceof CalculatedCacheContextInterface) {
|
||||
continue;
|
||||
}
|
||||
$with_labels[$context] = $service->getLabel();
|
||||
}
|
||||
return $with_labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts cache context tokens to cache keys.
|
||||
*
|
||||
* A cache context token is either:
|
||||
* - a cache context ID (if the service ID is 'cache_context.foo', then 'foo'
|
||||
* is a cache context ID), e.g. 'foo'
|
||||
* - a calculated cache context ID, followed by a double colon, followed by
|
||||
* the parameter for the calculated cache context, e.g. 'bar:some_parameter'
|
||||
*
|
||||
* @param string[] $context_tokens
|
||||
* An array of cache context tokens.
|
||||
*
|
||||
* @return string[]
|
||||
* The array of corresponding cache keys.
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function convertTokensToKeys(array $context_tokens) {
|
||||
$context_tokens = $this->optimizeTokens($context_tokens);
|
||||
sort($context_tokens);
|
||||
$keys = [];
|
||||
foreach (static::parseTokens($context_tokens) as $context) {
|
||||
list($context_id, $parameter) = $context;
|
||||
if (!in_array($context_id, $this->contexts)) {
|
||||
throw new \InvalidArgumentException(SafeMarkup::format('"@context" is not a valid cache context ID.', ['@context' => $context_id]));
|
||||
}
|
||||
$keys[] = $this->getService($context_id)->getContext($parameter);
|
||||
}
|
||||
return $keys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes cache context tokens (the minimal representative subset).
|
||||
*
|
||||
* A minimal representative subset means that any cache context token in the
|
||||
* given set of cache context tokens that is a property of another cache
|
||||
* context cache context token in the set, is removed.
|
||||
*
|
||||
* Hence a minimal representative subset is the most compact representation
|
||||
* possible of a set of cache context tokens, that still captures the entire
|
||||
* universe of variations.
|
||||
*
|
||||
* E.g. when caching per user ('user'), also caching per role ('user.roles')
|
||||
* is meaningless because "per role" is implied by "per user".
|
||||
*
|
||||
* Examples — remember that the period indicates hierarchy and the colon can
|
||||
* be used to get a specific value of a calculated cache context:
|
||||
* - ['a', 'a.b'] -> ['a']
|
||||
* - ['a', 'a.b.c'] -> ['a']
|
||||
* - ['a.b', 'a.b.c'] -> ['a.b']
|
||||
* - ['a', 'a.b', 'a.b.c'] -> ['a']
|
||||
* - ['x', 'x:foo'] -> ['x']
|
||||
* - ['a', 'a.b.c:bar'] -> ['a']
|
||||
*
|
||||
* @param string[] $context_tokens
|
||||
* A set of cache context tokens.
|
||||
*
|
||||
* @return string[]
|
||||
* A representative subset of the given set of cache context tokens..
|
||||
*/
|
||||
public function optimizeTokens(array $context_tokens) {
|
||||
$optimized_content_tokens = [];
|
||||
foreach ($context_tokens as $context_token) {
|
||||
// Context tokens without:
|
||||
// - a period means they don't have a parent
|
||||
// - a colon means they're not a specific value of a cache context
|
||||
// hence no optimizations are possible.
|
||||
if (strpos($context_token, '.') === FALSE && strpos($context_token, ':') === FALSE) {
|
||||
$optimized_content_tokens[] = $context_token;
|
||||
}
|
||||
// The context token has a period or a colon. Iterate over all ancestor
|
||||
// cache contexts. If one exists, omit the context token.
|
||||
else {
|
||||
$ancestor_found = FALSE;
|
||||
// Treat a colon like a period, that allows us to consider 'a' the
|
||||
// ancestor of 'a:foo', without any additional code for the colon.
|
||||
$ancestor = str_replace(':', '.', $context_token);
|
||||
do {
|
||||
$ancestor = substr($ancestor, 0, strrpos($ancestor, '.'));
|
||||
if (in_array($ancestor, $context_tokens)) {
|
||||
// An ancestor cache context is in $context_tokens, hence this cache
|
||||
// context is implied.
|
||||
$ancestor_found = TRUE;
|
||||
}
|
||||
|
||||
} while(!$ancestor_found && strpos($ancestor, '.') !== FALSE);
|
||||
if (!$ancestor_found) {
|
||||
$optimized_content_tokens[] = $context_token;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $optimized_content_tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a cache context service from the container.
|
||||
*
|
||||
* @param string $context_id
|
||||
* The context ID, which together with the service ID prefix allows the
|
||||
* corresponding cache context service to be retrieved.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\Context\CacheContextInterface
|
||||
* The requested cache context service.
|
||||
*/
|
||||
protected function getService($context_id) {
|
||||
return $this->container->get('cache_context.' . $context_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses cache context tokens into context IDs and optional parameters.
|
||||
*
|
||||
* @param string[] $context_tokens
|
||||
* An array of cache context tokens.
|
||||
*
|
||||
* @return array
|
||||
* An array with the parsed results, with each result being an array
|
||||
* containing:
|
||||
* - The cache context ID.
|
||||
* - The associated parameter (for a calculated cache context), or NULL if
|
||||
* there is no parameter.
|
||||
*/
|
||||
public static function parseTokens(array $context_tokens) {
|
||||
$contexts_with_parameters = [];
|
||||
foreach ($context_tokens as $context) {
|
||||
$context_id = $context;
|
||||
$parameter = NULL;
|
||||
if (strpos($context, ':') !== FALSE) {
|
||||
list($context_id, $parameter) = explode(':', $context, 2);
|
||||
}
|
||||
$contexts_with_parameters[] = [$context_id, $parameter];
|
||||
}
|
||||
return $contexts_with_parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an array of cache context tokens.
|
||||
*
|
||||
* Can be called before using cache contexts in operations, to check validity.
|
||||
*
|
||||
* @param string[] $context_tokens
|
||||
* An array of cache context tokens.
|
||||
*
|
||||
* @throws \LogicException
|
||||
*
|
||||
* @see \Drupal\Core\Cache\Context\CacheContextsManager::parseTokens()
|
||||
*/
|
||||
public function validateTokens(array $context_tokens = []) {
|
||||
if (empty($context_tokens)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize the set of valid context tokens with the container's contexts.
|
||||
if (!isset($this->validContextTokens)) {
|
||||
$this->validContextTokens = array_flip($this->contexts);
|
||||
}
|
||||
|
||||
foreach ($context_tokens as $context_token) {
|
||||
if (!is_string($context_token)) {
|
||||
throw new \LogicException(sprintf('Cache contexts must be strings, %s given.', gettype($context_token)));
|
||||
}
|
||||
|
||||
if (isset($this->validContextTokens[$context_token])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it's a valid context token, then the ID must be stored in the set
|
||||
// of valid context tokens (since we initialized it with the list of cache
|
||||
// context IDs using the container). In case of an invalid context token,
|
||||
// throw an exception, otherwise cache it, including the parameter, to
|
||||
// minimize the amount of work in future ::validateContexts() calls.
|
||||
$context_id = $context_token;
|
||||
$colon_pos = strpos($context_id, ':');
|
||||
if ($colon_pos !== FALSE) {
|
||||
$context_id = substr($context_id, 0, $colon_pos);
|
||||
}
|
||||
if (isset($this->validContextTokens[$context_id])) {
|
||||
$this->validContextTokens[$context_token] = TRUE;
|
||||
}
|
||||
else {
|
||||
throw new \LogicException(sprintf('"%s" is not a valid cache context ID.', $context_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
48
core/lib/Drupal/Core/Cache/Context/CacheContextsPass.php
Normal file
48
core/lib/Drupal/Core/Cache/Context/CacheContextsPass.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\CacheContextsPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Adds cache_contexts parameter to the container.
|
||||
*/
|
||||
class CacheContextsPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* Implements CompilerPassInterface::process().
|
||||
*
|
||||
* Collects the cache contexts into the cache_contexts parameter.
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$cache_contexts = [];
|
||||
foreach (array_keys($container->findTaggedServiceIds('cache.context')) as $id) {
|
||||
if (strpos($id, 'cache_context.') !== 0) {
|
||||
throw new \InvalidArgumentException(sprintf('The service "%s" has an invalid service ID: cache context service IDs must use the "cache_context." prefix. (The suffix is the cache context ID developers may use.)', $id));
|
||||
}
|
||||
$cache_contexts[] = substr($id, 14);
|
||||
}
|
||||
|
||||
// Validate.
|
||||
sort($cache_contexts);
|
||||
foreach ($cache_contexts as $id) {
|
||||
// Validate the hierarchy of non-root-level cache contexts.
|
||||
if (strpos($id, '.') !== FALSE) {
|
||||
$parent = substr($id, 0, strrpos($id, '.'));
|
||||
if (!in_array($parent, $cache_contexts)) {
|
||||
throw new \InvalidArgumentException(sprintf('The service "%s" has an invalid service ID: the period indicates the hierarchy of cache contexts, therefore "%s" is considered the parent cache context, but no cache context service with that name was found.', $id, $parent));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$container->setParameter('cache_contexts', $cache_contexts);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\CalculatedCacheContextInterface.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Provides an interface for defining a calculated cache context service.
|
||||
*/
|
||||
interface CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* Returns the label of the cache context.
|
||||
*
|
||||
* @return string
|
||||
* The label of the cache context.
|
||||
*
|
||||
* @see Cache
|
||||
*/
|
||||
public static function getLabel();
|
||||
|
||||
/**
|
||||
* Returns the string representation of the cache context.
|
||||
*
|
||||
* A cache context service's name is used as a token (placeholder) cache key,
|
||||
* and is then replaced with the string returned by this method.
|
||||
*
|
||||
* @param string|null $parameter
|
||||
* The parameter, or NULL to indicate all possible parameter values.
|
||||
*
|
||||
* @return string
|
||||
* The string representation of the cache context. When $parameter is NULL,
|
||||
* a value representing all possible parameters must be generated.
|
||||
*/
|
||||
public function getContext($parameter = NULL);
|
||||
|
||||
}
|
34
core/lib/Drupal/Core/Cache/Context/CookiesCacheContext.php
Normal file
34
core/lib/Drupal/Core/Cache/Context/CookiesCacheContext.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\CookiesCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the CookiesCacheContext service, for "per cookie" caching.
|
||||
*/
|
||||
class CookiesCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('HTTP cookies');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($cookie = NULL) {
|
||||
if ($cookie === NULL) {
|
||||
return $this->requestStack->getCurrentRequest()->cookies->all();
|
||||
}
|
||||
else {
|
||||
return $this->requestStack->getCurrentRequest()->cookies->get($cookie);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
34
core/lib/Drupal/Core/Cache/Context/HeadersCacheContext.php
Normal file
34
core/lib/Drupal/Core/Cache/Context/HeadersCacheContext.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\HeadersCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the HeadersCacheContext service, for "per header" caching.
|
||||
*/
|
||||
class HeadersCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('HTTP headers');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($header = NULL) {
|
||||
if ($header === NULL) {
|
||||
return $this->requestStack->getCurrentRequest()->headers->all();
|
||||
}
|
||||
else {
|
||||
return $this->requestStack->getCurrentRequest()->headers->get($header);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
29
core/lib/Drupal/Core/Cache/Context/IpCacheContext.php
Normal file
29
core/lib/Drupal/Core/Cache/Context/IpCacheContext.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\IpCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the IpCacheContext service, for "per IP address" caching.
|
||||
*/
|
||||
class IpCacheContext extends RequestStackCacheContextBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('IP address');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->requestStack->getCurrentRequest()->getClientIp();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\IsSuperUserCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the IsSuperUserCacheContext service, for "super user or not" caching.
|
||||
*/
|
||||
class IsSuperUserCacheContext extends UserCacheContext {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Is super user');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return ((int) $this->user->id()) === 1 ? '1' : '0';
|
||||
}
|
||||
|
||||
}
|
77
core/lib/Drupal/Core/Cache/Context/LanguagesCacheContext.php
Normal file
77
core/lib/Drupal/Core/Cache/Context/LanguagesCacheContext.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\LanguagesCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines the LanguagesCacheContext service, for "per language" caching.
|
||||
*/
|
||||
class LanguagesCacheContext implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* The language manager.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
*/
|
||||
protected $languageManager;
|
||||
|
||||
/**
|
||||
* Constructs a new LanguagesCacheContext service.
|
||||
*
|
||||
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
||||
* The language manager.
|
||||
*/
|
||||
public function __construct(LanguageManagerInterface $language_manager) {
|
||||
$this->languageManager = $language_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Language');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* $type can be NULL, or one of the language types supported by the language
|
||||
* manager, typically:
|
||||
* - LanguageInterface::TYPE_INTERFACE
|
||||
* - LanguageInterface::TYPE_CONTENT
|
||||
* - LanguageInterface::TYPE_URL
|
||||
*
|
||||
* @see \Drupal\Core\Language\LanguageManagerInterface::getLanguageTypes()
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* In case an invalid language type is specified.
|
||||
*/
|
||||
public function getContext($type = NULL) {
|
||||
if ($type === NULL) {
|
||||
$context_parts = array();
|
||||
if ($this->languageManager->isMultilingual()) {
|
||||
foreach ($this->languageManager->getLanguageTypes() as $type) {
|
||||
$context_parts[] = $this->languageManager->getCurrentLanguage($type)->getId();
|
||||
}
|
||||
}
|
||||
else {
|
||||
$context_parts[] = $this->languageManager->getCurrentLanguage()->getId();
|
||||
}
|
||||
return implode(',', $context_parts);
|
||||
}
|
||||
else {
|
||||
$language_types = $this->languageManager->getDefinedLanguageTypesInfo();
|
||||
if (!isset($language_types[$type])) {
|
||||
throw new \RuntimeException(sprintf('The language type "%s" is invalid.', $type));
|
||||
}
|
||||
return $this->languageManager->getCurrentLanguage($type)->getId();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\MenuActiveTrailsCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerAware;
|
||||
|
||||
/**
|
||||
* Defines the MenuActiveTrailsCacheContext service.
|
||||
*
|
||||
* This class is container-aware to avoid initializing the 'menu.active_trail'
|
||||
* service (and its dependencies) when it is not necessary.
|
||||
*/
|
||||
class MenuActiveTrailsCacheContext extends ContainerAware implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t("Active menu trail");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($menu_name = NULL) {
|
||||
$active_trail = $this->container->get('menu.active_trail')
|
||||
->getActiveTrailIds($menu_name);
|
||||
return 'menu_trail.' . $menu_name . '|' . implode('|', $active_trail);
|
||||
}
|
||||
|
||||
}
|
37
core/lib/Drupal/Core/Cache/Context/PagersCacheContext.php
Normal file
37
core/lib/Drupal/Core/Cache/Context/PagersCacheContext.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\PagersCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines a cache context for "per page in a pager" caching.
|
||||
*/
|
||||
class PagersCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Pager');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @see pager_find_page()
|
||||
*/
|
||||
public function getContext($pager_id = NULL) {
|
||||
// The value of the 'page' query argument contains the information that
|
||||
// controls *all* pagers.
|
||||
if ($pager_id === NULL) {
|
||||
return 'pager' . $this->requestStack->getCurrentRequest()->query->get('page', '');
|
||||
}
|
||||
|
||||
return 'pager.' . $pager_id . '.' . pager_find_page($pager_id);
|
||||
}
|
||||
|
||||
}
|
38
core/lib/Drupal/Core/Cache/Context/QueryArgsCacheContext.php
Normal file
38
core/lib/Drupal/Core/Cache/Context/QueryArgsCacheContext.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\QueryArgsCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the QueryArgsCacheContext service, for "per query args" caching.
|
||||
*
|
||||
* A "host" is defined as the combination of URI scheme, domain name and port.
|
||||
*
|
||||
* @see Symfony\Component\HttpFoundation::getSchemeAndHttpHost()
|
||||
*/
|
||||
class QueryArgsCacheContext extends RequestStackCacheContextBase implements CalculatedCacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Query arguments');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($query_arg = NULL) {
|
||||
if ($query_arg === NULL) {
|
||||
return $this->requestStack->getCurrentRequest()->getQueryString();
|
||||
}
|
||||
else {
|
||||
return $this->requestStack->getCurrentRequest()->query->get($query_arg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\RequestFormatCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the RequestFormatCacheContext service, for "per format" caching.
|
||||
*/
|
||||
class RequestFormatCacheContext extends RequestStackCacheContextBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Request format');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->requestStack->getCurrentRequest()->getRequestFormat();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\RequestStackCacheContextBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
/**
|
||||
* Defines a base class for cache contexts depending only on the request stack.
|
||||
*/
|
||||
abstract class RequestStackCacheContextBase implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* The request stack.
|
||||
*
|
||||
* @var \Symfony\Component\HttpFoundation\RequestStack
|
||||
*/
|
||||
protected $requestStack;
|
||||
|
||||
/**
|
||||
* Constructs a new RequestStackCacheContextBase class.
|
||||
*
|
||||
* @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
||||
* The request stack.
|
||||
*/
|
||||
public function __construct(RequestStack $request_stack) {
|
||||
$this->requestStack = $request_stack;
|
||||
}
|
||||
|
||||
}
|
48
core/lib/Drupal/Core/Cache/Context/RouteCacheContext.php
Normal file
48
core/lib/Drupal/Core/Cache/Context/RouteCacheContext.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\RouteCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
|
||||
/**
|
||||
* Defines the RouteCacheContext service, for "per route" caching.
|
||||
*/
|
||||
class RouteCacheContext implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* The route match.
|
||||
*
|
||||
* @var \Drupal\Core\Routing\RouteMatchInterface
|
||||
*/
|
||||
protected $routeMatch;
|
||||
|
||||
/**
|
||||
* Constructs a new RouteCacheContext class.
|
||||
*
|
||||
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
||||
* The route match.
|
||||
*/
|
||||
public function __construct(RouteMatchInterface $route_match) {
|
||||
$this->routeMatch = $route_match;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Route');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->routeMatch->getRouteName() . hash('sha256', serialize($this->routeMatch->getRawParameters()->all()));
|
||||
}
|
||||
|
||||
}
|
29
core/lib/Drupal/Core/Cache/Context/RouteNameCacheContext.php
Normal file
29
core/lib/Drupal/Core/Cache/Context/RouteNameCacheContext.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\RouteNameCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the RouteCacheContext service, for "per route name" caching.
|
||||
*/
|
||||
class RouteNameCacheContext extends RouteCacheContext {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Route name');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->routeMatch->getRouteName();
|
||||
}
|
||||
|
||||
}
|
38
core/lib/Drupal/Core/Cache/Context/SiteCacheContext.php
Normal file
38
core/lib/Drupal/Core/Cache/Context/SiteCacheContext.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\SiteCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the SiteCacheContext service, for "per site" caching.
|
||||
*
|
||||
* A "site" is defined as the combination of URI scheme, domain name, port and
|
||||
* base path. It allows for varying between the *same* site being accessed via
|
||||
* different entry points. (Different sites in a multisite setup have separate
|
||||
* databases.) For example: http://example.com and http://www.example.com.
|
||||
*
|
||||
* @see \Symfony\Component\HttpFoundation\Request::getSchemeAndHttpHost()
|
||||
* @see \Symfony\Component\HttpFoundation\Request::getBaseUrl()
|
||||
*/
|
||||
class SiteCacheContext extends RequestStackCacheContextBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Site');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
return $request->getSchemeAndHttpHost() . $request->getBaseUrl();
|
||||
}
|
||||
|
||||
}
|
49
core/lib/Drupal/Core/Cache/Context/ThemeCacheContext.php
Normal file
49
core/lib/Drupal/Core/Cache/Context/ThemeCacheContext.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\ThemeCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Drupal\Core\Theme\ThemeManagerInterface;
|
||||
|
||||
/**
|
||||
* Defines the ThemeCacheContext service, for "per theme" caching.
|
||||
*/
|
||||
class ThemeCacheContext implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* The theme manager.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* Constructs a new ThemeCacheContext service.
|
||||
*
|
||||
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
|
||||
* The theme manager.
|
||||
*/
|
||||
public function __construct(ThemeManagerInterface $theme_manager) {
|
||||
$this->themeManager = $theme_manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('Theme');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->themeManager->getActiveTheme()->getName() ?: 'stark';
|
||||
}
|
||||
|
||||
}
|
33
core/lib/Drupal/Core/Cache/Context/TimeZoneCacheContext.php
Normal file
33
core/lib/Drupal/Core/Cache/Context/TimeZoneCacheContext.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\TimeZoneCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the TimeZoneCacheContext service, for "per time zone" caching.
|
||||
*
|
||||
* @see \Drupal\Core\Session\AccountProxy::setAccount()
|
||||
*/
|
||||
class TimeZoneCacheContext implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t("Time zone");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
// date_default_timezone_set() is called in AccountProxy::setAccount(), so
|
||||
// we can safely retrieve the timezone.
|
||||
return date_default_timezone_get();
|
||||
}
|
||||
|
||||
}
|
29
core/lib/Drupal/Core/Cache/Context/UrlCacheContext.php
Normal file
29
core/lib/Drupal/Core/Cache/Context/UrlCacheContext.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\UrlCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the UrlCacheContext service, for "per page" caching.
|
||||
*/
|
||||
class UrlCacheContext extends RequestStackCacheContextBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('URL');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return $this->requestStack->getCurrentRequest()->getUri();
|
||||
}
|
||||
|
||||
}
|
41
core/lib/Drupal/Core/Cache/Context/UserCacheContext.php
Normal file
41
core/lib/Drupal/Core/Cache/Context/UserCacheContext.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\UserCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
|
||||
/**
|
||||
* Defines the UserCacheContext service, for "per user" caching.
|
||||
*/
|
||||
class UserCacheContext implements CacheContextInterface {
|
||||
|
||||
/**
|
||||
* Constructs a new UserCacheContext service.
|
||||
*
|
||||
* @param \Drupal\Core\Session\AccountInterface $user
|
||||
* The current user.
|
||||
*/
|
||||
public function __construct(AccountInterface $user) {
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t('User');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext() {
|
||||
return "u." . $this->user->id();
|
||||
}
|
||||
|
||||
}
|
44
core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php
Normal file
44
core/lib/Drupal/Core/Cache/Context/UserRolesCacheContext.php
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\Context\UserRolesCacheContext.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache\Context;
|
||||
|
||||
/**
|
||||
* Defines the UserRolesCacheContext service, for "per role" caching.
|
||||
*
|
||||
* Only use this cache context when checking explicitly for certain roles. Use
|
||||
* user.permissions for anything that checks permissions.
|
||||
*/
|
||||
class UserRolesCacheContext extends UserCacheContext implements CalculatedCacheContextInterface{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getLabel() {
|
||||
return t("User's roles");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getContext($role = NULL) {
|
||||
// User 1 does not actually have any special behavior for roles; this is
|
||||
// added as additional security and backwards compatibility protection for
|
||||
// SA-CORE-2015-002.
|
||||
// @todo Remove in Drupal 9.0.0.
|
||||
if ($this->user->id() == 1) {
|
||||
return 'is-super-user';
|
||||
}
|
||||
if ($role === NULL) {
|
||||
return 'r.' . implode(',', $this->user->getRoles());
|
||||
}
|
||||
else {
|
||||
return 'r.' . $role . '.' . (in_array($role, $this->user->getRoles()) ? '0' : '1');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
510
core/lib/Drupal/Core/Cache/DatabaseBackend.php
Normal file
510
core/lib/Drupal/Core/Cache/DatabaseBackend.php
Normal file
|
@ -0,0 +1,510 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\DatabaseBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
|
||||
/**
|
||||
* Defines a default cache implementation.
|
||||
*
|
||||
* This is Drupal's default cache implementation. It uses the database to store
|
||||
* cached data. Each cache bin corresponds to a database table by the same name.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class DatabaseBackend implements CacheBackendInterface {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bin;
|
||||
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The cache tags checksum provider.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsChecksumInterface
|
||||
*/
|
||||
protected $checksumProvider;
|
||||
|
||||
/**
|
||||
* Constructs a DatabaseBackend object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
* @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
|
||||
* The cache tags checksum provider.
|
||||
* @param string $bin
|
||||
* The cache bin for which the object is created.
|
||||
*/
|
||||
public function __construct(Connection $connection, CacheTagsChecksumInterface $checksum_provider, $bin) {
|
||||
// All cache tables should be prefixed with 'cache_'.
|
||||
$bin = 'cache_' . $bin;
|
||||
|
||||
$this->bin = $bin;
|
||||
$this->connection = $connection;
|
||||
$this->checksumProvider = $checksum_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::get().
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
$cids = array($cid);
|
||||
$cache = $this->getMultiple($cids, $allow_invalid);
|
||||
return reset($cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$cid_mapping = array();
|
||||
foreach ($cids as $cid) {
|
||||
$cid_mapping[$this->normalizeCid($cid)] = $cid;
|
||||
}
|
||||
// When serving cached pages, the overhead of using ::select() was found
|
||||
// to add around 30% overhead to the request. Since $this->bin is a
|
||||
// variable, this means the call to ::query() here uses a concatenated
|
||||
// string. This is highly discouraged under any other circumstances, and
|
||||
// is used here only due to the performance overhead we would incur
|
||||
// otherwise. When serving an uncached page, the overhead of using
|
||||
// ::select() is a much smaller proportion of the request.
|
||||
$result = array();
|
||||
try {
|
||||
$result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN ( :cids[] ) ORDER BY cid', array(':cids[]' => array_keys($cid_mapping)));
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Nothing to do.
|
||||
}
|
||||
$cache = array();
|
||||
foreach ($result as $item) {
|
||||
// Map the cache ID back to the original.
|
||||
$item->cid = $cid_mapping[$item->cid];
|
||||
$item = $this->prepareItem($item, $allow_invalid);
|
||||
if ($item) {
|
||||
$cache[$item->cid] = $item;
|
||||
}
|
||||
}
|
||||
$cids = array_diff($cids, array_keys($cache));
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a cached item.
|
||||
*
|
||||
* Checks that items are either permanent or did not expire, and unserializes
|
||||
* data as appropriate.
|
||||
*
|
||||
* @param object $cache
|
||||
* An item loaded from cache_get() or cache_get_multiple().
|
||||
* @param bool $allow_invalid
|
||||
* If FALSE, the method returns FALSE if the cache item is not valid.
|
||||
*
|
||||
* @return mixed|false
|
||||
* The item with data unserialized as appropriate and a property indicating
|
||||
* whether the item is valid, or FALSE if there is no valid item to load.
|
||||
*/
|
||||
protected function prepareItem($cache, $allow_invalid) {
|
||||
if (!isset($cache->data)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
$cache->tags = $cache->tags ? explode(' ', $cache->tags) : array();
|
||||
|
||||
// Check expire time.
|
||||
$cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
|
||||
|
||||
// Check if invalidateTags() has been called with any of the items's tags.
|
||||
if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
|
||||
$cache->valid = FALSE;
|
||||
}
|
||||
|
||||
if (!$allow_invalid && !$cache->valid) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Unserialize and return the cached data.
|
||||
if ($cache->serialized) {
|
||||
$cache->data = unserialize($cache->data);
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
$tags = array_unique($tags);
|
||||
// Sort the cache tags so that they are stored consistently in the database.
|
||||
sort($tags);
|
||||
$try_again = FALSE;
|
||||
try {
|
||||
// The bin might not yet exist.
|
||||
$this->doSet($cid, $data, $expire, $tags);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If there was an exception, try to create the bins.
|
||||
if (!$try_again = $this->ensureBinExists()) {
|
||||
// If the exception happened for other reason than the missing bin
|
||||
// table, propagate the exception.
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
// Now that the bin has been created, try again if necessary.
|
||||
if ($try_again) {
|
||||
$this->doSet($cid, $data, $expire, $tags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually set the cache.
|
||||
*/
|
||||
protected function doSet($cid, $data, $expire, $tags) {
|
||||
$fields = array(
|
||||
'created' => round(microtime(TRUE), 3),
|
||||
'expire' => $expire,
|
||||
'tags' => implode(' ', $tags),
|
||||
'checksum' => $this->checksumProvider->getCurrentChecksum($tags),
|
||||
);
|
||||
if (!is_string($data)) {
|
||||
$fields['data'] = serialize($data);
|
||||
$fields['serialized'] = 1;
|
||||
}
|
||||
else {
|
||||
$fields['data'] = $data;
|
||||
$fields['serialized'] = 0;
|
||||
}
|
||||
|
||||
$this->connection->merge($this->bin)
|
||||
->key('cid', $this->normalizeCid($cid))
|
||||
->fields($fields)
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items) {
|
||||
$values = array();
|
||||
|
||||
foreach ($items as $cid => $item) {
|
||||
$item += array(
|
||||
'expire' => CacheBackendInterface::CACHE_PERMANENT,
|
||||
'tags' => array(),
|
||||
);
|
||||
|
||||
Cache::validateTags($item['tags']);
|
||||
$item['tags'] = array_unique($item['tags']);
|
||||
// Sort the cache tags so that they are stored consistently in the DB.
|
||||
sort($item['tags']);
|
||||
|
||||
$fields = array(
|
||||
'cid' => $cid,
|
||||
'expire' => $item['expire'],
|
||||
'created' => round(microtime(TRUE), 3),
|
||||
'tags' => implode(' ', $item['tags']),
|
||||
'checksum' => $this->checksumProvider->getCurrentChecksum($item['tags']),
|
||||
);
|
||||
|
||||
if (!is_string($item['data'])) {
|
||||
$fields['data'] = serialize($item['data']);
|
||||
$fields['serialized'] = 1;
|
||||
}
|
||||
else {
|
||||
$fields['data'] = $item['data'];
|
||||
$fields['serialized'] = 0;
|
||||
}
|
||||
$values[] = $fields;
|
||||
}
|
||||
|
||||
// Use a transaction so that the database can write the changes in a single
|
||||
// commit. The transaction is started after calculating the tag checksums
|
||||
// since that can create a table and this causes an exception when using
|
||||
// PostgreSQL.
|
||||
$transaction = $this->connection->startTransaction();
|
||||
|
||||
try {
|
||||
// Delete all items first so we can do one insert. Rather than multiple
|
||||
// merge queries.
|
||||
$this->deleteMultiple(array_keys($items));
|
||||
|
||||
$query = $this->connection
|
||||
->insert($this->bin)
|
||||
->fields(array('cid', 'expire', 'created', 'tags', 'checksum', 'data', 'serialized'));
|
||||
foreach ($values as $fields) {
|
||||
// Only pass the values since the order of $fields matches the order of
|
||||
// the insert fields. This is a performance optimization to avoid
|
||||
// unnecessary loops within the method.
|
||||
$query->values(array_values($fields));
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$transaction->rollback();
|
||||
// @todo Log something here or just re throw?
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::delete().
|
||||
*/
|
||||
public function delete($cid) {
|
||||
$this->deleteMultiple(array($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
$cids = array_values(array_map(array($this, 'normalizeCid'), $cids));
|
||||
try {
|
||||
// Delete in chunks when a large array is passed.
|
||||
foreach (array_chunk($cids, 1000) as $cids_chunk) {
|
||||
$this->connection->delete($this->bin)
|
||||
->condition('cid', $cids_chunk, 'IN')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Create the cache table, which will be empty. This fixes cases during
|
||||
// core install where a cache table is cleared before it is set
|
||||
// with {cache_render} and {cache_data}.
|
||||
if (!$this->ensureBinExists()) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
|
||||
*/
|
||||
public function deleteAll() {
|
||||
try {
|
||||
$this->connection->truncate($this->bin)->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Create the cache table, which will be empty. This fixes cases during
|
||||
// core install where a cache table is cleared before it is set
|
||||
// with {cache_render} and {cache_data}.
|
||||
if (!$this->ensureBinExists()) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidate().
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
$this->invalidateMultiple(array($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple().
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
$cids = array_values(array_map(array($this, 'normalizeCid'), $cids));
|
||||
try {
|
||||
// Update in chunks when a large array is passed.
|
||||
foreach (array_chunk($cids, 1000) as $cids_chunk) {
|
||||
$this->connection->update($this->bin)
|
||||
->fields(array('expire' => REQUEST_TIME - 1))
|
||||
->condition('cid', $cids_chunk, 'IN')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
try {
|
||||
$this->connection->update($this->bin)
|
||||
->fields(array('expire' => REQUEST_TIME - 1))
|
||||
->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
try {
|
||||
$this->connection->delete($this->bin)
|
||||
->condition('expire', Cache::PERMANENT, '<>')
|
||||
->condition('expire', REQUEST_TIME, '<')
|
||||
->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If the table does not exist, it surely does not have garbage in it.
|
||||
// If the table exists, the next garbage collection will clean up.
|
||||
// There is nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
try {
|
||||
$this->connection->schema()->dropTable($this->bin);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cache bin exists and create it if not.
|
||||
*/
|
||||
protected function ensureBinExists() {
|
||||
try {
|
||||
$database_schema = $this->connection->schema();
|
||||
if (!$database_schema->tableExists($this->bin)) {
|
||||
$schema_definition = $this->schemaDefinition();
|
||||
$database_schema->createTable($this->bin, $schema_definition);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
// If another process has already created the cache table, attempting to
|
||||
// recreate it will throw an exception. In this case just catch the
|
||||
// exception and do nothing.
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on an exception when cache might be stale.
|
||||
*
|
||||
* If the table does not yet exist, that's fine, but if the table exists and
|
||||
* yet the query failed, then the cache is stale and the exception needs to
|
||||
* propagate.
|
||||
*
|
||||
* @param $e
|
||||
* The exception.
|
||||
* @param string|null $table_name
|
||||
* The table name. Defaults to $this->bin.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function catchException(\Exception $e, $table_name = NULL) {
|
||||
if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a cache ID in order to comply with database limitations.
|
||||
*
|
||||
* @param string $cid
|
||||
* The passed in cache ID.
|
||||
*
|
||||
* @return string
|
||||
* An ASCII-encoded cache ID that is at most 255 characters long.
|
||||
*/
|
||||
protected function normalizeCid($cid) {
|
||||
// Nothing to do if the ID is a US ASCII string of 255 characters or less.
|
||||
$cid_is_ascii = mb_check_encoding($cid, 'ASCII');
|
||||
if (strlen($cid) <= 255 && $cid_is_ascii) {
|
||||
return $cid;
|
||||
}
|
||||
// Return a string that uses as much as possible of the original cache ID
|
||||
// with the hash appended.
|
||||
$hash = Crypt::hashBase64($cid);
|
||||
if (!$cid_is_ascii) {
|
||||
return $hash;
|
||||
}
|
||||
return substr($cid, 0, 255 - strlen($hash)) . $hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the schema for the {cache_*} bin tables.
|
||||
*/
|
||||
public function schemaDefinition() {
|
||||
$schema = array(
|
||||
'description' => 'Storage for the cache API.',
|
||||
'fields' => array(
|
||||
'cid' => array(
|
||||
'description' => 'Primary Key: Unique cache ID.',
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
'binary' => TRUE,
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'A collection of data to cache.',
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
),
|
||||
'expire' => array(
|
||||
'description' => 'A Unix timestamp indicating when the cache entry should expire, or ' . Cache::PERMANENT . ' for never.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'created' => array(
|
||||
'description' => 'A timestamp with millisecond precision indicating when the cache entry was created.',
|
||||
'type' => 'numeric',
|
||||
'precision' => 14,
|
||||
'scale' => 3,
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'serialized' => array(
|
||||
'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
|
||||
'type' => 'int',
|
||||
'size' => 'small',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
'tags' => array(
|
||||
'description' => 'Space-separated list of cache tags for this entry.',
|
||||
'type' => 'text',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
),
|
||||
'checksum' => array(
|
||||
'description' => 'The tag invalidation checksum when this entry was saved.',
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
),
|
||||
),
|
||||
'indexes' => array(
|
||||
'expire' => array('expire'),
|
||||
),
|
||||
'primary key' => array('cid'),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
}
|
54
core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php
Normal file
54
core/lib/Drupal/Core/Cache/DatabaseBackendFactory.php
Normal file
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\DatabaseBackendFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
|
||||
class DatabaseBackendFactory implements CacheFactoryInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* The cache tags checksum provider.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsChecksumInterface
|
||||
*/
|
||||
protected $checksumProvider;
|
||||
|
||||
/**
|
||||
* Constructs the DatabaseBackendFactory object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* Database connection
|
||||
* @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
|
||||
* The cache tags checksum provider.
|
||||
*/
|
||||
function __construct(Connection $connection, CacheTagsChecksumInterface $checksum_provider) {
|
||||
$this->connection = $connection;
|
||||
$this->checksumProvider = $checksum_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets DatabaseBackend for the specified cache bin.
|
||||
*
|
||||
* @param $bin
|
||||
* The cache bin for which the object is created.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\DatabaseBackend
|
||||
* The cache backend object for the specified cache bin.
|
||||
*/
|
||||
function get($bin) {
|
||||
return new DatabaseBackend($this->connection, $this->checksumProvider, $bin);
|
||||
}
|
||||
|
||||
}
|
213
core/lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php
Normal file
213
core/lib/Drupal/Core/Cache/DatabaseCacheTagsChecksum.php
Normal file
|
@ -0,0 +1,213 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\DatabaseCacheTagsChecksum.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
|
||||
/**
|
||||
* Cache tags invalidations checksum implementation that uses the database.
|
||||
*/
|
||||
class DatabaseCacheTagsChecksum implements CacheTagsChecksumInterface, CacheTagsInvalidatorInterface {
|
||||
|
||||
/**
|
||||
* The database connection.
|
||||
*
|
||||
* @var \Drupal\Core\Database\Connection
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Contains already loaded cache invalidations from the database.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $tagCache = array();
|
||||
|
||||
/**
|
||||
* A list of tags that have already been invalidated in this request.
|
||||
*
|
||||
* Used to prevent the invalidation of the same cache tag multiple times.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $invalidatedTags = array();
|
||||
|
||||
/**
|
||||
* Constructs a DatabaseCacheTagsChecksum object.
|
||||
*
|
||||
* @param \Drupal\Core\Database\Connection $connection
|
||||
* The database connection.
|
||||
*/
|
||||
public function __construct(Connection $connection) {
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
try {
|
||||
foreach ($tags as $tag) {
|
||||
// Only invalidate tags once per request unless they are written again.
|
||||
if (isset($this->invalidatedTags[$tag])) {
|
||||
continue;
|
||||
}
|
||||
$this->invalidatedTags[$tag] = TRUE;
|
||||
unset($this->tagCache[$tag]);
|
||||
$this->connection->merge('cachetags')
|
||||
->insertFields(array('invalidations' => 1))
|
||||
->expression('invalidations', 'invalidations + 1')
|
||||
->key('tag', $tag)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// Create the cache table, which will be empty. This fixes cases during
|
||||
// core install where cache tags are invalidated before the table is
|
||||
// created.
|
||||
if (!$this->ensureTableExists()) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCurrentChecksum(array $tags) {
|
||||
// Remove tags that were already invalidated during this request from the
|
||||
// static caches so that another invalidation can occur later in the same
|
||||
// request. Without that, written cache items would not be invalidated
|
||||
// correctly.
|
||||
foreach ($tags as $tag) {
|
||||
unset($this->invalidatedTags[$tag]);
|
||||
}
|
||||
return $this->calculateChecksum($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isValid($checksum, array $tags) {
|
||||
return $checksum == $this->calculateChecksum($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the current checksum for a given set of tags.
|
||||
*
|
||||
* @param array $tags
|
||||
* The array of tags to calculate the checksum for.
|
||||
*
|
||||
* @return int
|
||||
* The calculated checksum.
|
||||
*/
|
||||
protected function calculateChecksum(array $tags) {
|
||||
$checksum = 0;
|
||||
|
||||
$query_tags = array_diff($tags, array_keys($this->tagCache));
|
||||
if ($query_tags) {
|
||||
$db_tags = array();
|
||||
try {
|
||||
$db_tags = $this->connection->query('SELECT tag, invalidations FROM {cachetags} WHERE tag IN ( :tags[] )', array(':tags[]' => $query_tags))
|
||||
->fetchAllKeyed();
|
||||
$this->tagCache += $db_tags;
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If the table does not exist yet, create.
|
||||
if (!$this->ensureTableExists()) {
|
||||
$this->catchException($e);
|
||||
}
|
||||
}
|
||||
// Fill static cache with empty objects for tags not found in the database.
|
||||
$this->tagCache += array_fill_keys(array_diff($query_tags, array_keys($db_tags)), 0);
|
||||
}
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
$checksum += $this->tagCache[$tag];
|
||||
}
|
||||
|
||||
return $checksum;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset() {
|
||||
$this->tagCache = array();
|
||||
$this->invalidatedTags = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cache tags table exists and create it if not.
|
||||
*/
|
||||
protected function ensureTableExists() {
|
||||
try {
|
||||
$database_schema = $this->connection->schema();
|
||||
// Create the cache tags table if it does not exist.
|
||||
if (!$database_schema->tableExists('cachetags')) {
|
||||
$schema_definition = $this->schemaDefinition();
|
||||
$database_schema->createTable('cachetags', $schema_definition);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
// If another process has already created the cachetags table, attempting to
|
||||
// recreate it will throw an exception. In this case just catch the
|
||||
// exception and do nothing.
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the schema for the {cachetags} table.
|
||||
*/
|
||||
public function schemaDefinition() {
|
||||
$schema = array(
|
||||
'description' => 'Cache table for tracking cache tag invalidations.',
|
||||
'fields' => array(
|
||||
'tag' => array(
|
||||
'description' => 'Namespace-prefixed tag string.',
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'invalidations' => array(
|
||||
'description' => 'Number incremented when the tag is invalidated.',
|
||||
'type' => 'int',
|
||||
'not null' => TRUE,
|
||||
'default' => 0,
|
||||
),
|
||||
),
|
||||
'primary key' => array('tag'),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Act on an exception when cache might be stale.
|
||||
*
|
||||
* If the {cachetags} table does not yet exist, that's fine but if the table
|
||||
* exists and yet the query failed, then the cache is stale and the
|
||||
* exception needs to propagate.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* The exception.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function catchException(\Exception $e) {
|
||||
if ($this->connection->schema()->tableExists('cachetags')) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
36
core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
Normal file
36
core/lib/Drupal/Core/Cache/ListCacheBinsPass.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\ListCacheBinsPass.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
|
||||
/**
|
||||
* Adds cache_bins parameter to the container.
|
||||
*/
|
||||
class ListCacheBinsPass implements CompilerPassInterface {
|
||||
|
||||
/**
|
||||
* Implements CompilerPassInterface::process().
|
||||
*
|
||||
* Collects the cache bins into the cache_bins parameter.
|
||||
*/
|
||||
public function process(ContainerBuilder $container) {
|
||||
$cache_bins = array();
|
||||
$cache_default_bin_backends = array();
|
||||
foreach ($container->findTaggedServiceIds('cache.bin') as $id => $attributes) {
|
||||
$bin = substr($id, strpos($id, '.') + 1);
|
||||
$cache_bins[$id] = $bin;
|
||||
if (isset($attributes[0]['default_backend'])) {
|
||||
$cache_default_bin_backends[$bin] = $attributes[0]['default_backend'];
|
||||
}
|
||||
}
|
||||
$container->setParameter('cache_bins', $cache_bins);
|
||||
$container->setParameter('cache_default_bin_backends', $cache_default_bin_backends);
|
||||
}
|
||||
}
|
220
core/lib/Drupal/Core/Cache/MemoryBackend.php
Normal file
220
core/lib/Drupal/Core/Cache/MemoryBackend.php
Normal file
|
@ -0,0 +1,220 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\MemoryBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines a memory cache implementation.
|
||||
*
|
||||
* Stores cache items in memory using a PHP array.
|
||||
*
|
||||
* Should be used for unit tests and specialist use-cases only, does not
|
||||
* store cached items between requests.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class MemoryBackend implements CacheBackendInterface, CacheTagsInvalidatorInterface {
|
||||
|
||||
/**
|
||||
* Array to store cache objects.
|
||||
*/
|
||||
protected $cache = array();
|
||||
|
||||
/**
|
||||
* Constructs a MemoryBackend object.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which the object is created.
|
||||
*/
|
||||
public function __construct($bin) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::get().
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
if (isset($this->cache[$cid])) {
|
||||
return $this->prepareItem($this->cache[$cid], $allow_invalid);
|
||||
}
|
||||
else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$ret = array();
|
||||
|
||||
$items = array_intersect_key($this->cache, array_flip($cids));
|
||||
|
||||
foreach ($items as $item) {
|
||||
$item = $this->prepareItem($item, $allow_invalid);
|
||||
if ($item) {
|
||||
$ret[$item->cid] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$cids = array_diff($cids, array_keys($ret));
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a cached item.
|
||||
*
|
||||
* Checks that items are either permanent or did not expire, and returns data
|
||||
* as appropriate.
|
||||
*
|
||||
* @param object $cache
|
||||
* An item loaded from cache_get() or cache_get_multiple().
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, cache items may be returned even if they have expired
|
||||
* or been invalidated.
|
||||
*
|
||||
* @return mixed
|
||||
* The item with data as appropriate or FALSE if there is no
|
||||
* valid item to load.
|
||||
*/
|
||||
protected function prepareItem($cache, $allow_invalid) {
|
||||
if (!isset($cache->data)) {
|
||||
return FALSE;
|
||||
}
|
||||
// The object passed into this function is the one stored in $this->cache.
|
||||
// We must clone it as part of the preparation step so that the actual
|
||||
// cache object is not affected by the unserialize() call or other
|
||||
// manipulations of the returned object.
|
||||
|
||||
$prepared = clone $cache;
|
||||
$prepared->data = unserialize($prepared->data);
|
||||
|
||||
// Check expire time.
|
||||
$prepared->valid = $prepared->expire == Cache::PERMANENT || $prepared->expire >= $this->getRequestTime();
|
||||
|
||||
if (!$allow_invalid && !$prepared->valid) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $prepared;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
$tags = array_unique($tags);
|
||||
// Sort the cache tags so that they are stored consistently in the database.
|
||||
sort($tags);
|
||||
$this->cache[$cid] = (object) array(
|
||||
'cid' => $cid,
|
||||
'data' => serialize($data),
|
||||
'created' => $this->getRequestTime(),
|
||||
'expire' => $expire,
|
||||
'tags' => $tags,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items = array()) {
|
||||
foreach ($items as $cid => $item) {
|
||||
$this->set($cid, $item['data'], isset($item['expire']) ? $item['expire'] : CacheBackendInterface::CACHE_PERMANENT, isset($item['tags']) ? $item['tags'] : array());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::delete().
|
||||
*/
|
||||
public function delete($cid) {
|
||||
unset($this->cache[$cid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
$this->cache = array_diff_key($this->cache, array_flip($cids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
|
||||
*/
|
||||
public function deleteAll() {
|
||||
$this->cache = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidate().
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
if (isset($this->cache[$cid])) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple().
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
foreach ($cids as $cid) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags) {
|
||||
foreach ($this->cache as $cid => $item) {
|
||||
if (array_intersect($tags, $item->tags)) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
foreach ($this->cache as $cid => $item) {
|
||||
$this->cache[$cid]->expire = $this->getRequestTime() - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection()
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
$this->cache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper method for REQUEST_TIME constant.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getRequestTime() {
|
||||
return defined('REQUEST_TIME') ? REQUEST_TIME : (int) $_SERVER['REQUEST_TIME'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents data stored in memory backends from being serialized.
|
||||
*/
|
||||
public function __sleep() {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
29
core/lib/Drupal/Core/Cache/MemoryBackendFactory.php
Normal file
29
core/lib/Drupal/Core/Cache/MemoryBackendFactory.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\MemoryBackendFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
class MemoryBackendFactory implements CacheFactoryInterface {
|
||||
|
||||
/**
|
||||
* Instantiated memory cache bins.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\MemoryBackend[]
|
||||
*/
|
||||
protected $bins = array();
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function get($bin) {
|
||||
if (!isset($this->bins[$bin])) {
|
||||
$this->bins[$bin] = new MemoryBackend($bin);
|
||||
}
|
||||
return $this->bins[$bin];
|
||||
}
|
||||
|
||||
}
|
100
core/lib/Drupal/Core/Cache/MemoryCounterBackend.php
Normal file
100
core/lib/Drupal/Core/Cache/MemoryCounterBackend.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\MemoryCounterBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines a memory cache implementation that counts set and get calls.
|
||||
*
|
||||
* This can be used to mock a cache backend where one needs to know how
|
||||
* many times a cache entry was set or requested.
|
||||
*
|
||||
* @todo On the longrun this backend should be replaced by phpunit mock objects.
|
||||
*
|
||||
*/
|
||||
class MemoryCounterBackend extends MemoryBackend {
|
||||
|
||||
/**
|
||||
* Stores a list of cache cid calls keyed by function name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $counter = array();
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Cache\CacheBackendInterface::get().
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
$this->increaseCounter(__FUNCTION__, $cid);
|
||||
return parent::get($cid, $allow_invalid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
$this->increaseCounter(__FUNCTION__, $cid);
|
||||
parent::set($cid, $data, $expire, $tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements \Drupal\Core\Cache\CacheBackendInterface::delete().
|
||||
*/
|
||||
public function delete($cid) {
|
||||
$this->increaseCounter(__FUNCTION__, $cid);
|
||||
parent::delete($cid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the counter for a function with a certain cid.
|
||||
*
|
||||
* @param string $function
|
||||
* The called function.
|
||||
*
|
||||
* @param string $cid
|
||||
* The cache ID of the cache entry to increase the counter.
|
||||
*/
|
||||
protected function increaseCounter($function, $cid) {
|
||||
if (!isset($this->counter[$function][$cid])) {
|
||||
$this->counter[$function][$cid] = 1;
|
||||
}
|
||||
else {
|
||||
$this->counter[$function][$cid]++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call counter for the get, set and delete methods.
|
||||
*
|
||||
* @param string $method
|
||||
* (optional) The name of the method to return the call counter for.
|
||||
* @param string $cid
|
||||
* (optional) The name of the cache id to return the call counter for.
|
||||
*
|
||||
* @return int|array
|
||||
* An integer if both method and cid is given, an array otherwise.
|
||||
*/
|
||||
public function getCounter($method = NULL, $cid = NULL) {
|
||||
if ($method && $cid) {
|
||||
return isset($this->counter[$method][$cid]) ? $this->counter[$method][$cid] : 0;
|
||||
}
|
||||
elseif ($method) {
|
||||
return isset($this->counter[$method]) ? $this->counter[$method] : array();
|
||||
}
|
||||
else {
|
||||
return $this->counter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the call counter.
|
||||
*/
|
||||
public function resetCounter() {
|
||||
$this->counter = array();
|
||||
}
|
||||
|
||||
}
|
96
core/lib/Drupal/Core/Cache/NullBackend.php
Normal file
96
core/lib/Drupal/Core/Cache/NullBackend.php
Normal file
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\NullBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Defines a stub cache implementation.
|
||||
*
|
||||
* The stub implementation is needed when database access is not yet available.
|
||||
* Because Drupal's caching system never requires that cached data be present,
|
||||
* these stub functions can short-circuit the process and sidestep the need for
|
||||
* any persistent storage. Using this cache implementation during normal
|
||||
* operations would have a negative impact on performance.
|
||||
*
|
||||
* This also can be used for testing purposes.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class NullBackend implements CacheBackendInterface {
|
||||
|
||||
/**
|
||||
* Constructs a NullBackend object.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which the object is created.
|
||||
*/
|
||||
public function __construct($bin) {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::get().
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple().
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items = array()) {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::delete().
|
||||
*/
|
||||
public function delete($cid) {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
|
||||
*/
|
||||
public function deleteAll() {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidate().
|
||||
*/
|
||||
public function invalidate($cid) {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateMultiple().
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
|
||||
*/
|
||||
public function invalidateAll() {}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
|
||||
*/
|
||||
public function garbageCollection() {}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {}
|
||||
}
|
19
core/lib/Drupal/Core/Cache/NullBackendFactory.php
Normal file
19
core/lib/Drupal/Core/Cache/NullBackendFactory.php
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\NullBackendFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
class NullBackendFactory implements CacheFactoryInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
function get($bin) {
|
||||
return new NullBackend($bin);
|
||||
}
|
||||
|
||||
}
|
276
core/lib/Drupal/Core/Cache/PhpBackend.php
Normal file
276
core/lib/Drupal/Core/Cache/PhpBackend.php
Normal file
|
@ -0,0 +1,276 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\PhpBackend.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
use Drupal\Component\Utility\Crypt;
|
||||
|
||||
/**
|
||||
* Defines a PHP cache implementation.
|
||||
*
|
||||
* Stores cache items in a PHP file using a storage that implements
|
||||
* Drupal\Component\PhpStorage\PhpStorageInterface.
|
||||
*
|
||||
* This is fast because of PHP's opcode caching mechanism. Once a file's
|
||||
* content is stored in PHP's opcode cache, including it doesn't require
|
||||
* reading the contents from a filesystem. Instead, PHP will use the already
|
||||
* compiled opcodes stored in memory.
|
||||
*
|
||||
* @ingroup cache
|
||||
*/
|
||||
class PhpBackend implements CacheBackendInterface {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $bin;
|
||||
|
||||
/**
|
||||
* Array to store cache objects.
|
||||
*/
|
||||
protected $cache = array();
|
||||
|
||||
/**
|
||||
* The cache tags checksum provider.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsChecksumInterface
|
||||
*/
|
||||
protected $checksumProvider;
|
||||
|
||||
/**
|
||||
* Constructs a PhpBackend object.
|
||||
*
|
||||
* @param string $bin
|
||||
* The cache bin for which the object is created.
|
||||
* @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
|
||||
* The cache tags checksum provider.
|
||||
*/
|
||||
public function __construct($bin, CacheTagsChecksumInterface $checksum_provider) {
|
||||
$this->bin = 'cache_' . $bin;
|
||||
$this->checksumProvider = $checksum_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($cid, $allow_invalid = FALSE) {
|
||||
return $this->getByHash($this->normalizeCid($cid), $allow_invalid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a cache item using a hashed cache ID.
|
||||
*
|
||||
* @param string $cidhash
|
||||
* The hashed version of the original cache ID after being normalized.
|
||||
* @param bool $allow_invalid
|
||||
* (optional) If TRUE, a cache item may be returned even if it is expired or
|
||||
* has been invalidated.
|
||||
*
|
||||
* @return bool|mixed
|
||||
*/
|
||||
protected function getByHash($cidhash, $allow_invalid = FALSE) {
|
||||
if ($file = $this->storage()->getFullPath($cidhash)) {
|
||||
$cache = @include $file;
|
||||
}
|
||||
if (isset($cache)) {
|
||||
return $this->prepareItem($cache, $allow_invalid);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function setMultiple(array $items) {
|
||||
foreach ($items as $cid => $item) {
|
||||
$this->set($cid, $item['data'], isset($item['expire']) ? $item['expire'] : CacheBackendInterface::CACHE_PERMANENT, isset($item['tags']) ? $item['tags'] : array());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMultiple(&$cids, $allow_invalid = FALSE) {
|
||||
$ret = array();
|
||||
|
||||
foreach ($cids as $cid) {
|
||||
if ($item = $this->get($cid, $allow_invalid)) {
|
||||
$ret[$item->cid] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$cids = array_diff($cids, array_keys($ret));
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a cached item.
|
||||
*
|
||||
* Checks that items are either permanent or did not expire, and returns data
|
||||
* as appropriate.
|
||||
*
|
||||
* @param object $cache
|
||||
* An item loaded from cache_get() or cache_get_multiple().
|
||||
* @param bool $allow_invalid
|
||||
* If FALSE, the method returns FALSE if the cache item is not valid.
|
||||
*
|
||||
* @return mixed
|
||||
* The item with data as appropriate or FALSE if there is no
|
||||
* valid item to load.
|
||||
*/
|
||||
protected function prepareItem($cache, $allow_invalid) {
|
||||
if (!isset($cache->data)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Check expire time.
|
||||
$cache->valid = $cache->expire == Cache::PERMANENT || $cache->expire >= REQUEST_TIME;
|
||||
|
||||
// Check if invalidateTags() has been called with any of the item's tags.
|
||||
if (!$this->checksumProvider->isValid($cache->checksum, $cache->tags)) {
|
||||
$cache->valid = FALSE;
|
||||
}
|
||||
|
||||
if (!$allow_invalid && !$cache->valid) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
|
||||
Cache::validateTags($tags);
|
||||
$item = (object) array(
|
||||
'cid' => $cid,
|
||||
'data' => $data,
|
||||
'created' => round(microtime(TRUE), 3),
|
||||
'expire' => $expire,
|
||||
'tags' => array_unique($tags),
|
||||
'checksum' => $this->checksumProvider->getCurrentChecksum($tags),
|
||||
);
|
||||
$this->writeItem($this->normalizeCid($cid), $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete($cid) {
|
||||
$this->storage()->delete($this->normalizeCid($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteMultiple(array $cids) {
|
||||
foreach ($cids as $cid) {
|
||||
$this->delete($cid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll() {
|
||||
$this->storage()->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidate($cid) {
|
||||
$this->invalidatebyHash($this->normalizeCid($cid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate one cache item.
|
||||
*
|
||||
* @param string $cidhash
|
||||
* The hashed version of the original cache ID after being normalized.
|
||||
*/
|
||||
protected function invalidatebyHash($cidhash) {
|
||||
if ($item = $this->getByHash($cidhash)) {
|
||||
$item->expire = REQUEST_TIME - 1;
|
||||
$this->writeItem($cidhash, $item);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateMultiple(array $cids) {
|
||||
foreach ($cids as $cid) {
|
||||
$this->invalidate($cid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateAll() {
|
||||
foreach($this->storage()->listAll() as $cidhash) {
|
||||
$this->invalidatebyHash($cidhash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function garbageCollection() {
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeBin() {
|
||||
$this->cache = array();
|
||||
$this->storage()->deleteAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a cache item to PhpStorage.
|
||||
*
|
||||
* @param string $cidhash
|
||||
* The hashed version of the original cache ID after being normalized.
|
||||
* @param \stdClass $item
|
||||
* The cache item to store.
|
||||
*/
|
||||
protected function writeItem($cidhash, \stdClass $item) {
|
||||
$content = '<?php return unserialize(' . var_export(serialize($item), TRUE) . ');';
|
||||
$this->storage()->save($cidhash, $content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the PHP code storage object to use.
|
||||
*
|
||||
* @return \Drupal\Component\PhpStorage\PhpStorageInterface
|
||||
*/
|
||||
protected function storage() {
|
||||
if (!isset($this->storage)) {
|
||||
$this->storage = PhpStorageFactory::get($this->bin);
|
||||
}
|
||||
return $this->storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a normalized cache ID.
|
||||
*
|
||||
* @param string $cid
|
||||
* The passed in cache ID.
|
||||
*
|
||||
* @return string
|
||||
* A normalized cache ID.
|
||||
*/
|
||||
protected function normalizeCid($cid) {
|
||||
return Crypt::hashBase64($cid);
|
||||
}
|
||||
|
||||
}
|
42
core/lib/Drupal/Core/Cache/PhpBackendFactory.php
Normal file
42
core/lib/Drupal/Core/Cache/PhpBackendFactory.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\PhpBackendFactory.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
class PhpBackendFactory implements CacheFactoryInterface {
|
||||
|
||||
/**
|
||||
* The cache tags checksum provider.
|
||||
*
|
||||
* @var \Drupal\Core\Cache\CacheTagsChecksumInterface
|
||||
*/
|
||||
protected $checksumProvider;
|
||||
|
||||
/**
|
||||
* Constructs a PhpBackendFactory object.
|
||||
*
|
||||
* @param \Drupal\Core\Cache\CacheTagsChecksumInterface $checksum_provider
|
||||
* The cache tags checksum provider.
|
||||
*/
|
||||
public function __construct(CacheTagsChecksumInterface $checksum_provider) {
|
||||
$this->checksumProvider = $checksum_provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets PhpBackend for the specified cache bin.
|
||||
*
|
||||
* @param $bin
|
||||
* The cache bin for which the object is created.
|
||||
*
|
||||
* @return \Drupal\Core\Cache\PhpBackend
|
||||
* The cache backend object for the specified cache bin.
|
||||
*/
|
||||
function get($bin) {
|
||||
return new PhpBackend($bin, $this->checksumProvider);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Core\Cache\UnchangingCacheableDependencyTrait.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Cache;
|
||||
|
||||
/**
|
||||
* Trait to implement CacheableDependencyInterface for unchanging objects.
|
||||
*
|
||||
* @see \Drupal\Core\Cache\CacheableDependencyInterface
|
||||
*/
|
||||
trait UnchangingCacheableDependencyTrait {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue