Initial Commit
This commit is contained in:
180
dependencies/symfony/cache/Adapter/AbstractAdapter.php
vendored
Normal file
180
dependencies/symfony/cache/Adapter/AbstractAdapter.php
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\AbstractAdapterTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
abstract class AbstractAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use AbstractAdapterTrait;
|
||||
use ContractsTrait;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected const NS_SEPARATOR = ':';
|
||||
private static $apcuSupported;
|
||||
private static $phpFilesSupported;
|
||||
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace) . static::NS_SEPARATOR;
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
|
||||
throw new InvalidArgumentException(\sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
|
||||
}
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->value = $v = $value;
|
||||
$item->isHit = $isHit;
|
||||
// Detect wrapped values that encode for their expiry and creation duration
|
||||
// For compactness, these values are packed in the key of an array using
|
||||
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||
if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) \array_key_first($v)) && "\x9d" === $k[0] && "\x00" === $k[5] && "_" === $k[9]) {
|
||||
$item->value = $v[$k];
|
||||
$v = \unpack('Ve/Nc', \substr($k, 1, -1));
|
||||
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
|
||||
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
|
||||
}
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
self::$mergeByLifetime ?? (self::$mergeByLifetime = \Closure::bind(static function ($deferred, $namespace, &$expiredIds, $getId, $defaultLifetime) {
|
||||
$byLifetime = [];
|
||||
$now = \microtime(\true);
|
||||
$expiredIds = [];
|
||||
foreach ($deferred as $key => $item) {
|
||||
$key = (string) $key;
|
||||
if (null === $item->expiry) {
|
||||
$ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
|
||||
} elseif (!$item->expiry) {
|
||||
$ttl = 0;
|
||||
} elseif (0 >= ($ttl = (int) (0.1 + $item->expiry - $now))) {
|
||||
$expiredIds[] = $getId($key);
|
||||
continue;
|
||||
}
|
||||
if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) {
|
||||
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||
}
|
||||
// For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
|
||||
$byLifetime[$ttl][$getId($key)] = $metadata ? ["\x9d" . \pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]) . "_" => $item->value] : $item->value;
|
||||
}
|
||||
return $byLifetime;
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* Returns the best possible adapter that your runtime supports.
|
||||
*
|
||||
* Using ApcuAdapter makes system caches compatible with read-only filesystems.
|
||||
*
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
public static function createSystemCache(string $namespace, int $defaultLifetime, string $version, string $directory, LoggerInterface $logger = null)
|
||||
{
|
||||
$opcache = new PhpFilesAdapter($namespace, $defaultLifetime, $directory, \true);
|
||||
if (null !== $logger) {
|
||||
$opcache->setLogger($logger);
|
||||
}
|
||||
if (!(self::$apcuSupported = self::$apcuSupported ?? ApcuAdapter::isSupported())) {
|
||||
return $opcache;
|
||||
}
|
||||
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true) && !\filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
|
||||
return $opcache;
|
||||
}
|
||||
$apcu = new ApcuAdapter($namespace, \intdiv($defaultLifetime, 5), $version);
|
||||
if (null !== $logger) {
|
||||
$apcu->setLogger($logger);
|
||||
}
|
||||
return new ChainAdapter([$apcu, $opcache]);
|
||||
}
|
||||
public static function createConnection(string $dsn, array $options = [])
|
||||
{
|
||||
if (\str_starts_with($dsn, 'redis:') || \str_starts_with($dsn, 'rediss:')) {
|
||||
return RedisAdapter::createConnection($dsn, $options);
|
||||
}
|
||||
if (\str_starts_with($dsn, 'memcached:')) {
|
||||
return MemcachedAdapter::createConnection($dsn, $options);
|
||||
}
|
||||
if (0 === \strpos($dsn, 'couchbase:')) {
|
||||
if (CouchbaseBucketAdapter::isSupported()) {
|
||||
return CouchbaseBucketAdapter::createConnection($dsn, $options);
|
||||
}
|
||||
return CouchbaseCollectionAdapter::createConnection($dsn, $options);
|
||||
}
|
||||
throw new InvalidArgumentException(\sprintf('Unsupported DSN: "%s".', $dsn));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$ok = \true;
|
||||
$byLifetime = (self::$mergeByLifetime)($this->deferred, $this->namespace, $expiredIds, \Closure::fromCallable([$this, 'getId']), $this->defaultLifetime);
|
||||
$retry = $this->deferred = [];
|
||||
if ($expiredIds) {
|
||||
try {
|
||||
$this->doDelete($expiredIds);
|
||||
} catch (\Exception $e) {
|
||||
$ok = \false;
|
||||
CacheItem::log($this->logger, 'Failed to delete expired items: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
}
|
||||
foreach ($byLifetime as $lifetime => $values) {
|
||||
try {
|
||||
$e = $this->doSave($values, $lifetime);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (\true === $e || [] === $e) {
|
||||
continue;
|
||||
}
|
||||
if (\is_array($e) || 1 === \count($values)) {
|
||||
foreach (\is_array($e) ? $e : \array_keys($values) as $id) {
|
||||
$ok = \false;
|
||||
$v = $values[$id];
|
||||
$type = \get_debug_type($v);
|
||||
$message = \sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => \substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
} else {
|
||||
foreach ($values as $id => $v) {
|
||||
$retry[$lifetime][] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// When bulk-save failed, retry each item individually
|
||||
foreach ($retry as $lifetime => $ids) {
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$v = $byLifetime[$lifetime][$id];
|
||||
$e = $this->doSave([$id => $v], $lifetime);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (\true === $e || [] === $e) {
|
||||
continue;
|
||||
}
|
||||
$ok = \false;
|
||||
$type = \get_debug_type($v);
|
||||
$message = \sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => \substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
}
|
287
dependencies/symfony/cache/Adapter/AbstractTagAwareAdapter.php
vendored
Normal file
287
dependencies/symfony/cache/Adapter/AbstractTagAwareAdapter.php
vendored
Normal file
@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\AbstractAdapterTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
/**
|
||||
* Abstract for native TagAware adapters.
|
||||
*
|
||||
* To keep info on tags, the tags are both serialized as part of cache value and provided as tag ids
|
||||
* to Adapters on operations when needed for storage to doSave(), doDelete() & doInvalidate().
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author André Rømcke <andre.romcke+symfony@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
abstract class AbstractTagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use AbstractAdapterTrait;
|
||||
use ContractsTrait;
|
||||
private const TAGS_PREFIX = "\x00tags\x00";
|
||||
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace) . ':';
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
|
||||
throw new InvalidArgumentException(\sprintf('Namespace must be %d chars max, %d given ("%s").', $this->maxIdLength - 24, \strlen($namespace), $namespace));
|
||||
}
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->isTaggable = \true;
|
||||
// If structure does not match what we expect return item as is (no value and not a hit)
|
||||
if (!\is_array($value) || !\array_key_exists('value', $value)) {
|
||||
return $item;
|
||||
}
|
||||
$item->isHit = $isHit;
|
||||
// Extract value, tags and meta data from the cache value
|
||||
$item->value = $value['value'];
|
||||
$item->metadata[CacheItem::METADATA_TAGS] = $value['tags'] ?? [];
|
||||
if (isset($value['meta'])) {
|
||||
// For compactness these values are packed, & expiry is offset to reduce size
|
||||
$v = \unpack('Ve/Nc', $value['meta']);
|
||||
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
|
||||
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
|
||||
}
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
self::$mergeByLifetime ?? (self::$mergeByLifetime = \Closure::bind(static function ($deferred, &$expiredIds, $getId, $tagPrefix, $defaultLifetime) {
|
||||
$byLifetime = [];
|
||||
$now = \microtime(\true);
|
||||
$expiredIds = [];
|
||||
foreach ($deferred as $key => $item) {
|
||||
$key = (string) $key;
|
||||
if (null === $item->expiry) {
|
||||
$ttl = 0 < $defaultLifetime ? $defaultLifetime : 0;
|
||||
} elseif (!$item->expiry) {
|
||||
$ttl = 0;
|
||||
} elseif (0 >= ($ttl = (int) (0.1 + $item->expiry - $now))) {
|
||||
$expiredIds[] = $getId($key);
|
||||
continue;
|
||||
}
|
||||
// Store Value and Tags on the cache value
|
||||
if (isset(($metadata = $item->newMetadata)[CacheItem::METADATA_TAGS])) {
|
||||
$value = ['value' => $item->value, 'tags' => $metadata[CacheItem::METADATA_TAGS]];
|
||||
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||
} else {
|
||||
$value = ['value' => $item->value, 'tags' => []];
|
||||
}
|
||||
if ($metadata) {
|
||||
// For compactness, expiry and creation duration are packed, using magic numbers as separators
|
||||
$value['meta'] = \pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]);
|
||||
}
|
||||
// Extract tag changes, these should be removed from values in doSave()
|
||||
$value['tag-operations'] = ['add' => [], 'remove' => []];
|
||||
$oldTags = $item->metadata[CacheItem::METADATA_TAGS] ?? [];
|
||||
foreach (\array_diff($value['tags'], $oldTags) as $addedTag) {
|
||||
$value['tag-operations']['add'][] = $getId($tagPrefix . $addedTag);
|
||||
}
|
||||
foreach (\array_diff($oldTags, $value['tags']) as $removedTag) {
|
||||
$value['tag-operations']['remove'][] = $getId($tagPrefix . $removedTag);
|
||||
}
|
||||
$byLifetime[$ttl][$getId($key)] = $value;
|
||||
$item->metadata = $item->newMetadata;
|
||||
}
|
||||
return $byLifetime;
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* Persists several cache items immediately.
|
||||
*
|
||||
* @param array $values The values to cache, indexed by their cache identifier
|
||||
* @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
|
||||
* @param array[] $addTagData Hash where key is tag id, and array value is list of cache id's to add to tag
|
||||
* @param array[] $removeTagData Hash where key is tag id, and array value is list of cache id's to remove to tag
|
||||
*
|
||||
* @return array The identifiers that failed to be cached or a boolean stating if caching succeeded or not
|
||||
*/
|
||||
protected abstract function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []) : array;
|
||||
/**
|
||||
* Removes multiple items from the pool and their corresponding tags.
|
||||
*
|
||||
* @param array $ids An array of identifiers that should be removed from the pool
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected abstract function doDelete(array $ids);
|
||||
/**
|
||||
* Removes relations between tags and deleted items.
|
||||
*
|
||||
* @param array $tagData Array of tag => key identifiers that should be removed from the pool
|
||||
*/
|
||||
protected abstract function doDeleteTagRelations(array $tagData) : bool;
|
||||
/**
|
||||
* Invalidates cached items using tags.
|
||||
*
|
||||
* @param string[] $tagIds An array of tags to invalidate, key is tag and value is tag id
|
||||
*/
|
||||
protected abstract function doInvalidate(array $tagIds) : bool;
|
||||
/**
|
||||
* Delete items and yields the tags they were bound to.
|
||||
*/
|
||||
protected function doDeleteYieldTags(array $ids) : iterable
|
||||
{
|
||||
foreach ($this->doFetch($ids) as $id => $value) {
|
||||
(yield $id => \is_array($value) && \is_array($value['tags'] ?? null) ? $value['tags'] : []);
|
||||
}
|
||||
$this->doDelete($ids);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function commit() : bool
|
||||
{
|
||||
$ok = \true;
|
||||
$byLifetime = (self::$mergeByLifetime)($this->deferred, $expiredIds, \Closure::fromCallable([$this, 'getId']), self::TAGS_PREFIX, $this->defaultLifetime);
|
||||
$retry = $this->deferred = [];
|
||||
if ($expiredIds) {
|
||||
// Tags are not cleaned up in this case, however that is done on invalidateTags().
|
||||
try {
|
||||
$this->doDelete($expiredIds);
|
||||
} catch (\Exception $e) {
|
||||
$ok = \false;
|
||||
CacheItem::log($this->logger, 'Failed to delete expired items: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
}
|
||||
foreach ($byLifetime as $lifetime => $values) {
|
||||
try {
|
||||
$values = $this->extractTagData($values, $addTagData, $removeTagData);
|
||||
$e = $this->doSave($values, $lifetime, $addTagData, $removeTagData);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (\true === $e || [] === $e) {
|
||||
continue;
|
||||
}
|
||||
if (\is_array($e) || 1 === \count($values)) {
|
||||
foreach (\is_array($e) ? $e : \array_keys($values) as $id) {
|
||||
$ok = \false;
|
||||
$v = $values[$id];
|
||||
$type = \get_debug_type($v);
|
||||
$message = \sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => \substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
} else {
|
||||
foreach ($values as $id => $v) {
|
||||
$retry[$lifetime][] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// When bulk-save failed, retry each item individually
|
||||
foreach ($retry as $lifetime => $ids) {
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$v = $byLifetime[$lifetime][$id];
|
||||
$values = $this->extractTagData([$id => $v], $addTagData, $removeTagData);
|
||||
$e = $this->doSave($values, $lifetime, $addTagData, $removeTagData);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (\true === $e || [] === $e) {
|
||||
continue;
|
||||
}
|
||||
$ok = \false;
|
||||
$type = \get_debug_type($v);
|
||||
$message = \sprintf('Failed to save key "{key}" of type %s%s', $type, $e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => \substr($id, \strlen($this->namespace)), 'exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteItems(array $keys) : bool
|
||||
{
|
||||
if (!$keys) {
|
||||
return \true;
|
||||
}
|
||||
$ok = \true;
|
||||
$ids = [];
|
||||
$tagData = [];
|
||||
foreach ($keys as $key) {
|
||||
$ids[$key] = $this->getId($key);
|
||||
unset($this->deferred[$key]);
|
||||
}
|
||||
try {
|
||||
foreach ($this->doDeleteYieldTags(\array_values($ids)) as $id => $tags) {
|
||||
foreach ($tags as $tag) {
|
||||
$tagData[$this->getId(self::TAGS_PREFIX . $tag)][] = $id;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$ok = \false;
|
||||
}
|
||||
try {
|
||||
if ((!$tagData || $this->doDeleteTagRelations($tagData)) && $ok) {
|
||||
return \true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
// When bulk-delete failed, retry each item individually
|
||||
foreach ($ids as $key => $id) {
|
||||
try {
|
||||
$e = null;
|
||||
if ($this->doDelete([$id])) {
|
||||
continue;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
$message = 'Failed to delete key "{key}"' . ($e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
$ok = \false;
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags)
|
||||
{
|
||||
if (empty($tags)) {
|
||||
return \false;
|
||||
}
|
||||
$tagIds = [];
|
||||
foreach (\array_unique($tags) as $tag) {
|
||||
$tagIds[] = $this->getId(self::TAGS_PREFIX . $tag);
|
||||
}
|
||||
try {
|
||||
if ($this->doInvalidate($tagIds)) {
|
||||
return \true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to invalidate tags: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* Extracts tags operation data from $values set in mergeByLifetime, and returns values without it.
|
||||
*/
|
||||
private function extractTagData(array $values, ?array &$addTagData, ?array &$removeTagData) : array
|
||||
{
|
||||
$addTagData = $removeTagData = [];
|
||||
foreach ($values as $id => $value) {
|
||||
foreach ($value['tag-operations']['add'] as $tag => $tagId) {
|
||||
$addTagData[$tagId][] = $id;
|
||||
}
|
||||
foreach ($value['tag-operations']['remove'] as $tag => $tagId) {
|
||||
$removeTagData[$tagId][] = $id;
|
||||
}
|
||||
unset($values[$id]['tag-operations']);
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
}
|
42
dependencies/symfony/cache/Adapter/AdapterInterface.php
vendored
Normal file
42
dependencies/symfony/cache/Adapter/AdapterInterface.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemPoolInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
\class_exists(CacheItem::class);
|
||||
/**
|
||||
* Interface for adapters managing instances of Symfony's CacheItem.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
interface AdapterInterface extends CacheItemPoolInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return CacheItem
|
||||
*/
|
||||
public function getItem($key);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return \Traversable<string, CacheItem>
|
||||
*/
|
||||
public function getItems(array $keys = []);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '');
|
||||
}
|
118
dependencies/symfony/cache/Adapter/ApcuAdapter.php
vendored
Normal file
118
dependencies/symfony/cache/Adapter/ApcuAdapter.php
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ApcuAdapter extends AbstractAdapter
|
||||
{
|
||||
private $marshaller;
|
||||
/**
|
||||
* @throws CacheException if APCu is not enabled
|
||||
*/
|
||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('APCu is not enabled.');
|
||||
}
|
||||
if ('cli' === \PHP_SAPI) {
|
||||
\ini_set('apc.use_request_time', 0);
|
||||
}
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
if (null !== $version) {
|
||||
CacheItem::validateKey($version);
|
||||
if (!\apcu_exists($version . '@' . $namespace)) {
|
||||
$this->doClear($namespace);
|
||||
\apcu_add($version . '@' . $namespace, null);
|
||||
}
|
||||
}
|
||||
$this->marshaller = $marshaller;
|
||||
}
|
||||
public static function isSupported()
|
||||
{
|
||||
return \function_exists('apcu_fetch') && \filter_var(\ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
$unserializeCallbackHandler = \ini_set('unserialize_callback_func', __CLASS__ . '::handleUnserializeCallback');
|
||||
try {
|
||||
$values = [];
|
||||
$ids = \array_flip($ids);
|
||||
foreach (\apcu_fetch(\array_keys($ids), $ok) ?: [] as $k => $v) {
|
||||
if (!isset($ids[$k])) {
|
||||
// work around https://github.com/krakjoe/apcu/issues/247
|
||||
$k = \key($ids);
|
||||
}
|
||||
unset($ids[$k]);
|
||||
if (null !== $v || $ok) {
|
||||
$values[$k] = null !== $this->marshaller ? $this->marshaller->unmarshall($v) : $v;
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
} catch (\Error $e) {
|
||||
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||
} finally {
|
||||
\ini_set('unserialize_callback_func', $unserializeCallbackHandler);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
return \apcu_exists($id);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
return isset($namespace[0]) && \class_exists(\APCUIterator::class, \false) && ('cli' !== \PHP_SAPI || \filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) ? \apcu_delete(new \APCUIterator(\sprintf('/^%s/', \preg_quote($namespace, '/')), \APC_ITER_KEY)) : \apcu_clear_cache();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
foreach ($ids as $id) {
|
||||
\apcu_delete($id);
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
if (null !== $this->marshaller && !($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
try {
|
||||
if (\false === ($failures = \apcu_store($values, null, $lifetime))) {
|
||||
$failures = $values;
|
||||
}
|
||||
return \array_keys($failures);
|
||||
} catch (\Throwable $e) {
|
||||
if (1 === \count($values)) {
|
||||
// Workaround https://github.com/krakjoe/apcu/issues/170
|
||||
\apcu_delete(\array_key_first($values));
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
346
dependencies/symfony/cache/Adapter/ArrayAdapter.php
vendored
Normal file
346
dependencies/symfony/cache/Adapter/ArrayAdapter.php
vendored
Normal file
@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* An in-memory cache storage.
|
||||
*
|
||||
* Acts as a least-recently-used (LRU) storage when configured with a maximum number of items.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
private $storeSerialized;
|
||||
private $values = [];
|
||||
private $expiries = [];
|
||||
private $defaultLifetime;
|
||||
private $maxLifetime;
|
||||
private $maxItems;
|
||||
private static $createCacheItem;
|
||||
/**
|
||||
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
|
||||
*/
|
||||
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = \true, float $maxLifetime = 0, int $maxItems = 0)
|
||||
{
|
||||
if (0 > $maxLifetime) {
|
||||
throw new InvalidArgumentException(\sprintf('Argument $maxLifetime must be positive, %F passed.', $maxLifetime));
|
||||
}
|
||||
if (0 > $maxItems) {
|
||||
throw new InvalidArgumentException(\sprintf('Argument $maxItems must be a positive integer, %d passed.', $maxItems));
|
||||
}
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
$this->storeSerialized = $storeSerialized;
|
||||
$this->maxLifetime = $maxLifetime;
|
||||
$this->maxItems = $maxItems;
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->value = $value;
|
||||
$item->isHit = $isHit;
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
$item = $this->getItem($key);
|
||||
$metadata = $item->getMetadata();
|
||||
// ArrayAdapter works in memory, we don't care about stampede protection
|
||||
if (\INF === $beta || !$item->isHit()) {
|
||||
$save = \true;
|
||||
$item->set($callback($item, $save));
|
||||
if ($save) {
|
||||
$this->save($item);
|
||||
}
|
||||
}
|
||||
return $item->get();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(string $key) : bool
|
||||
{
|
||||
return $this->deleteItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > \microtime(\true)) {
|
||||
if ($this->maxItems) {
|
||||
// Move the item last in the storage
|
||||
$value = $this->values[$key];
|
||||
unset($this->values[$key]);
|
||||
$this->values[$key] = $value;
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
\assert('' !== CacheItem::validateKey($key));
|
||||
return isset($this->expiries[$key]) && !$this->deleteItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
if (!($isHit = $this->hasItem($key))) {
|
||||
$value = null;
|
||||
if (!$this->maxItems) {
|
||||
// Track misses in non-LRU mode only
|
||||
$this->values[$key] = null;
|
||||
}
|
||||
} else {
|
||||
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
|
||||
}
|
||||
return (self::$createCacheItem)($key, $value, $isHit);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
\assert(self::validateKeys($keys));
|
||||
return $this->generateItems($keys, \microtime(\true), self::$createCacheItem);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
\assert('' !== CacheItem::validateKey($key));
|
||||
unset($this->values[$key], $this->expiries[$key]);
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
$this->deleteItem($key);
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
if (!$item instanceof CacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$item = (array) $item;
|
||||
$key = $item["\x00*\x00key"];
|
||||
$value = $item["\x00*\x00value"];
|
||||
$expiry = $item["\x00*\x00expiry"];
|
||||
$now = \microtime(\true);
|
||||
if (null !== $expiry) {
|
||||
if (!$expiry) {
|
||||
$expiry = \PHP_INT_MAX;
|
||||
} elseif ($expiry <= $now) {
|
||||
$this->deleteItem($key);
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
if ($this->storeSerialized && null === ($value = $this->freeze($value, $key))) {
|
||||
return \false;
|
||||
}
|
||||
if (null === $expiry && 0 < $this->defaultLifetime) {
|
||||
$expiry = $this->defaultLifetime;
|
||||
$expiry = $now + ($expiry > ($this->maxLifetime ?: $expiry) ? $this->maxLifetime : $expiry);
|
||||
} elseif ($this->maxLifetime && (null === $expiry || $expiry > $now + $this->maxLifetime)) {
|
||||
$expiry = $now + $this->maxLifetime;
|
||||
}
|
||||
if ($this->maxItems) {
|
||||
unset($this->values[$key]);
|
||||
// Iterate items and vacuum expired ones while we are at it
|
||||
foreach ($this->values as $k => $v) {
|
||||
if ($this->expiries[$k] > $now && \count($this->values) < $this->maxItems) {
|
||||
break;
|
||||
}
|
||||
unset($this->values[$k], $this->expiries[$k]);
|
||||
}
|
||||
}
|
||||
$this->values[$key] = $value;
|
||||
$this->expiries[$key] = $expiry ?? \PHP_INT_MAX;
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
return $this->save($item);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
if ('' !== $prefix) {
|
||||
$now = \microtime(\true);
|
||||
foreach ($this->values as $key => $value) {
|
||||
if (!isset($this->expiries[$key]) || $this->expiries[$key] <= $now || 0 === \strpos($key, $prefix)) {
|
||||
unset($this->values[$key], $this->expiries[$key]);
|
||||
}
|
||||
}
|
||||
if ($this->values) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
$this->values = $this->expiries = [];
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Returns all cached values, with cache miss as null.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getValues()
|
||||
{
|
||||
if (!$this->storeSerialized) {
|
||||
return $this->values;
|
||||
}
|
||||
$values = $this->values;
|
||||
foreach ($values as $k => $v) {
|
||||
if (null === $v || 'N;' === $v) {
|
||||
continue;
|
||||
}
|
||||
if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
|
||||
$values[$k] = \serialize($v);
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->clear();
|
||||
}
|
||||
private function generateItems(array $keys, float $now, \Closure $f) : \Generator
|
||||
{
|
||||
foreach ($keys as $i => $key) {
|
||||
if (!($isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key)))) {
|
||||
$value = null;
|
||||
if (!$this->maxItems) {
|
||||
// Track misses in non-LRU mode only
|
||||
$this->values[$key] = null;
|
||||
}
|
||||
} else {
|
||||
if ($this->maxItems) {
|
||||
// Move the item last in the storage
|
||||
$value = $this->values[$key];
|
||||
unset($this->values[$key]);
|
||||
$this->values[$key] = $value;
|
||||
}
|
||||
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
|
||||
}
|
||||
unset($keys[$i]);
|
||||
(yield $key => $f($key, $value, $isHit));
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
(yield $key => $f($key, null, \false));
|
||||
}
|
||||
}
|
||||
private function freeze($value, string $key)
|
||||
{
|
||||
if (null === $value) {
|
||||
return 'N;';
|
||||
}
|
||||
if (\is_string($value)) {
|
||||
// Serialize strings if they could be confused with serialized objects or arrays
|
||||
if ('N;' === $value || isset($value[2]) && ':' === $value[1]) {
|
||||
return \serialize($value);
|
||||
}
|
||||
} elseif (!\is_scalar($value)) {
|
||||
try {
|
||||
$serialized = \serialize($value);
|
||||
} catch (\Exception $e) {
|
||||
unset($this->values[$key]);
|
||||
$type = \get_debug_type($value);
|
||||
$message = \sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage());
|
||||
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
return;
|
||||
}
|
||||
// Keep value serialized if it contains any objects or any internal references
|
||||
if ('C' === $serialized[0] || 'O' === $serialized[0] || \preg_match('/;[OCRr]:[1-9]/', $serialized)) {
|
||||
return $serialized;
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
private function unfreeze(string $key, bool &$isHit)
|
||||
{
|
||||
if ('N;' === ($value = $this->values[$key])) {
|
||||
return null;
|
||||
}
|
||||
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
|
||||
try {
|
||||
$value = \unserialize($value);
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to unserialize key "{key}": ' . $e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
$value = \false;
|
||||
}
|
||||
if (\false === $value) {
|
||||
$value = null;
|
||||
$isHit = \false;
|
||||
if (!$this->maxItems) {
|
||||
$this->values[$key] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
private function validateKeys(array $keys) : bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key) || !isset($this->expiries[$key])) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
}
|
287
dependencies/symfony/cache/Adapter/ChainAdapter.php
vendored
Normal file
287
dependencies/symfony/cache/Adapter/ChainAdapter.php
vendored
Normal file
@ -0,0 +1,287 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemPoolInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Service\ResetInterface;
|
||||
/**
|
||||
* Chains several adapters together.
|
||||
*
|
||||
* Cached items are fetched from the first adapter having them in its data store.
|
||||
* They are saved and deleted in all adapters at once.
|
||||
*
|
||||
* @author Kévin Dunglas <dunglas@gmail.com>
|
||||
*/
|
||||
class ChainAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use ContractsTrait;
|
||||
private $adapters = [];
|
||||
private $adapterCount;
|
||||
private $defaultLifetime;
|
||||
private static $syncItem;
|
||||
/**
|
||||
* @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items
|
||||
* @param int $defaultLifetime The default lifetime of items propagated from lower adapters to upper ones
|
||||
*/
|
||||
public function __construct(array $adapters, int $defaultLifetime = 0)
|
||||
{
|
||||
if (!$adapters) {
|
||||
throw new InvalidArgumentException('At least one adapter must be specified.');
|
||||
}
|
||||
foreach ($adapters as $adapter) {
|
||||
if (!$adapter instanceof CacheItemPoolInterface) {
|
||||
throw new InvalidArgumentException(\sprintf('The class "%s" does not implement the "%s" interface.', \get_debug_type($adapter), CacheItemPoolInterface::class));
|
||||
}
|
||||
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true) && $adapter instanceof ApcuAdapter && !\filter_var(\ini_get('apc.enable_cli'), \FILTER_VALIDATE_BOOLEAN)) {
|
||||
continue;
|
||||
// skip putting APCu in the chain when the backend is disabled
|
||||
}
|
||||
if ($adapter instanceof AdapterInterface) {
|
||||
$this->adapters[] = $adapter;
|
||||
} else {
|
||||
$this->adapters[] = new ProxyAdapter($adapter);
|
||||
}
|
||||
}
|
||||
$this->adapterCount = \count($this->adapters);
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
self::$syncItem ?? (self::$syncItem = \Closure::bind(static function ($sourceItem, $item, $defaultLifetime, $sourceMetadata = null) {
|
||||
$sourceItem->isTaggable = \false;
|
||||
$sourceMetadata = $sourceMetadata ?? $sourceItem->metadata;
|
||||
unset($sourceMetadata[CacheItem::METADATA_TAGS]);
|
||||
$item->value = $sourceItem->value;
|
||||
$item->isHit = $sourceItem->isHit;
|
||||
$item->metadata = $item->newMetadata = $sourceItem->metadata = $sourceMetadata;
|
||||
if (isset($item->metadata[CacheItem::METADATA_EXPIRY])) {
|
||||
$item->expiresAt(\DateTime::createFromFormat('U.u', \sprintf('%.6F', $item->metadata[CacheItem::METADATA_EXPIRY])));
|
||||
} elseif (0 < $defaultLifetime) {
|
||||
$item->expiresAfter($defaultLifetime);
|
||||
}
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
$doSave = \true;
|
||||
$callback = static function (CacheItem $item, bool &$save) use($callback, &$doSave) {
|
||||
$value = $callback($item, $save);
|
||||
$doSave = $save;
|
||||
return $value;
|
||||
};
|
||||
$lastItem = null;
|
||||
$i = 0;
|
||||
$wrap = function (CacheItem $item = null, bool &$save = \true) use($key, $callback, $beta, &$wrap, &$i, &$doSave, &$lastItem, &$metadata) {
|
||||
$adapter = $this->adapters[$i];
|
||||
if (isset($this->adapters[++$i])) {
|
||||
$callback = $wrap;
|
||||
$beta = \INF === $beta ? \INF : 0;
|
||||
}
|
||||
if ($adapter instanceof CacheInterface) {
|
||||
$value = $adapter->get($key, $callback, $beta, $metadata);
|
||||
} else {
|
||||
$value = $this->doGet($adapter, $key, $callback, $beta, $metadata);
|
||||
}
|
||||
if (null !== $item) {
|
||||
(self::$syncItem)($lastItem = $lastItem ?? $item, $item, $this->defaultLifetime, $metadata);
|
||||
}
|
||||
$save = $doSave;
|
||||
return $value;
|
||||
};
|
||||
return $wrap();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$syncItem = self::$syncItem;
|
||||
$misses = [];
|
||||
foreach ($this->adapters as $i => $adapter) {
|
||||
$item = $adapter->getItem($key);
|
||||
if ($item->isHit()) {
|
||||
while (0 <= --$i) {
|
||||
$this->adapters[$i]->save($syncItem($item, $misses[$i], $this->defaultLifetime));
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
$misses[$i] = $item;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
return $this->generateItems($this->adapters[0]->getItems($keys), 0);
|
||||
}
|
||||
private function generateItems(iterable $items, int $adapterIndex) : \Generator
|
||||
{
|
||||
$missing = [];
|
||||
$misses = [];
|
||||
$nextAdapterIndex = $adapterIndex + 1;
|
||||
$nextAdapter = $this->adapters[$nextAdapterIndex] ?? null;
|
||||
foreach ($items as $k => $item) {
|
||||
if (!$nextAdapter || $item->isHit()) {
|
||||
(yield $k => $item);
|
||||
} else {
|
||||
$missing[] = $k;
|
||||
$misses[$k] = $item;
|
||||
}
|
||||
}
|
||||
if ($missing) {
|
||||
$syncItem = self::$syncItem;
|
||||
$adapter = $this->adapters[$adapterIndex];
|
||||
$items = $this->generateItems($nextAdapter->getItems($missing), $nextAdapterIndex);
|
||||
foreach ($items as $k => $item) {
|
||||
if ($item->isHit()) {
|
||||
$adapter->save($syncItem($item, $misses[$k], $this->defaultLifetime));
|
||||
}
|
||||
(yield $k => $item);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
foreach ($this->adapters as $adapter) {
|
||||
if ($adapter->hasItem($key)) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
$cleared = \true;
|
||||
$i = $this->adapterCount;
|
||||
while ($i--) {
|
||||
if ($this->adapters[$i] instanceof AdapterInterface) {
|
||||
$cleared = $this->adapters[$i]->clear($prefix) && $cleared;
|
||||
} else {
|
||||
$cleared = $this->adapters[$i]->clear() && $cleared;
|
||||
}
|
||||
}
|
||||
return $cleared;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
$deleted = \true;
|
||||
$i = $this->adapterCount;
|
||||
while ($i--) {
|
||||
$deleted = $this->adapters[$i]->deleteItem($key) && $deleted;
|
||||
}
|
||||
return $deleted;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
$deleted = \true;
|
||||
$i = $this->adapterCount;
|
||||
while ($i--) {
|
||||
$deleted = $this->adapters[$i]->deleteItems($keys) && $deleted;
|
||||
}
|
||||
return $deleted;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
$saved = \true;
|
||||
$i = $this->adapterCount;
|
||||
while ($i--) {
|
||||
$saved = $this->adapters[$i]->save($item) && $saved;
|
||||
}
|
||||
return $saved;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
$saved = \true;
|
||||
$i = $this->adapterCount;
|
||||
while ($i--) {
|
||||
$saved = $this->adapters[$i]->saveDeferred($item) && $saved;
|
||||
}
|
||||
return $saved;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$committed = \true;
|
||||
$i = $this->adapterCount;
|
||||
while ($i--) {
|
||||
$committed = $this->adapters[$i]->commit() && $committed;
|
||||
}
|
||||
return $committed;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
$pruned = \true;
|
||||
foreach ($this->adapters as $adapter) {
|
||||
if ($adapter instanceof PruneableInterface) {
|
||||
$pruned = $adapter->prune() && $pruned;
|
||||
}
|
||||
}
|
||||
return $pruned;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
foreach ($this->adapters as $adapter) {
|
||||
if ($adapter instanceof ResetInterface) {
|
||||
$adapter->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
196
dependencies/symfony/cache/Adapter/CouchbaseBucketAdapter.php
vendored
Normal file
196
dependencies/symfony/cache/Adapter/CouchbaseBucketAdapter.php
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
/**
|
||||
* @author Antonio Jose Cerezo Aranda <aj.cerezo@gmail.com>
|
||||
*/
|
||||
class CouchbaseBucketAdapter extends AbstractAdapter
|
||||
{
|
||||
private const THIRTY_DAYS_IN_SECONDS = 2592000;
|
||||
private const MAX_KEY_LENGTH = 250;
|
||||
private const KEY_NOT_FOUND = 13;
|
||||
private const VALID_DSN_OPTIONS = ['operationTimeout', 'configTimeout', 'configNodeTimeout', 'n1qlTimeout', 'httpTimeout', 'configDelay', 'htconfigIdleTimeout', 'durabilityInterval', 'durabilityTimeout'];
|
||||
private $bucket;
|
||||
private $marshaller;
|
||||
public function __construct(\WP_Ultimo\Dependencies\CouchbaseBucket $bucket, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.');
|
||||
}
|
||||
$this->maxIdLength = static::MAX_KEY_LENGTH;
|
||||
$this->bucket = $bucket;
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
$this->enableVersioning();
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
}
|
||||
/**
|
||||
* @param array|string $servers
|
||||
*/
|
||||
public static function createConnection($servers, array $options = []) : \WP_Ultimo\Dependencies\CouchbaseBucket
|
||||
{
|
||||
if (\is_string($servers)) {
|
||||
$servers = [$servers];
|
||||
} elseif (!\is_array($servers)) {
|
||||
throw new \TypeError(\sprintf('Argument 1 passed to "%s()" must be array or string, "%s" given.', __METHOD__, \get_debug_type($servers)));
|
||||
}
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('Couchbase >= 2.6.0 < 3.0.0 is required.');
|
||||
}
|
||||
\set_error_handler(function ($type, $msg, $file, $line) {
|
||||
throw new \ErrorException($msg, 0, $type, $file, $line);
|
||||
});
|
||||
$dsnPattern = '/^(?<protocol>couchbase(?:s)?)\\:\\/\\/(?:(?<username>[^\\:]+)\\:(?<password>[^\\@]{6,})@)?' . '(?<host>[^\\:]+(?:\\:\\d+)?)(?:\\/(?<bucketName>[^\\?]+))(?:\\?(?<options>.*))?$/i';
|
||||
$newServers = [];
|
||||
$protocol = 'couchbase';
|
||||
try {
|
||||
$options = self::initOptions($options);
|
||||
$username = $options['username'];
|
||||
$password = $options['password'];
|
||||
foreach ($servers as $dsn) {
|
||||
if (0 !== \strpos($dsn, 'couchbase:')) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Couchbase DSN: "%s" does not start with "couchbase:".', $dsn));
|
||||
}
|
||||
\preg_match($dsnPattern, $dsn, $matches);
|
||||
$username = $matches['username'] ?: $username;
|
||||
$password = $matches['password'] ?: $password;
|
||||
$protocol = $matches['protocol'] ?: $protocol;
|
||||
if (isset($matches['options'])) {
|
||||
$optionsInDsn = self::getOptions($matches['options']);
|
||||
foreach ($optionsInDsn as $parameter => $value) {
|
||||
$options[$parameter] = $value;
|
||||
}
|
||||
}
|
||||
$newServers[] = $matches['host'];
|
||||
}
|
||||
$connectionString = $protocol . '://' . \implode(',', $newServers);
|
||||
$client = new \WP_Ultimo\Dependencies\CouchbaseCluster($connectionString);
|
||||
$client->authenticateAs($username, $password);
|
||||
$bucket = $client->openBucket($matches['bucketName']);
|
||||
unset($options['username'], $options['password']);
|
||||
foreach ($options as $option => $value) {
|
||||
if (!empty($value)) {
|
||||
$bucket->{$option} = $value;
|
||||
}
|
||||
}
|
||||
return $bucket;
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
}
|
||||
public static function isSupported() : bool
|
||||
{
|
||||
return \extension_loaded('couchbase') && \version_compare(\phpversion('couchbase'), '2.6.0', '>=') && \version_compare(\phpversion('couchbase'), '3.0', '<');
|
||||
}
|
||||
private static function getOptions(string $options) : array
|
||||
{
|
||||
$results = [];
|
||||
$optionsInArray = \explode('&', $options);
|
||||
foreach ($optionsInArray as $option) {
|
||||
[$key, $value] = \explode('=', $option);
|
||||
if (\in_array($key, static::VALID_DSN_OPTIONS, \true)) {
|
||||
$results[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
private static function initOptions(array $options) : array
|
||||
{
|
||||
$options['username'] = $options['username'] ?? '';
|
||||
$options['password'] = $options['password'] ?? '';
|
||||
$options['operationTimeout'] = $options['operationTimeout'] ?? 0;
|
||||
$options['configTimeout'] = $options['configTimeout'] ?? 0;
|
||||
$options['configNodeTimeout'] = $options['configNodeTimeout'] ?? 0;
|
||||
$options['n1qlTimeout'] = $options['n1qlTimeout'] ?? 0;
|
||||
$options['httpTimeout'] = $options['httpTimeout'] ?? 0;
|
||||
$options['configDelay'] = $options['configDelay'] ?? 0;
|
||||
$options['htconfigIdleTimeout'] = $options['htconfigIdleTimeout'] ?? 0;
|
||||
$options['durabilityInterval'] = $options['durabilityInterval'] ?? 0;
|
||||
$options['durabilityTimeout'] = $options['durabilityTimeout'] ?? 0;
|
||||
return $options;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
$resultsCouchbase = $this->bucket->get($ids);
|
||||
$results = [];
|
||||
foreach ($resultsCouchbase as $key => $value) {
|
||||
if (null !== $value->error) {
|
||||
continue;
|
||||
}
|
||||
$results[$key] = $this->marshaller->unmarshall($value->value);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id) : bool
|
||||
{
|
||||
return \false !== $this->bucket->get($id);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace) : bool
|
||||
{
|
||||
if ('' === $namespace) {
|
||||
$this->bucket->manager()->flush();
|
||||
return \true;
|
||||
}
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids) : bool
|
||||
{
|
||||
$results = $this->bucket->remove(\array_values($ids));
|
||||
foreach ($results as $key => $result) {
|
||||
if (null !== $result->error && static::KEY_NOT_FOUND !== $result->error->getCode()) {
|
||||
continue;
|
||||
}
|
||||
unset($results[$key]);
|
||||
}
|
||||
return 0 === \count($results);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
if (!($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
$lifetime = $this->normalizeExpiry($lifetime);
|
||||
$ko = [];
|
||||
foreach ($values as $key => $value) {
|
||||
$result = $this->bucket->upsert($key, $value, ['expiry' => $lifetime]);
|
||||
if (null !== $result->error) {
|
||||
$ko[$key] = $result;
|
||||
}
|
||||
}
|
||||
return [] === $ko ? \true : $ko;
|
||||
}
|
||||
private function normalizeExpiry(int $expiry) : int
|
||||
{
|
||||
if ($expiry && $expiry > static::THIRTY_DAYS_IN_SECONDS) {
|
||||
$expiry += \time();
|
||||
}
|
||||
return $expiry;
|
||||
}
|
||||
}
|
181
dependencies/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
vendored
Normal file
181
dependencies/symfony/cache/Adapter/CouchbaseCollectionAdapter.php
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Couchbase\Bucket;
|
||||
use Couchbase\Cluster;
|
||||
use Couchbase\ClusterOptions;
|
||||
use Couchbase\Collection;
|
||||
use Couchbase\DocumentNotFoundException;
|
||||
use Couchbase\UpsertOptions;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
/**
|
||||
* @author Antonio Jose Cerezo Aranda <aj.cerezo@gmail.com>
|
||||
*/
|
||||
class CouchbaseCollectionAdapter extends AbstractAdapter
|
||||
{
|
||||
private const MAX_KEY_LENGTH = 250;
|
||||
/** @var Collection */
|
||||
private $connection;
|
||||
private $marshaller;
|
||||
public function __construct(Collection $connection, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.');
|
||||
}
|
||||
$this->maxIdLength = static::MAX_KEY_LENGTH;
|
||||
$this->connection = $connection;
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
$this->enableVersioning();
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
}
|
||||
/**
|
||||
* @param array|string $dsn
|
||||
*
|
||||
* @return Bucket|Collection
|
||||
*/
|
||||
public static function createConnection($dsn, array $options = [])
|
||||
{
|
||||
if (\is_string($dsn)) {
|
||||
$dsn = [$dsn];
|
||||
} elseif (!\is_array($dsn)) {
|
||||
throw new \TypeError(\sprintf('Argument 1 passed to "%s()" must be array or string, "%s" given.', __METHOD__, \get_debug_type($dsn)));
|
||||
}
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('Couchbase >= 3.0.0 < 4.0.0 is required.');
|
||||
}
|
||||
\set_error_handler(function ($type, $msg, $file, $line) : bool {
|
||||
throw new \ErrorException($msg, 0, $type, $file, $line);
|
||||
});
|
||||
$dsnPattern = '/^(?<protocol>couchbase(?:s)?)\\:\\/\\/(?:(?<username>[^\\:]+)\\:(?<password>[^\\@]{6,})@)?' . '(?<host>[^\\:]+(?:\\:\\d+)?)(?:\\/(?<bucketName>[^\\/\\?]+))(?:(?:\\/(?<scopeName>[^\\/]+))' . '(?:\\/(?<collectionName>[^\\/\\?]+)))?(?:\\/)?(?:\\?(?<options>.*))?$/i';
|
||||
$newServers = [];
|
||||
$protocol = 'couchbase';
|
||||
try {
|
||||
$username = $options['username'] ?? '';
|
||||
$password = $options['password'] ?? '';
|
||||
foreach ($dsn as $server) {
|
||||
if (0 !== \strpos($server, 'couchbase:')) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Couchbase DSN: "%s" does not start with "couchbase:".', $server));
|
||||
}
|
||||
\preg_match($dsnPattern, $server, $matches);
|
||||
$username = $matches['username'] ?: $username;
|
||||
$password = $matches['password'] ?: $password;
|
||||
$protocol = $matches['protocol'] ?: $protocol;
|
||||
if (isset($matches['options'])) {
|
||||
$optionsInDsn = self::getOptions($matches['options']);
|
||||
foreach ($optionsInDsn as $parameter => $value) {
|
||||
$options[$parameter] = $value;
|
||||
}
|
||||
}
|
||||
$newServers[] = $matches['host'];
|
||||
}
|
||||
$option = isset($matches['options']) ? '?' . $matches['options'] : '';
|
||||
$connectionString = $protocol . '://' . \implode(',', $newServers) . $option;
|
||||
$clusterOptions = new ClusterOptions();
|
||||
$clusterOptions->credentials($username, $password);
|
||||
$client = new Cluster($connectionString, $clusterOptions);
|
||||
$bucket = $client->bucket($matches['bucketName']);
|
||||
$collection = $bucket->defaultCollection();
|
||||
if (!empty($matches['scopeName'])) {
|
||||
$scope = $bucket->scope($matches['scopeName']);
|
||||
$collection = $scope->collection($matches['collectionName']);
|
||||
}
|
||||
return $collection;
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
}
|
||||
public static function isSupported() : bool
|
||||
{
|
||||
return \extension_loaded('couchbase') && \version_compare(\phpversion('couchbase'), '3.0.5', '>=') && \version_compare(\phpversion('couchbase'), '4.0', '<');
|
||||
}
|
||||
private static function getOptions(string $options) : array
|
||||
{
|
||||
$results = [];
|
||||
$optionsInArray = \explode('&', $options);
|
||||
foreach ($optionsInArray as $option) {
|
||||
[$key, $value] = \explode('=', $option);
|
||||
$results[$key] = $value;
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids) : array
|
||||
{
|
||||
$results = [];
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$resultCouchbase = $this->connection->get($id);
|
||||
} catch (DocumentNotFoundException $exception) {
|
||||
continue;
|
||||
}
|
||||
$content = $resultCouchbase->value ?? $resultCouchbase->content();
|
||||
$results[$id] = $this->marshaller->unmarshall($content);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave($id) : bool
|
||||
{
|
||||
return $this->connection->exists($id)->exists();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($namespace) : bool
|
||||
{
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids) : bool
|
||||
{
|
||||
$idsErrors = [];
|
||||
foreach ($ids as $id) {
|
||||
try {
|
||||
$result = $this->connection->remove($id);
|
||||
if (null === $result->mutationToken()) {
|
||||
$idsErrors[] = $id;
|
||||
}
|
||||
} catch (DocumentNotFoundException $exception) {
|
||||
}
|
||||
}
|
||||
return 0 === \count($idsErrors);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, $lifetime)
|
||||
{
|
||||
if (!($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
$upsertOptions = new UpsertOptions();
|
||||
$upsertOptions->expiry($lifetime);
|
||||
$ko = [];
|
||||
foreach ($values as $key => $value) {
|
||||
try {
|
||||
$this->connection->upsert($key, $value, $upsertOptions);
|
||||
} catch (\Exception $exception) {
|
||||
$ko[$key] = '';
|
||||
}
|
||||
}
|
||||
return [] === $ko ? \true : $ko;
|
||||
}
|
||||
}
|
94
dependencies/symfony/cache/Adapter/DoctrineAdapter.php
vendored
Normal file
94
dependencies/symfony/cache/Adapter/DoctrineAdapter.php
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Doctrine\Common\Cache\CacheProvider;
|
||||
use WP_Ultimo\Dependencies\Doctrine\Common\Cache\Psr6\CacheAdapter;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated Since Symfony 5.4, use Doctrine\Common\Cache\Psr6\CacheAdapter instead
|
||||
*/
|
||||
class DoctrineAdapter extends AbstractAdapter
|
||||
{
|
||||
private $provider;
|
||||
public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
trigger_deprecation('symfony/cache', '5.4', '"%s" is deprecated, use "%s" instead.', __CLASS__, CacheAdapter::class);
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->provider = $provider;
|
||||
$provider->setNamespace($namespace);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
$this->provider->setNamespace($this->provider->getNamespace());
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
$unserializeCallbackHandler = \ini_set('unserialize_callback_func', parent::class . '::handleUnserializeCallback');
|
||||
try {
|
||||
return $this->provider->fetchMultiple($ids);
|
||||
} catch (\Error $e) {
|
||||
$trace = $e->getTrace();
|
||||
if (isset($trace[0]['function']) && !isset($trace[0]['class'])) {
|
||||
switch ($trace[0]['function']) {
|
||||
case 'unserialize':
|
||||
case 'apcu_fetch':
|
||||
case 'apc_fetch':
|
||||
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||
}
|
||||
}
|
||||
throw $e;
|
||||
} finally {
|
||||
\ini_set('unserialize_callback_func', $unserializeCallbackHandler);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
return $this->provider->contains($id);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
$namespace = $this->provider->getNamespace();
|
||||
return isset($namespace[0]) ? $this->provider->deleteAll() : $this->provider->flushAll();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
$ok = \true;
|
||||
foreach ($ids as $id) {
|
||||
$ok = $this->provider->delete($id) && $ok;
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
return $this->provider->saveMultiple($values, $lifetime);
|
||||
}
|
||||
}
|
356
dependencies/symfony/cache/Adapter/DoctrineDbalAdapter.php
vendored
Normal file
356
dependencies/symfony/cache/Adapter/DoctrineDbalAdapter.php
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\ArrayParameterType;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Configuration;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Connection;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\DriverManager;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Exception as DBALException;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Exception\TableNotFoundException;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\ParameterType;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Schema\DefaultSchemaManagerFactory;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Schema\Schema;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Tools\DsnParser;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
class DoctrineDbalAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
protected $maxIdLength = 255;
|
||||
private $marshaller;
|
||||
private $conn;
|
||||
private $platformName;
|
||||
private $serverVersion;
|
||||
private $table = 'cache_items';
|
||||
private $idCol = 'item_id';
|
||||
private $dataCol = 'item_data';
|
||||
private $lifetimeCol = 'item_lifetime';
|
||||
private $timeCol = 'item_time';
|
||||
private $namespace;
|
||||
/**
|
||||
* You can either pass an existing database Doctrine DBAL Connection or
|
||||
* a DSN string that will be used to connect to the database.
|
||||
*
|
||||
* The cache table is created automatically when possible.
|
||||
* Otherwise, use the createTable() method.
|
||||
*
|
||||
* List of available options:
|
||||
* * db_table: The name of the table [default: cache_items]
|
||||
* * db_id_col: The column where to store the cache id [default: item_id]
|
||||
* * db_data_col: The column where to store the cache data [default: item_data]
|
||||
* * db_lifetime_col: The column where to store the lifetime [default: item_lifetime]
|
||||
* * db_time_col: The column where to store the timestamp [default: item_time]
|
||||
*
|
||||
* @param Connection|string $connOrDsn
|
||||
*
|
||||
* @throws InvalidArgumentException When namespace contains invalid characters
|
||||
*/
|
||||
public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if (isset($namespace[0]) && \preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
|
||||
throw new InvalidArgumentException(\sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
|
||||
}
|
||||
if ($connOrDsn instanceof Connection) {
|
||||
$this->conn = $connOrDsn;
|
||||
} elseif (\is_string($connOrDsn)) {
|
||||
if (!\class_exists(DriverManager::class)) {
|
||||
throw new InvalidArgumentException(\sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $connOrDsn));
|
||||
}
|
||||
if (\class_exists(DsnParser::class)) {
|
||||
$params = (new DsnParser(['db2' => 'ibm_db2', 'mssql' => 'pdo_sqlsrv', 'mysql' => 'pdo_mysql', 'mysql2' => 'pdo_mysql', 'postgres' => 'pdo_pgsql', 'postgresql' => 'pdo_pgsql', 'pgsql' => 'pdo_pgsql', 'sqlite' => 'pdo_sqlite', 'sqlite3' => 'pdo_sqlite']))->parse($connOrDsn);
|
||||
} else {
|
||||
$params = ['url' => $connOrDsn];
|
||||
}
|
||||
$config = new Configuration();
|
||||
if (\class_exists(DefaultSchemaManagerFactory::class)) {
|
||||
$config->setSchemaManagerFactory(new DefaultSchemaManagerFactory());
|
||||
}
|
||||
$this->conn = DriverManager::getConnection($params, $config);
|
||||
} else {
|
||||
throw new \TypeError(\sprintf('Argument 1 passed to "%s()" must be "%s" or string, "%s" given.', __METHOD__, Connection::class, \get_debug_type($connOrDsn)));
|
||||
}
|
||||
$this->table = $options['db_table'] ?? $this->table;
|
||||
$this->idCol = $options['db_id_col'] ?? $this->idCol;
|
||||
$this->dataCol = $options['db_data_col'] ?? $this->dataCol;
|
||||
$this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol;
|
||||
$this->timeCol = $options['db_time_col'] ?? $this->timeCol;
|
||||
$this->namespace = $namespace;
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
}
|
||||
/**
|
||||
* Creates the table to store cache items which can be called once for setup.
|
||||
*
|
||||
* Cache ID are saved in a column of maximum length 255. Cache data is
|
||||
* saved in a BLOB.
|
||||
*
|
||||
* @throws DBALException When the table already exists
|
||||
*/
|
||||
public function createTable()
|
||||
{
|
||||
$schema = new Schema();
|
||||
$this->addTableToSchema($schema);
|
||||
foreach ($schema->toSql($this->conn->getDatabasePlatform()) as $sql) {
|
||||
$this->conn->executeStatement($sql);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function configureSchema(Schema $schema, Connection $forConnection) : void
|
||||
{
|
||||
// only update the schema for this connection
|
||||
if ($forConnection !== $this->conn) {
|
||||
return;
|
||||
}
|
||||
if ($schema->hasTable($this->table)) {
|
||||
return;
|
||||
}
|
||||
$this->addTableToSchema($schema);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune() : bool
|
||||
{
|
||||
$deleteSql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= ?";
|
||||
$params = [\time()];
|
||||
$paramTypes = [ParameterType::INTEGER];
|
||||
if ('' !== $this->namespace) {
|
||||
$deleteSql .= " AND {$this->idCol} LIKE ?";
|
||||
$params[] = \sprintf('%s%%', $this->namespace);
|
||||
$paramTypes[] = ParameterType::STRING;
|
||||
}
|
||||
try {
|
||||
$this->conn->executeStatement($deleteSql, $params, $paramTypes);
|
||||
} catch (TableNotFoundException $e) {
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids) : iterable
|
||||
{
|
||||
$now = \time();
|
||||
$expired = [];
|
||||
$sql = "SELECT {$this->idCol}, CASE WHEN {$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > ? THEN {$this->dataCol} ELSE NULL END FROM {$this->table} WHERE {$this->idCol} IN (?)";
|
||||
$result = $this->conn->executeQuery($sql, [$now, $ids], [ParameterType::INTEGER, \class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY])->iterateNumeric();
|
||||
foreach ($result as $row) {
|
||||
if (null === $row[1]) {
|
||||
$expired[] = $row[0];
|
||||
} else {
|
||||
(yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? \stream_get_contents($row[1]) : $row[1]));
|
||||
}
|
||||
}
|
||||
if ($expired) {
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= ? AND {$this->idCol} IN (?)";
|
||||
$this->conn->executeStatement($sql, [$now, $expired], [ParameterType::INTEGER, \class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id) : bool
|
||||
{
|
||||
$sql = "SELECT 1 FROM {$this->table} WHERE {$this->idCol} = ? AND ({$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > ?)";
|
||||
$result = $this->conn->executeQuery($sql, [$id, \time()], [ParameterType::STRING, ParameterType::INTEGER]);
|
||||
return (bool) $result->fetchOne();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace) : bool
|
||||
{
|
||||
if ('' === $namespace) {
|
||||
if ('sqlite' === $this->getPlatformName()) {
|
||||
$sql = "DELETE FROM {$this->table}";
|
||||
} else {
|
||||
$sql = "TRUNCATE TABLE {$this->table}";
|
||||
}
|
||||
} else {
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->idCol} LIKE '{$namespace}%'";
|
||||
}
|
||||
try {
|
||||
$this->conn->executeStatement($sql);
|
||||
} catch (TableNotFoundException $e) {
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids) : bool
|
||||
{
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->idCol} IN (?)";
|
||||
try {
|
||||
$this->conn->executeStatement($sql, [\array_values($ids)], [\class_exists(ArrayParameterType::class) ? ArrayParameterType::STRING : Connection::PARAM_STR_ARRAY]);
|
||||
} catch (TableNotFoundException $e) {
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
if (!($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
$platformName = $this->getPlatformName();
|
||||
$insertSql = "INSERT INTO {$this->table} ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?)";
|
||||
switch (\true) {
|
||||
case 'mysql' === $platformName:
|
||||
$sql = $insertSql . " ON DUPLICATE KEY UPDATE {$this->dataCol} = VALUES({$this->dataCol}), {$this->lifetimeCol} = VALUES({$this->lifetimeCol}), {$this->timeCol} = VALUES({$this->timeCol})";
|
||||
break;
|
||||
case 'oci' === $platformName:
|
||||
// DUAL is Oracle specific dummy table
|
||||
$sql = "MERGE INTO {$this->table} USING DUAL ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?";
|
||||
break;
|
||||
case 'sqlsrv' === $platformName && \version_compare($this->getServerVersion(), '10', '>='):
|
||||
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
|
||||
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||
$sql = "MERGE INTO {$this->table} WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?;";
|
||||
break;
|
||||
case 'sqlite' === $platformName:
|
||||
$sql = 'INSERT OR REPLACE' . \substr($insertSql, 6);
|
||||
break;
|
||||
case 'pgsql' === $platformName && \version_compare($this->getServerVersion(), '9.5', '>='):
|
||||
$sql = $insertSql . " ON CONFLICT ({$this->idCol}) DO UPDATE SET ({$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) = (EXCLUDED.{$this->dataCol}, EXCLUDED.{$this->lifetimeCol}, EXCLUDED.{$this->timeCol})";
|
||||
break;
|
||||
default:
|
||||
$platformName = null;
|
||||
$sql = "UPDATE {$this->table} SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ? WHERE {$this->idCol} = ?";
|
||||
break;
|
||||
}
|
||||
$now = \time();
|
||||
$lifetime = $lifetime ?: null;
|
||||
try {
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
} catch (TableNotFoundException $e) {
|
||||
if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], \true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
}
|
||||
if ('sqlsrv' === $platformName || 'oci' === $platformName) {
|
||||
$bind = static function ($id, $data) use($stmt) {
|
||||
$stmt->bindValue(1, $id);
|
||||
$stmt->bindValue(2, $id);
|
||||
$stmt->bindValue(3, $data, ParameterType::LARGE_OBJECT);
|
||||
$stmt->bindValue(6, $data, ParameterType::LARGE_OBJECT);
|
||||
};
|
||||
$stmt->bindValue(4, $lifetime, ParameterType::INTEGER);
|
||||
$stmt->bindValue(5, $now, ParameterType::INTEGER);
|
||||
$stmt->bindValue(7, $lifetime, ParameterType::INTEGER);
|
||||
$stmt->bindValue(8, $now, ParameterType::INTEGER);
|
||||
} elseif (null !== $platformName) {
|
||||
$bind = static function ($id, $data) use($stmt) {
|
||||
$stmt->bindValue(1, $id);
|
||||
$stmt->bindValue(2, $data, ParameterType::LARGE_OBJECT);
|
||||
};
|
||||
$stmt->bindValue(3, $lifetime, ParameterType::INTEGER);
|
||||
$stmt->bindValue(4, $now, ParameterType::INTEGER);
|
||||
} else {
|
||||
$stmt->bindValue(2, $lifetime, ParameterType::INTEGER);
|
||||
$stmt->bindValue(3, $now, ParameterType::INTEGER);
|
||||
$insertStmt = $this->conn->prepare($insertSql);
|
||||
$insertStmt->bindValue(3, $lifetime, ParameterType::INTEGER);
|
||||
$insertStmt->bindValue(4, $now, ParameterType::INTEGER);
|
||||
$bind = static function ($id, $data) use($stmt, $insertStmt) {
|
||||
$stmt->bindValue(1, $data, ParameterType::LARGE_OBJECT);
|
||||
$stmt->bindValue(4, $id);
|
||||
$insertStmt->bindValue(1, $id);
|
||||
$insertStmt->bindValue(2, $data, ParameterType::LARGE_OBJECT);
|
||||
};
|
||||
}
|
||||
foreach ($values as $id => $data) {
|
||||
$bind($id, $data);
|
||||
try {
|
||||
$rowCount = $stmt->executeStatement();
|
||||
} catch (TableNotFoundException $e) {
|
||||
if (!$this->conn->isTransactionActive() || \in_array($platformName, ['pgsql', 'sqlite', 'sqlsrv'], \true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$rowCount = $stmt->executeStatement();
|
||||
}
|
||||
if (null === $platformName && 0 === $rowCount) {
|
||||
try {
|
||||
$insertStmt->executeStatement();
|
||||
} catch (DBALException $e) {
|
||||
// A concurrent write won, let it be
|
||||
}
|
||||
}
|
||||
}
|
||||
return $failed;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function getId($key)
|
||||
{
|
||||
if ('pgsql' !== $this->getPlatformName()) {
|
||||
return parent::getId($key);
|
||||
}
|
||||
if (\str_contains($key, "\x00") || \str_contains($key, '%') || !\preg_match('//u', $key)) {
|
||||
$key = \rawurlencode($key);
|
||||
}
|
||||
return parent::getId($key);
|
||||
}
|
||||
private function getPlatformName() : string
|
||||
{
|
||||
if (isset($this->platformName)) {
|
||||
return $this->platformName;
|
||||
}
|
||||
$platform = $this->conn->getDatabasePlatform();
|
||||
switch (\true) {
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\MySQLPlatform:
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\MySQL57Platform:
|
||||
return $this->platformName = 'mysql';
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\SqlitePlatform:
|
||||
return $this->platformName = 'sqlite';
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\PostgreSQLPlatform:
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\PostgreSQL94Platform:
|
||||
return $this->platformName = 'pgsql';
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\OraclePlatform:
|
||||
return $this->platformName = 'oci';
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\SQLServerPlatform:
|
||||
case $platform instanceof \WP_Ultimo\Dependencies\Doctrine\DBAL\Platforms\SQLServer2012Platform:
|
||||
return $this->platformName = 'sqlsrv';
|
||||
default:
|
||||
return $this->platformName = \get_class($platform);
|
||||
}
|
||||
}
|
||||
private function getServerVersion() : string
|
||||
{
|
||||
if (isset($this->serverVersion)) {
|
||||
return $this->serverVersion;
|
||||
}
|
||||
$conn = $this->conn->getWrappedConnection();
|
||||
if ($conn instanceof ServerInfoAwareConnection) {
|
||||
return $this->serverVersion = $conn->getServerVersion();
|
||||
}
|
||||
return $this->serverVersion = '0';
|
||||
}
|
||||
private function addTableToSchema(Schema $schema) : void
|
||||
{
|
||||
$types = ['mysql' => 'binary', 'sqlite' => 'text'];
|
||||
$table = $schema->createTable($this->table);
|
||||
$table->addColumn($this->idCol, $types[$this->getPlatformName()] ?? 'string', ['length' => 255]);
|
||||
$table->addColumn($this->dataCol, 'blob', ['length' => 16777215]);
|
||||
$table->addColumn($this->lifetimeCol, 'integer', ['unsigned' => \true, 'notnull' => \false]);
|
||||
$table->addColumn($this->timeCol, 'integer', ['unsigned' => \true]);
|
||||
$table->setPrimaryKey([$this->idCol]);
|
||||
}
|
||||
}
|
26
dependencies/symfony/cache/Adapter/FilesystemAdapter.php
vendored
Normal file
26
dependencies/symfony/cache/Adapter/FilesystemAdapter.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\FilesystemTrait;
|
||||
class FilesystemAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
use FilesystemTrait;
|
||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->init($namespace, $directory);
|
||||
}
|
||||
}
|
205
dependencies/symfony/cache/Adapter/FilesystemTagAwareAdapter.php
vendored
Normal file
205
dependencies/symfony/cache/Adapter/FilesystemTagAwareAdapter.php
vendored
Normal file
@ -0,0 +1,205 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\FilesystemTrait;
|
||||
/**
|
||||
* Stores tag id <> cache id relationship as a symlink, and lookup on invalidation calls.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author André Rømcke <andre.romcke+symfony@gmail.com>
|
||||
*/
|
||||
class FilesystemTagAwareAdapter extends AbstractTagAwareAdapter implements PruneableInterface
|
||||
{
|
||||
use FilesystemTrait {
|
||||
doClear as private doClearCache;
|
||||
doSave as private doSaveCache;
|
||||
}
|
||||
/**
|
||||
* Folder used for tag symlinks.
|
||||
*/
|
||||
private const TAG_FOLDER = 'tags';
|
||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
$this->marshaller = new TagAwareMarshaller($marshaller);
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->init($namespace, $directory);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
$ok = $this->doClearCache($namespace);
|
||||
if ('' !== $namespace) {
|
||||
return $ok;
|
||||
}
|
||||
\set_error_handler(static function () {
|
||||
});
|
||||
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
try {
|
||||
foreach ($this->scanHashDir($this->directory . self::TAG_FOLDER . \DIRECTORY_SEPARATOR) as $dir) {
|
||||
if (\rename($dir, $renamed = \substr_replace($dir, \bin2hex(\random_bytes(4)), -8))) {
|
||||
$dir = $renamed . \DIRECTORY_SEPARATOR;
|
||||
} else {
|
||||
$dir .= \DIRECTORY_SEPARATOR;
|
||||
$renamed = null;
|
||||
}
|
||||
for ($i = 0; $i < 38; ++$i) {
|
||||
if (!\is_dir($dir . $chars[$i])) {
|
||||
continue;
|
||||
}
|
||||
for ($j = 0; $j < 38; ++$j) {
|
||||
if (!\is_dir($d = $dir . $chars[$i] . \DIRECTORY_SEPARATOR . $chars[$j])) {
|
||||
continue;
|
||||
}
|
||||
foreach (\scandir($d, \SCANDIR_SORT_NONE) ?: [] as $link) {
|
||||
if ('.' !== $link && '..' !== $link && (null !== $renamed || !\realpath($d . \DIRECTORY_SEPARATOR . $link))) {
|
||||
\unlink($d . \DIRECTORY_SEPARATOR . $link);
|
||||
}
|
||||
}
|
||||
null === $renamed ?: \rmdir($d);
|
||||
}
|
||||
null === $renamed ?: \rmdir($dir . $chars[$i]);
|
||||
}
|
||||
null === $renamed ?: \rmdir($renamed);
|
||||
}
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime, array $addTagData = [], array $removeTagData = []) : array
|
||||
{
|
||||
$failed = $this->doSaveCache($values, $lifetime);
|
||||
// Add Tags as symlinks
|
||||
foreach ($addTagData as $tagId => $ids) {
|
||||
$tagFolder = $this->getTagFolder($tagId);
|
||||
foreach ($ids as $id) {
|
||||
if ($failed && \in_array($id, $failed, \true)) {
|
||||
continue;
|
||||
}
|
||||
$file = $this->getFile($id);
|
||||
if (!@\symlink($file, $tagLink = $this->getFile($id, \true, $tagFolder)) && !\is_link($tagLink)) {
|
||||
@\unlink($file);
|
||||
$failed[] = $id;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unlink removed Tags
|
||||
foreach ($removeTagData as $tagId => $ids) {
|
||||
$tagFolder = $this->getTagFolder($tagId);
|
||||
foreach ($ids as $id) {
|
||||
if ($failed && \in_array($id, $failed, \true)) {
|
||||
continue;
|
||||
}
|
||||
@\unlink($this->getFile($id, \false, $tagFolder));
|
||||
}
|
||||
}
|
||||
return $failed;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteYieldTags(array $ids) : iterable
|
||||
{
|
||||
foreach ($ids as $id) {
|
||||
$file = $this->getFile($id);
|
||||
if (!\is_file($file) || !($h = @\fopen($file, 'r'))) {
|
||||
continue;
|
||||
}
|
||||
if ((\PHP_VERSION_ID >= 70300 || '\\' !== \DIRECTORY_SEPARATOR) && !@\unlink($file)) {
|
||||
\fclose($h);
|
||||
continue;
|
||||
}
|
||||
$meta = \explode("\n", \fread($h, 4096), 3)[2] ?? '';
|
||||
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||
if (13 < \strlen($meta) && "\x9d" === $meta[0] && "\x00" === $meta[5] && "_" === $meta[9]) {
|
||||
$meta[9] = "\x00";
|
||||
$tagLen = \unpack('Nlen', $meta, 9)['len'];
|
||||
$meta = \substr($meta, 13, $tagLen);
|
||||
if (0 < ($tagLen -= \strlen($meta))) {
|
||||
$meta .= \fread($h, $tagLen);
|
||||
}
|
||||
try {
|
||||
(yield $id => '' === $meta ? [] : $this->marshaller->unmarshall($meta));
|
||||
} catch (\Exception $e) {
|
||||
(yield $id => []);
|
||||
}
|
||||
}
|
||||
\fclose($h);
|
||||
if (\PHP_VERSION_ID < 70300 && '\\' === \DIRECTORY_SEPARATOR) {
|
||||
@\unlink($file);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteTagRelations(array $tagData) : bool
|
||||
{
|
||||
foreach ($tagData as $tagId => $idList) {
|
||||
$tagFolder = $this->getTagFolder($tagId);
|
||||
foreach ($idList as $id) {
|
||||
@\unlink($this->getFile($id, \false, $tagFolder));
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doInvalidate(array $tagIds) : bool
|
||||
{
|
||||
foreach ($tagIds as $tagId) {
|
||||
if (!\is_dir($tagFolder = $this->getTagFolder($tagId))) {
|
||||
continue;
|
||||
}
|
||||
\set_error_handler(static function () {
|
||||
});
|
||||
try {
|
||||
if (\rename($tagFolder, $renamed = \substr_replace($tagFolder, \bin2hex(\random_bytes(4)), -9))) {
|
||||
$tagFolder = $renamed . \DIRECTORY_SEPARATOR;
|
||||
} else {
|
||||
$renamed = null;
|
||||
}
|
||||
foreach ($this->scanHashDir($tagFolder) as $itemLink) {
|
||||
\unlink(\realpath($itemLink) ?: $itemLink);
|
||||
\unlink($itemLink);
|
||||
}
|
||||
if (null === $renamed) {
|
||||
continue;
|
||||
}
|
||||
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
for ($i = 0; $i < 38; ++$i) {
|
||||
for ($j = 0; $j < 38; ++$j) {
|
||||
\rmdir($tagFolder . $chars[$i] . \DIRECTORY_SEPARATOR . $chars[$j]);
|
||||
}
|
||||
\rmdir($tagFolder . $chars[$i]);
|
||||
}
|
||||
\rmdir($renamed);
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
private function getTagFolder(string $tagId) : string
|
||||
{
|
||||
return $this->getFile($tagId, \false, $this->directory . self::TAG_FOLDER . \DIRECTORY_SEPARATOR) . \DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
303
dependencies/symfony/cache/Adapter/MemcachedAdapter.php
vendored
Normal file
303
dependencies/symfony/cache/Adapter/MemcachedAdapter.php
vendored
Normal file
@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
/**
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class MemcachedAdapter extends AbstractAdapter
|
||||
{
|
||||
/**
|
||||
* We are replacing characters that are illegal in Memcached keys with reserved characters from
|
||||
* {@see \Symfony\Contracts\Cache\ItemInterface::RESERVED_CHARACTERS} that are legal in Memcached.
|
||||
* Note: don’t use {@see \Symfony\Component\Cache\Adapter\AbstractAdapter::NS_SEPARATOR}.
|
||||
*/
|
||||
private const RESERVED_MEMCACHED = " \n\r\t\v\f\x00";
|
||||
private const RESERVED_PSR6 = '@()\\{}/';
|
||||
protected $maxIdLength = 250;
|
||||
private $marshaller;
|
||||
private $client;
|
||||
private $lazyClient;
|
||||
/**
|
||||
* Using a MemcachedAdapter with a TagAwareAdapter for storing tags is discouraged.
|
||||
* Using a RedisAdapter is recommended instead. If you cannot do otherwise, be aware that:
|
||||
* - the Memcached::OPT_BINARY_PROTOCOL must be enabled
|
||||
* (that's the default when using MemcachedAdapter::createConnection());
|
||||
* - tags eviction by Memcached's LRU algorithm will break by-tags invalidation;
|
||||
* your Memcached memory should be large enough to never trigger LRU.
|
||||
*
|
||||
* Using a MemcachedAdapter as a pure items store is fine.
|
||||
*/
|
||||
public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('Memcached ' . (\PHP_VERSION_ID >= 80100 ? '> 3.1.5' : '>= 2.2.0') . ' is required.');
|
||||
}
|
||||
if ('Memcached' === \get_class($client)) {
|
||||
$opt = $client->getOption(\Memcached::OPT_SERIALIZER);
|
||||
if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
|
||||
throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
|
||||
}
|
||||
$this->maxIdLength -= \strlen($client->getOption(\Memcached::OPT_PREFIX_KEY));
|
||||
$this->client = $client;
|
||||
} else {
|
||||
$this->lazyClient = $client;
|
||||
}
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
$this->enableVersioning();
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
}
|
||||
public static function isSupported()
|
||||
{
|
||||
return \extension_loaded('memcached') && \version_compare(\phpversion('memcached'), \PHP_VERSION_ID >= 80100 ? '3.1.6' : '2.2.0', '>=');
|
||||
}
|
||||
/**
|
||||
* Creates a Memcached instance.
|
||||
*
|
||||
* By default, the binary protocol, no block, and libketama compatible options are enabled.
|
||||
*
|
||||
* Examples for servers:
|
||||
* - 'memcached://user:pass@localhost?weight=33'
|
||||
* - [['localhost', 11211, 33]]
|
||||
*
|
||||
* @param array[]|string|string[] $servers An array of servers, a DSN, or an array of DSNs
|
||||
*
|
||||
* @return \Memcached
|
||||
*
|
||||
* @throws \ErrorException When invalid options or servers are provided
|
||||
*/
|
||||
public static function createConnection($servers, array $options = [])
|
||||
{
|
||||
if (\is_string($servers)) {
|
||||
$servers = [$servers];
|
||||
} elseif (!\is_array($servers)) {
|
||||
throw new InvalidArgumentException(\sprintf('MemcachedAdapter::createClient() expects array or string as first argument, "%s" given.', \get_debug_type($servers)));
|
||||
}
|
||||
if (!static::isSupported()) {
|
||||
throw new CacheException('Memcached ' . (\PHP_VERSION_ID >= 80100 ? '> 3.1.5' : '>= 2.2.0') . ' is required.');
|
||||
}
|
||||
\set_error_handler(function ($type, $msg, $file, $line) {
|
||||
throw new \ErrorException($msg, 0, $type, $file, $line);
|
||||
});
|
||||
try {
|
||||
$client = new \Memcached($options['persistent_id'] ?? null);
|
||||
$username = $options['username'] ?? null;
|
||||
$password = $options['password'] ?? null;
|
||||
// parse any DSN in $servers
|
||||
foreach ($servers as $i => $dsn) {
|
||||
if (\is_array($dsn)) {
|
||||
continue;
|
||||
}
|
||||
if (!\str_starts_with($dsn, 'memcached:')) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Memcached DSN: "%s" does not start with "memcached:".', $dsn));
|
||||
}
|
||||
$params = \preg_replace_callback('#^memcached:(//)?(?:([^@]*+)@)?#', function ($m) use(&$username, &$password) {
|
||||
if (!empty($m[2])) {
|
||||
[$username, $password] = \explode(':', $m[2], 2) + [1 => null];
|
||||
}
|
||||
return 'file:' . ($m[1] ?? '');
|
||||
}, $dsn);
|
||||
if (\false === ($params = \parse_url($params))) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Memcached DSN: "%s".', $dsn));
|
||||
}
|
||||
$query = $hosts = [];
|
||||
if (isset($params['query'])) {
|
||||
\parse_str($params['query'], $query);
|
||||
if (isset($query['host'])) {
|
||||
if (!\is_array($hosts = $query['host'])) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Memcached DSN: "%s".', $dsn));
|
||||
}
|
||||
foreach ($hosts as $host => $weight) {
|
||||
if (\false === ($port = \strrpos($host, ':'))) {
|
||||
$hosts[$host] = [$host, 11211, (int) $weight];
|
||||
} else {
|
||||
$hosts[$host] = [\substr($host, 0, $port), (int) \substr($host, 1 + $port), (int) $weight];
|
||||
}
|
||||
}
|
||||
$hosts = \array_values($hosts);
|
||||
unset($query['host']);
|
||||
}
|
||||
if ($hosts && !isset($params['host']) && !isset($params['path'])) {
|
||||
unset($servers[$i]);
|
||||
$servers = \array_merge($servers, $hosts);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!isset($params['host']) && !isset($params['path'])) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Memcached DSN: "%s".', $dsn));
|
||||
}
|
||||
if (isset($params['path']) && \preg_match('#/(\\d+)$#', $params['path'], $m)) {
|
||||
$params['weight'] = $m[1];
|
||||
$params['path'] = \substr($params['path'], 0, -\strlen($m[0]));
|
||||
}
|
||||
$params += ['host' => $params['host'] ?? $params['path'], 'port' => isset($params['host']) ? 11211 : null, 'weight' => 0];
|
||||
if ($query) {
|
||||
$params += $query;
|
||||
$options = $query + $options;
|
||||
}
|
||||
$servers[$i] = [$params['host'], $params['port'], $params['weight']];
|
||||
if ($hosts) {
|
||||
$servers = \array_merge($servers, $hosts);
|
||||
}
|
||||
}
|
||||
// set client's options
|
||||
unset($options['persistent_id'], $options['username'], $options['password'], $options['weight'], $options['lazy']);
|
||||
$options = \array_change_key_case($options, \CASE_UPPER);
|
||||
$client->setOption(\Memcached::OPT_BINARY_PROTOCOL, \true);
|
||||
$client->setOption(\Memcached::OPT_NO_BLOCK, \true);
|
||||
$client->setOption(\Memcached::OPT_TCP_NODELAY, \true);
|
||||
if (!\array_key_exists('LIBKETAMA_COMPATIBLE', $options) && !\array_key_exists(\Memcached::OPT_LIBKETAMA_COMPATIBLE, $options)) {
|
||||
$client->setOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE, \true);
|
||||
}
|
||||
foreach ($options as $name => $value) {
|
||||
if (\is_int($name)) {
|
||||
continue;
|
||||
}
|
||||
if ('HASH' === $name || 'SERIALIZER' === $name || 'DISTRIBUTION' === $name) {
|
||||
$value = \constant('Memcached::' . $name . '_' . \strtoupper($value));
|
||||
}
|
||||
unset($options[$name]);
|
||||
if (\defined('Memcached::OPT_' . $name)) {
|
||||
$options[\constant('Memcached::OPT_' . $name)] = $value;
|
||||
}
|
||||
}
|
||||
$client->setOptions($options + [\Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP]);
|
||||
// set client's servers, taking care of persistent connections
|
||||
if (!$client->isPristine()) {
|
||||
$oldServers = [];
|
||||
foreach ($client->getServerList() as $server) {
|
||||
$oldServers[] = [$server['host'], $server['port']];
|
||||
}
|
||||
$newServers = [];
|
||||
foreach ($servers as $server) {
|
||||
if (1 < \count($server)) {
|
||||
$server = \array_values($server);
|
||||
unset($server[2]);
|
||||
$server[1] = (int) $server[1];
|
||||
}
|
||||
$newServers[] = $server;
|
||||
}
|
||||
if ($oldServers !== $newServers) {
|
||||
$client->resetServerList();
|
||||
$client->addServers($servers);
|
||||
}
|
||||
} else {
|
||||
$client->addServers($servers);
|
||||
}
|
||||
if (null !== $username || null !== $password) {
|
||||
if (!\method_exists($client, 'setSaslAuthData')) {
|
||||
\trigger_error('Missing SASL support: the memcached extension must be compiled with --enable-memcached-sasl.');
|
||||
}
|
||||
$client->setSaslAuthData($username, $password);
|
||||
}
|
||||
return $client;
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
if (!($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
if ($lifetime && $lifetime > 30 * 86400) {
|
||||
$lifetime += \time();
|
||||
}
|
||||
$encodedValues = [];
|
||||
foreach ($values as $key => $value) {
|
||||
$encodedValues[self::encodeKey($key)] = $value;
|
||||
}
|
||||
return $this->checkResultCode($this->getClient()->setMulti($encodedValues, $lifetime)) ? $failed : \false;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
try {
|
||||
$encodedIds = \array_map([__CLASS__, 'encodeKey'], $ids);
|
||||
$encodedResult = $this->checkResultCode($this->getClient()->getMulti($encodedIds));
|
||||
$result = [];
|
||||
foreach ($encodedResult as $key => $value) {
|
||||
$result[self::decodeKey($key)] = $this->marshaller->unmarshall($value);
|
||||
}
|
||||
return $result;
|
||||
} catch (\Error $e) {
|
||||
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
return \false !== $this->getClient()->get(self::encodeKey($id)) || $this->checkResultCode(\Memcached::RES_SUCCESS === $this->client->getResultCode());
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
$ok = \true;
|
||||
$encodedIds = \array_map([__CLASS__, 'encodeKey'], $ids);
|
||||
foreach ($this->checkResultCode($this->getClient()->deleteMulti($encodedIds)) as $result) {
|
||||
if (\Memcached::RES_SUCCESS !== $result && \Memcached::RES_NOTFOUND !== $result) {
|
||||
$ok = \false;
|
||||
}
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
return '' === $namespace && $this->getClient()->flush();
|
||||
}
|
||||
private function checkResultCode($result)
|
||||
{
|
||||
$code = $this->client->getResultCode();
|
||||
if (\Memcached::RES_SUCCESS === $code || \Memcached::RES_NOTFOUND === $code) {
|
||||
return $result;
|
||||
}
|
||||
throw new CacheException('MemcachedAdapter client error: ' . \strtolower($this->client->getResultMessage()));
|
||||
}
|
||||
private function getClient() : \Memcached
|
||||
{
|
||||
if ($this->client) {
|
||||
return $this->client;
|
||||
}
|
||||
$opt = $this->lazyClient->getOption(\Memcached::OPT_SERIALIZER);
|
||||
if (\Memcached::SERIALIZER_PHP !== $opt && \Memcached::SERIALIZER_IGBINARY !== $opt) {
|
||||
throw new CacheException('MemcachedAdapter: "serializer" option must be "php" or "igbinary".');
|
||||
}
|
||||
if ('' !== ($prefix = (string) $this->lazyClient->getOption(\Memcached::OPT_PREFIX_KEY))) {
|
||||
throw new CacheException(\sprintf('MemcachedAdapter: "prefix_key" option must be empty when using proxified connections, "%s" given.', $prefix));
|
||||
}
|
||||
return $this->client = $this->lazyClient;
|
||||
}
|
||||
private static function encodeKey(string $key) : string
|
||||
{
|
||||
return \strtr($key, self::RESERVED_MEMCACHED, self::RESERVED_PSR6);
|
||||
}
|
||||
private static function decodeKey(string $key) : string
|
||||
{
|
||||
return \strtr($key, self::RESERVED_PSR6, self::RESERVED_MEMCACHED);
|
||||
}
|
||||
}
|
130
dependencies/symfony/cache/Adapter/NullAdapter.php
vendored
Normal file
130
dependencies/symfony/cache/Adapter/NullAdapter.php
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class NullAdapter implements AdapterInterface, CacheInterface
|
||||
{
|
||||
private static $createCacheItem;
|
||||
public function __construct()
|
||||
{
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->isHit = \false;
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
$save = \true;
|
||||
return $callback((self::$createCacheItem)($key), $save);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
return (self::$createCacheItem)($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
return $this->generateItems($keys);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
return \false;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(string $key) : bool
|
||||
{
|
||||
return $this->deleteItem($key);
|
||||
}
|
||||
private function generateItems(array $keys) : \Generator
|
||||
{
|
||||
$f = self::$createCacheItem;
|
||||
foreach ($keys as $key) {
|
||||
(yield $key => $f($key));
|
||||
}
|
||||
}
|
||||
}
|
32
dependencies/symfony/cache/Adapter/ParameterNormalizer.php
vendored
Normal file
32
dependencies/symfony/cache/Adapter/ParameterNormalizer.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
/**
|
||||
* @author Lars Strojny <lars@strojny.net>
|
||||
*/
|
||||
final class ParameterNormalizer
|
||||
{
|
||||
public static function normalizeDuration(string $duration) : int
|
||||
{
|
||||
if (\is_numeric($duration)) {
|
||||
return $duration;
|
||||
}
|
||||
if (\false !== ($time = \strtotime($duration, 0))) {
|
||||
return $time;
|
||||
}
|
||||
try {
|
||||
return \DateTime::createFromFormat('U', 0)->add(new \DateInterval($duration))->getTimestamp();
|
||||
} catch (\Exception $e) {
|
||||
throw new \InvalidArgumentException(\sprintf('Cannot parse date interval "%s".', $duration), 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
512
dependencies/symfony/cache/Adapter/PdoAdapter.php
vendored
Normal file
512
dependencies/symfony/cache/Adapter/PdoAdapter.php
vendored
Normal file
@ -0,0 +1,512 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Connection;
|
||||
use WP_Ultimo\Dependencies\Doctrine\DBAL\Schema\Schema;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
class PdoAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
protected $maxIdLength = 255;
|
||||
private $marshaller;
|
||||
private $conn;
|
||||
private $dsn;
|
||||
private $driver;
|
||||
private $serverVersion;
|
||||
private $table = 'cache_items';
|
||||
private $idCol = 'item_id';
|
||||
private $dataCol = 'item_data';
|
||||
private $lifetimeCol = 'item_lifetime';
|
||||
private $timeCol = 'item_time';
|
||||
private $username = '';
|
||||
private $password = '';
|
||||
private $connectionOptions = [];
|
||||
private $namespace;
|
||||
private $dbalAdapter;
|
||||
/**
|
||||
* You can either pass an existing database connection as PDO instance or
|
||||
* a DSN string that will be used to lazy-connect to the database when the
|
||||
* cache is actually used.
|
||||
*
|
||||
* List of available options:
|
||||
* * db_table: The name of the table [default: cache_items]
|
||||
* * db_id_col: The column where to store the cache id [default: item_id]
|
||||
* * db_data_col: The column where to store the cache data [default: item_data]
|
||||
* * db_lifetime_col: The column where to store the lifetime [default: item_lifetime]
|
||||
* * db_time_col: The column where to store the timestamp [default: item_time]
|
||||
* * db_username: The username when lazy-connect [default: '']
|
||||
* * db_password: The password when lazy-connect [default: '']
|
||||
* * db_connection_options: An array of driver-specific connection options [default: []]
|
||||
*
|
||||
* @param \PDO|string $connOrDsn
|
||||
*
|
||||
* @throws InvalidArgumentException When first argument is not PDO nor Connection nor string
|
||||
* @throws InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
|
||||
* @throws InvalidArgumentException When namespace contains invalid characters
|
||||
*/
|
||||
public function __construct($connOrDsn, string $namespace = '', int $defaultLifetime = 0, array $options = [], MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if ($connOrDsn instanceof Connection || \is_string($connOrDsn) && \str_contains($connOrDsn, '://')) {
|
||||
trigger_deprecation('symfony/cache', '5.4', 'Usage of a DBAL Connection with "%s" is deprecated and will be removed in symfony 6.0. Use "%s" instead.', __CLASS__, DoctrineDbalAdapter::class);
|
||||
$this->dbalAdapter = new DoctrineDbalAdapter($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
|
||||
return;
|
||||
}
|
||||
if (isset($namespace[0]) && \preg_match('#[^-+.A-Za-z0-9]#', $namespace, $match)) {
|
||||
throw new InvalidArgumentException(\sprintf('Namespace contains "%s" but only characters in [-+.A-Za-z0-9] are allowed.', $match[0]));
|
||||
}
|
||||
if ($connOrDsn instanceof \PDO) {
|
||||
if (\PDO::ERRMODE_EXCEPTION !== $connOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
|
||||
throw new InvalidArgumentException(\sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)).', __CLASS__));
|
||||
}
|
||||
$this->conn = $connOrDsn;
|
||||
} elseif (\is_string($connOrDsn)) {
|
||||
$this->dsn = $connOrDsn;
|
||||
} else {
|
||||
throw new InvalidArgumentException(\sprintf('"%s" requires PDO or Doctrine\\DBAL\\Connection instance or DSN string as first argument, "%s" given.', __CLASS__, \get_debug_type($connOrDsn)));
|
||||
}
|
||||
$this->table = $options['db_table'] ?? $this->table;
|
||||
$this->idCol = $options['db_id_col'] ?? $this->idCol;
|
||||
$this->dataCol = $options['db_data_col'] ?? $this->dataCol;
|
||||
$this->lifetimeCol = $options['db_lifetime_col'] ?? $this->lifetimeCol;
|
||||
$this->timeCol = $options['db_time_col'] ?? $this->timeCol;
|
||||
$this->username = $options['db_username'] ?? $this->username;
|
||||
$this->password = $options['db_password'] ?? $this->password;
|
||||
$this->connectionOptions = $options['db_connection_options'] ?? $this->connectionOptions;
|
||||
$this->namespace = $namespace;
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->getItem($key);
|
||||
}
|
||||
return parent::getItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->getItems($keys);
|
||||
}
|
||||
return parent::getItems($keys);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->hasItem($key);
|
||||
}
|
||||
return parent::hasItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->deleteItem($key);
|
||||
}
|
||||
return parent::deleteItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->deleteItems($keys);
|
||||
}
|
||||
return parent::deleteItems($keys);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->clear($prefix);
|
||||
}
|
||||
return parent::clear($prefix);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->get($key, $callback, $beta, $metadata);
|
||||
}
|
||||
return parent::get($key, $callback, $beta, $metadata);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function delete(string $key) : bool
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->delete($key);
|
||||
}
|
||||
return parent::delete($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->save($item);
|
||||
}
|
||||
return parent::save($item);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->saveDeferred($item);
|
||||
}
|
||||
return parent::saveDeferred($item);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger) : void
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
$this->dbalAdapter->setLogger($logger);
|
||||
return;
|
||||
}
|
||||
parent::setLogger($logger);
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->commit();
|
||||
}
|
||||
return parent::commit();
|
||||
}
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
$this->dbalAdapter->reset();
|
||||
return;
|
||||
}
|
||||
parent::reset();
|
||||
}
|
||||
/**
|
||||
* Creates the table to store cache items which can be called once for setup.
|
||||
*
|
||||
* Cache ID are saved in a column of maximum length 255. Cache data is
|
||||
* saved in a BLOB.
|
||||
*
|
||||
* @throws \PDOException When the table already exists
|
||||
* @throws \DomainException When an unsupported PDO driver is used
|
||||
*/
|
||||
public function createTable()
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
$this->dbalAdapter->createTable();
|
||||
return;
|
||||
}
|
||||
// connect if we are not yet
|
||||
$conn = $this->getConnection();
|
||||
switch ($this->driver) {
|
||||
case 'mysql':
|
||||
// We use varbinary for the ID column because it prevents unwanted conversions:
|
||||
// - character set conversions between server and client
|
||||
// - trailing space removal
|
||||
// - case-insensitivity
|
||||
// - language processing like é == e
|
||||
$sql = "CREATE TABLE {$this->table} ({$this->idCol} VARBINARY(255) NOT NULL PRIMARY KEY, {$this->dataCol} MEDIUMBLOB NOT NULL, {$this->lifetimeCol} INTEGER UNSIGNED, {$this->timeCol} INTEGER UNSIGNED NOT NULL) COLLATE utf8mb4_bin, ENGINE = InnoDB";
|
||||
break;
|
||||
case 'sqlite':
|
||||
$sql = "CREATE TABLE {$this->table} ({$this->idCol} TEXT NOT NULL PRIMARY KEY, {$this->dataCol} BLOB NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)";
|
||||
break;
|
||||
case 'pgsql':
|
||||
$sql = "CREATE TABLE {$this->table} ({$this->idCol} VARCHAR(255) NOT NULL PRIMARY KEY, {$this->dataCol} BYTEA NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)";
|
||||
break;
|
||||
case 'oci':
|
||||
$sql = "CREATE TABLE {$this->table} ({$this->idCol} VARCHAR2(255) NOT NULL PRIMARY KEY, {$this->dataCol} BLOB NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)";
|
||||
break;
|
||||
case 'sqlsrv':
|
||||
$sql = "CREATE TABLE {$this->table} ({$this->idCol} VARCHAR(255) NOT NULL PRIMARY KEY, {$this->dataCol} VARBINARY(MAX) NOT NULL, {$this->lifetimeCol} INTEGER, {$this->timeCol} INTEGER NOT NULL)";
|
||||
break;
|
||||
default:
|
||||
throw new \DomainException(\sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
|
||||
}
|
||||
$conn->exec($sql);
|
||||
}
|
||||
/**
|
||||
* Adds the Table to the Schema if the adapter uses this Connection.
|
||||
*
|
||||
* @deprecated since symfony/cache 5.4 use DoctrineDbalAdapter instead
|
||||
*/
|
||||
public function configureSchema(Schema $schema, Connection $forConnection) : void
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
$this->dbalAdapter->configureSchema($schema, $forConnection);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
if (isset($this->dbalAdapter)) {
|
||||
return $this->dbalAdapter->prune();
|
||||
}
|
||||
$deleteSql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= :time";
|
||||
if ('' !== $this->namespace) {
|
||||
$deleteSql .= " AND {$this->idCol} LIKE :namespace";
|
||||
}
|
||||
$connection = $this->getConnection();
|
||||
try {
|
||||
$delete = $connection->prepare($deleteSql);
|
||||
} catch (\PDOException $e) {
|
||||
return \true;
|
||||
}
|
||||
$delete->bindValue(':time', \time(), \PDO::PARAM_INT);
|
||||
if ('' !== $this->namespace) {
|
||||
$delete->bindValue(':namespace', \sprintf('%s%%', $this->namespace), \PDO::PARAM_STR);
|
||||
}
|
||||
try {
|
||||
return $delete->execute();
|
||||
} catch (\PDOException $e) {
|
||||
return \true;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$now = \time();
|
||||
$expired = [];
|
||||
$sql = \str_pad('', (\count($ids) << 1) - 1, '?,');
|
||||
$sql = "SELECT {$this->idCol}, CASE WHEN {$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > ? THEN {$this->dataCol} ELSE NULL END FROM {$this->table} WHERE {$this->idCol} IN ({$sql})";
|
||||
$stmt = $connection->prepare($sql);
|
||||
$stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
|
||||
foreach ($ids as $id) {
|
||||
$stmt->bindValue(++$i, $id);
|
||||
}
|
||||
$result = $stmt->execute();
|
||||
if (\is_object($result)) {
|
||||
$result = $result->iterateNumeric();
|
||||
} else {
|
||||
$stmt->setFetchMode(\PDO::FETCH_NUM);
|
||||
$result = $stmt;
|
||||
}
|
||||
foreach ($result as $row) {
|
||||
if (null === $row[1]) {
|
||||
$expired[] = $row[0];
|
||||
} else {
|
||||
(yield $row[0] => $this->marshaller->unmarshall(\is_resource($row[1]) ? \stream_get_contents($row[1]) : $row[1]));
|
||||
}
|
||||
}
|
||||
if ($expired) {
|
||||
$sql = \str_pad('', (\count($expired) << 1) - 1, '?,');
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->lifetimeCol} + {$this->timeCol} <= ? AND {$this->idCol} IN ({$sql})";
|
||||
$stmt = $connection->prepare($sql);
|
||||
$stmt->bindValue($i = 1, $now, \PDO::PARAM_INT);
|
||||
foreach ($expired as $id) {
|
||||
$stmt->bindValue(++$i, $id);
|
||||
}
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$sql = "SELECT 1 FROM {$this->table} WHERE {$this->idCol} = :id AND ({$this->lifetimeCol} IS NULL OR {$this->lifetimeCol} + {$this->timeCol} > :time)";
|
||||
$stmt = $connection->prepare($sql);
|
||||
$stmt->bindValue(':id', $id);
|
||||
$stmt->bindValue(':time', \time(), \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
return (bool) $stmt->fetchColumn();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
$conn = $this->getConnection();
|
||||
if ('' === $namespace) {
|
||||
if ('sqlite' === $this->driver) {
|
||||
$sql = "DELETE FROM {$this->table}";
|
||||
} else {
|
||||
$sql = "TRUNCATE TABLE {$this->table}";
|
||||
}
|
||||
} else {
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->idCol} LIKE '{$namespace}%'";
|
||||
}
|
||||
try {
|
||||
$conn->exec($sql);
|
||||
} catch (\PDOException $e) {
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
$sql = \str_pad('', (\count($ids) << 1) - 1, '?,');
|
||||
$sql = "DELETE FROM {$this->table} WHERE {$this->idCol} IN ({$sql})";
|
||||
try {
|
||||
$stmt = $this->getConnection()->prepare($sql);
|
||||
$stmt->execute(\array_values($ids));
|
||||
} catch (\PDOException $e) {
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
if (!($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
$conn = $this->getConnection();
|
||||
$driver = $this->driver;
|
||||
$insertSql = "INSERT INTO {$this->table} ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (:id, :data, :lifetime, :time)";
|
||||
switch (\true) {
|
||||
case 'mysql' === $driver:
|
||||
$sql = $insertSql . " ON DUPLICATE KEY UPDATE {$this->dataCol} = VALUES({$this->dataCol}), {$this->lifetimeCol} = VALUES({$this->lifetimeCol}), {$this->timeCol} = VALUES({$this->timeCol})";
|
||||
break;
|
||||
case 'oci' === $driver:
|
||||
// DUAL is Oracle specific dummy table
|
||||
$sql = "MERGE INTO {$this->table} USING DUAL ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?";
|
||||
break;
|
||||
case 'sqlsrv' === $driver && \version_compare($this->getServerVersion(), '10', '>='):
|
||||
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
|
||||
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||
$sql = "MERGE INTO {$this->table} WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ({$this->idCol} = ?) " . "WHEN NOT MATCHED THEN INSERT ({$this->idCol}, {$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) VALUES (?, ?, ?, ?) " . "WHEN MATCHED THEN UPDATE SET {$this->dataCol} = ?, {$this->lifetimeCol} = ?, {$this->timeCol} = ?;";
|
||||
break;
|
||||
case 'sqlite' === $driver:
|
||||
$sql = 'INSERT OR REPLACE' . \substr($insertSql, 6);
|
||||
break;
|
||||
case 'pgsql' === $driver && \version_compare($this->getServerVersion(), '9.5', '>='):
|
||||
$sql = $insertSql . " ON CONFLICT ({$this->idCol}) DO UPDATE SET ({$this->dataCol}, {$this->lifetimeCol}, {$this->timeCol}) = (EXCLUDED.{$this->dataCol}, EXCLUDED.{$this->lifetimeCol}, EXCLUDED.{$this->timeCol})";
|
||||
break;
|
||||
default:
|
||||
$driver = null;
|
||||
$sql = "UPDATE {$this->table} SET {$this->dataCol} = :data, {$this->lifetimeCol} = :lifetime, {$this->timeCol} = :time WHERE {$this->idCol} = :id";
|
||||
break;
|
||||
}
|
||||
$now = \time();
|
||||
$lifetime = $lifetime ?: null;
|
||||
try {
|
||||
$stmt = $conn->prepare($sql);
|
||||
} catch (\PDOException $e) {
|
||||
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], \true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$stmt = $conn->prepare($sql);
|
||||
}
|
||||
// $id and $data are defined later in the loop. Binding is done by reference, values are read on execution.
|
||||
if ('sqlsrv' === $driver || 'oci' === $driver) {
|
||||
$stmt->bindParam(1, $id);
|
||||
$stmt->bindParam(2, $id);
|
||||
$stmt->bindParam(3, $data, \PDO::PARAM_LOB);
|
||||
$stmt->bindValue(4, $lifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(5, $now, \PDO::PARAM_INT);
|
||||
$stmt->bindParam(6, $data, \PDO::PARAM_LOB);
|
||||
$stmt->bindValue(7, $lifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(8, $now, \PDO::PARAM_INT);
|
||||
} else {
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||
$stmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':time', $now, \PDO::PARAM_INT);
|
||||
}
|
||||
if (null === $driver) {
|
||||
$insertStmt = $conn->prepare($insertSql);
|
||||
$insertStmt->bindParam(':id', $id);
|
||||
$insertStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
|
||||
$insertStmt->bindValue(':lifetime', $lifetime, \PDO::PARAM_INT);
|
||||
$insertStmt->bindValue(':time', $now, \PDO::PARAM_INT);
|
||||
}
|
||||
foreach ($values as $id => $data) {
|
||||
try {
|
||||
$stmt->execute();
|
||||
} catch (\PDOException $e) {
|
||||
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], \true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$stmt->execute();
|
||||
}
|
||||
if (null === $driver && !$stmt->rowCount()) {
|
||||
try {
|
||||
$insertStmt->execute();
|
||||
} catch (\PDOException $e) {
|
||||
// A concurrent write won, let it be
|
||||
}
|
||||
}
|
||||
}
|
||||
return $failed;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function getId($key)
|
||||
{
|
||||
if ('pgsql' !== $this->driver ?? ($this->getConnection() ? $this->driver : null)) {
|
||||
return parent::getId($key);
|
||||
}
|
||||
if (\str_contains($key, "\x00") || \str_contains($key, '%') || !\preg_match('//u', $key)) {
|
||||
$key = \rawurlencode($key);
|
||||
}
|
||||
return parent::getId($key);
|
||||
}
|
||||
private function getConnection() : \PDO
|
||||
{
|
||||
if (null === $this->conn) {
|
||||
$this->conn = new \PDO($this->dsn, $this->username, $this->password, $this->connectionOptions);
|
||||
$this->conn->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
if (null === $this->driver) {
|
||||
$this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
}
|
||||
return $this->conn;
|
||||
}
|
||||
private function getServerVersion() : string
|
||||
{
|
||||
if (null === $this->serverVersion) {
|
||||
$this->serverVersion = $this->conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
return $this->serverVersion;
|
||||
}
|
||||
}
|
372
dependencies/symfony/cache/Adapter/PhpArrayAdapter.php
vendored
Normal file
372
dependencies/symfony/cache/Adapter/PhpArrayAdapter.php
vendored
Normal file
@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemPoolInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\VarExporter\VarExporter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* Caches items at warm up time using a PHP array that is stored in shared memory by OPCache since PHP 7.0.
|
||||
* Warmed up items are read-only and run-time discovered items are cached using a fallback adapter.
|
||||
*
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class PhpArrayAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use ContractsTrait;
|
||||
use ProxyTrait;
|
||||
private $file;
|
||||
private $keys;
|
||||
private $values;
|
||||
private static $createCacheItem;
|
||||
private static $valuesCache = [];
|
||||
/**
|
||||
* @param string $file The PHP file were values are cached
|
||||
* @param AdapterInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||
*/
|
||||
public function __construct(string $file, AdapterInterface $fallbackPool)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->pool = $fallbackPool;
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key, $value, $isHit) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->value = $value;
|
||||
$item->isHit = $isHit;
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
||||
*
|
||||
* @param string $file The PHP file were values are cached
|
||||
* @param CacheItemPoolInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||
*
|
||||
* @return CacheItemPoolInterface
|
||||
*/
|
||||
public static function create(string $file, CacheItemPoolInterface $fallbackPool)
|
||||
{
|
||||
if (!$fallbackPool instanceof AdapterInterface) {
|
||||
$fallbackPool = new ProxyAdapter($fallbackPool);
|
||||
}
|
||||
return new static($file, $fallbackPool);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->keys[$key])) {
|
||||
get_from_pool:
|
||||
if ($this->pool instanceof CacheInterface) {
|
||||
return $this->pool->get($key, $callback, $beta, $metadata);
|
||||
}
|
||||
return $this->doGet($this->pool, $key, $callback, $beta, $metadata);
|
||||
}
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
if ('N;' === $value) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if ($value instanceof \Closure) {
|
||||
return $value();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
unset($this->keys[$key]);
|
||||
goto get_from_pool;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key must be string, "%s" given.', \get_debug_type($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->keys[$key])) {
|
||||
return $this->pool->getItem($key);
|
||||
}
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
$isHit = \true;
|
||||
if ('N;' === $value) {
|
||||
$value = null;
|
||||
} elseif ($value instanceof \Closure) {
|
||||
try {
|
||||
$value = $value();
|
||||
} catch (\Throwable $e) {
|
||||
$value = null;
|
||||
$isHit = \false;
|
||||
}
|
||||
}
|
||||
return (self::$createCacheItem)($key, $value, $isHit);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key must be string, "%s" given.', \get_debug_type($key)));
|
||||
}
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
return $this->generateItems($keys);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key must be string, "%s" given.', \get_debug_type($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
return isset($this->keys[$key]) || $this->pool->hasItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key must be string, "%s" given.', \get_debug_type($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
return !isset($this->keys[$key]) && $this->pool->deleteItem($key);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
$deleted = \true;
|
||||
$fallbackKeys = [];
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key must be string, "%s" given.', \get_debug_type($key)));
|
||||
}
|
||||
if (isset($this->keys[$key])) {
|
||||
$deleted = \false;
|
||||
} else {
|
||||
$fallbackKeys[] = $key;
|
||||
}
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
if ($fallbackKeys) {
|
||||
$deleted = $this->pool->deleteItems($fallbackKeys) && $deleted;
|
||||
}
|
||||
return $deleted;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
return !isset($this->keys[$item->getKey()]) && $this->pool->save($item);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
return !isset($this->keys[$item->getKey()]) && $this->pool->saveDeferred($item);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
return $this->pool->commit();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
$this->keys = $this->values = [];
|
||||
$cleared = @\unlink($this->file) || !\file_exists($this->file);
|
||||
unset(self::$valuesCache[$this->file]);
|
||||
if ($this->pool instanceof AdapterInterface) {
|
||||
return $this->pool->clear($prefix) && $cleared;
|
||||
}
|
||||
return $this->pool->clear() && $cleared;
|
||||
}
|
||||
/**
|
||||
* Store an array of cached values.
|
||||
*
|
||||
* @param array $values The cached values
|
||||
*
|
||||
* @return string[] A list of classes to preload on PHP 7.4+
|
||||
*/
|
||||
public function warmUp(array $values)
|
||||
{
|
||||
if (\file_exists($this->file)) {
|
||||
if (!\is_file($this->file)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache path exists and is not a file: "%s".', $this->file));
|
||||
}
|
||||
if (!\is_writable($this->file)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache file is not writable: "%s".', $this->file));
|
||||
}
|
||||
} else {
|
||||
$directory = \dirname($this->file);
|
||||
if (!\is_dir($directory) && !@\mkdir($directory, 0777, \true)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache directory does not exist and cannot be created: "%s".', $directory));
|
||||
}
|
||||
if (!\is_writable($directory)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache directory is not writable: "%s".', $directory));
|
||||
}
|
||||
}
|
||||
$preload = [];
|
||||
$dumpedValues = '';
|
||||
$dumpedMap = [];
|
||||
$dump = <<<'EOF'
|
||||
<?php
|
||||
|
||||
// This file has been auto-generated by the Symfony Cache Component.
|
||||
|
||||
return [[
|
||||
|
||||
|
||||
EOF;
|
||||
foreach ($values as $key => $value) {
|
||||
CacheItem::validateKey(\is_int($key) ? (string) $key : $key);
|
||||
$isStaticValue = \true;
|
||||
if (null === $value) {
|
||||
$value = "'N;'";
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$value = VarExporter::export($value, $isStaticValue, $preload);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \get_debug_type($value)), 0, $e);
|
||||
}
|
||||
} elseif (\is_string($value)) {
|
||||
// Wrap "N;" in a closure to not confuse it with an encoded `null`
|
||||
if ('N;' === $value) {
|
||||
$isStaticValue = \false;
|
||||
}
|
||||
$value = \var_export($value, \true);
|
||||
} elseif (!\is_scalar($value)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \get_debug_type($value)));
|
||||
} else {
|
||||
$value = \var_export($value, \true);
|
||||
}
|
||||
if (!$isStaticValue) {
|
||||
$value = \str_replace("\n", "\n ", $value);
|
||||
$value = "static function () {\n return {$value};\n}";
|
||||
}
|
||||
$hash = \hash('md5', $value);
|
||||
if (null === ($id = $dumpedMap[$hash] ?? null)) {
|
||||
$id = $dumpedMap[$hash] = \count($dumpedMap);
|
||||
$dumpedValues .= "{$id} => {$value},\n";
|
||||
}
|
||||
$dump .= \var_export($key, \true) . " => {$id},\n";
|
||||
}
|
||||
$dump .= "\n], [\n\n{$dumpedValues}\n]];\n";
|
||||
$tmpFile = \uniqid($this->file, \true);
|
||||
\file_put_contents($tmpFile, $dump);
|
||||
@\chmod($tmpFile, 0666 & ~\umask());
|
||||
unset($serialized, $value, $dump);
|
||||
@\rename($tmpFile, $this->file);
|
||||
unset(self::$valuesCache[$this->file]);
|
||||
$this->initialize();
|
||||
return $preload;
|
||||
}
|
||||
/**
|
||||
* Load the cache file.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
if (isset(self::$valuesCache[$this->file])) {
|
||||
$values = self::$valuesCache[$this->file];
|
||||
} elseif (!\is_file($this->file)) {
|
||||
$this->keys = $this->values = [];
|
||||
return;
|
||||
} else {
|
||||
$values = self::$valuesCache[$this->file] = (include $this->file) ?: [[], []];
|
||||
}
|
||||
if (2 !== \count($values) || !isset($values[0], $values[1])) {
|
||||
$this->keys = $this->values = [];
|
||||
} else {
|
||||
[$this->keys, $this->values] = $values;
|
||||
}
|
||||
}
|
||||
private function generateItems(array $keys) : \Generator
|
||||
{
|
||||
$f = self::$createCacheItem;
|
||||
$fallbackKeys = [];
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->keys[$key])) {
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
if ('N;' === $value) {
|
||||
(yield $key => $f($key, null, \true));
|
||||
} elseif ($value instanceof \Closure) {
|
||||
try {
|
||||
(yield $key => $f($key, $value(), \true));
|
||||
} catch (\Throwable $e) {
|
||||
(yield $key => $f($key, null, \false));
|
||||
}
|
||||
} else {
|
||||
(yield $key => $f($key, $value, \true));
|
||||
}
|
||||
} else {
|
||||
$fallbackKeys[] = $key;
|
||||
}
|
||||
}
|
||||
if ($fallbackKeys) {
|
||||
yield from $this->pool->getItems($fallbackKeys);
|
||||
}
|
||||
}
|
||||
}
|
284
dependencies/symfony/cache/Adapter/PhpFilesAdapter.php
vendored
Normal file
284
dependencies/symfony/cache/Adapter/PhpFilesAdapter.php
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\FilesystemCommonTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\VarExporter\VarExporter;
|
||||
/**
|
||||
* @author Piotr Stankowski <git@trakos.pl>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
*/
|
||||
class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
use FilesystemCommonTrait {
|
||||
doClear as private doCommonClear;
|
||||
doDelete as private doCommonDelete;
|
||||
}
|
||||
private $includeHandler;
|
||||
private $appendOnly;
|
||||
private $values = [];
|
||||
private $files = [];
|
||||
private static $startTime;
|
||||
private static $valuesCache = [];
|
||||
/**
|
||||
* @param $appendOnly Set to `true` to gain extra performance when the items stored in this pool never expire.
|
||||
* Doing so is encouraged because it fits perfectly OPcache's memory model.
|
||||
*
|
||||
* @throws CacheException if OPcache is not enabled
|
||||
*/
|
||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $directory = null, bool $appendOnly = \false)
|
||||
{
|
||||
$this->appendOnly = $appendOnly;
|
||||
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? \time();
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->init($namespace, $directory);
|
||||
$this->includeHandler = static function ($type, $msg, $file, $line) {
|
||||
throw new \ErrorException($msg, 0, $type, $file, $line);
|
||||
};
|
||||
}
|
||||
public static function isSupported()
|
||||
{
|
||||
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? \time();
|
||||
return \function_exists('opcache_invalidate') && \filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true) || \filter_var(\ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN));
|
||||
}
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
$time = \time();
|
||||
$pruned = \true;
|
||||
$getExpiry = \true;
|
||||
\set_error_handler($this->includeHandler);
|
||||
try {
|
||||
foreach ($this->scanHashDir($this->directory) as $file) {
|
||||
try {
|
||||
if (\is_array($expiresAt = (include $file))) {
|
||||
$expiresAt = $expiresAt[0];
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
$expiresAt = $time;
|
||||
}
|
||||
if ($time >= $expiresAt) {
|
||||
$pruned = ($this->doUnlink($file) || !\file_exists($file)) && $pruned;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
return $pruned;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
if ($this->appendOnly) {
|
||||
$now = 0;
|
||||
$missingIds = [];
|
||||
} else {
|
||||
$now = \time();
|
||||
$missingIds = $ids;
|
||||
$ids = [];
|
||||
}
|
||||
$values = [];
|
||||
begin:
|
||||
$getExpiry = \false;
|
||||
foreach ($ids as $id) {
|
||||
if (null === ($value = $this->values[$id] ?? null)) {
|
||||
$missingIds[] = $id;
|
||||
} elseif ('N;' === $value) {
|
||||
$values[$id] = null;
|
||||
} elseif (!\is_object($value)) {
|
||||
$values[$id] = $value;
|
||||
} elseif (!$value instanceof LazyValue) {
|
||||
$values[$id] = $value();
|
||||
} elseif (\false === ($values[$id] = (include $value->file))) {
|
||||
unset($values[$id], $this->values[$id]);
|
||||
$missingIds[] = $id;
|
||||
}
|
||||
if (!$this->appendOnly) {
|
||||
unset($this->values[$id]);
|
||||
}
|
||||
}
|
||||
if (!$missingIds) {
|
||||
return $values;
|
||||
}
|
||||
\set_error_handler($this->includeHandler);
|
||||
try {
|
||||
$getExpiry = \true;
|
||||
foreach ($missingIds as $k => $id) {
|
||||
try {
|
||||
$file = $this->files[$id] ?? ($this->files[$id] = $this->getFile($id));
|
||||
if (isset(self::$valuesCache[$file])) {
|
||||
[$expiresAt, $this->values[$id]] = self::$valuesCache[$file];
|
||||
} elseif (\is_array($expiresAt = (include $file))) {
|
||||
if ($this->appendOnly) {
|
||||
self::$valuesCache[$file] = $expiresAt;
|
||||
}
|
||||
[$expiresAt, $this->values[$id]] = $expiresAt;
|
||||
} elseif ($now < $expiresAt) {
|
||||
$this->values[$id] = new LazyValue($file);
|
||||
}
|
||||
if ($now >= $expiresAt) {
|
||||
unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
unset($missingIds[$k]);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
$ids = $missingIds;
|
||||
$missingIds = [];
|
||||
goto begin;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
if ($this->appendOnly && isset($this->values[$id])) {
|
||||
return \true;
|
||||
}
|
||||
\set_error_handler($this->includeHandler);
|
||||
try {
|
||||
$file = $this->files[$id] ?? ($this->files[$id] = $this->getFile($id));
|
||||
$getExpiry = \true;
|
||||
if (isset(self::$valuesCache[$file])) {
|
||||
[$expiresAt, $value] = self::$valuesCache[$file];
|
||||
} elseif (\is_array($expiresAt = (include $file))) {
|
||||
if ($this->appendOnly) {
|
||||
self::$valuesCache[$file] = $expiresAt;
|
||||
}
|
||||
[$expiresAt, $value] = $expiresAt;
|
||||
} elseif ($this->appendOnly) {
|
||||
$value = new LazyValue($file);
|
||||
}
|
||||
} catch (\ErrorException $e) {
|
||||
return \false;
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
if ($this->appendOnly) {
|
||||
$now = 0;
|
||||
$this->values[$id] = $value;
|
||||
} else {
|
||||
$now = \time();
|
||||
}
|
||||
return $now < $expiresAt;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
$ok = \true;
|
||||
$expiry = $lifetime ? \time() + $lifetime : 'PHP_INT_MAX';
|
||||
$allowCompile = self::isSupported();
|
||||
foreach ($values as $key => $value) {
|
||||
unset($this->values[$key]);
|
||||
$isStaticValue = \true;
|
||||
if (null === $value) {
|
||||
$value = "'N;'";
|
||||
} elseif (\is_object($value) || \is_array($value)) {
|
||||
try {
|
||||
$value = VarExporter::export($value, $isStaticValue);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \get_debug_type($value)), 0, $e);
|
||||
}
|
||||
} elseif (\is_string($value)) {
|
||||
// Wrap "N;" in a closure to not confuse it with an encoded `null`
|
||||
if ('N;' === $value) {
|
||||
$isStaticValue = \false;
|
||||
}
|
||||
$value = \var_export($value, \true);
|
||||
} elseif (!\is_scalar($value)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \get_debug_type($value)));
|
||||
} else {
|
||||
$value = \var_export($value, \true);
|
||||
}
|
||||
$encodedKey = \rawurlencode($key);
|
||||
if ($isStaticValue) {
|
||||
$value = "return [{$expiry}, {$value}];";
|
||||
} elseif ($this->appendOnly) {
|
||||
$value = "return [{$expiry}, static function () { return {$value}; }];";
|
||||
} else {
|
||||
// We cannot use a closure here because of https://bugs.php.net/76982
|
||||
$value = \str_replace('\\Symfony\\Component\\VarExporter\\Internal\\', '', $value);
|
||||
$value = "namespace Symfony\\Component\\VarExporter\\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
|
||||
}
|
||||
$file = $this->files[$key] = $this->getFile($key, \true);
|
||||
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
||||
$ok = $this->write($file, "<?php //{$encodedKey}\n\n{$value}\n", self::$startTime - 10) && $ok;
|
||||
if ($allowCompile) {
|
||||
@\opcache_invalidate($file, \true);
|
||||
@\opcache_compile_file($file);
|
||||
}
|
||||
unset(self::$valuesCache[$file]);
|
||||
}
|
||||
if (!$ok && !\is_writable($this->directory)) {
|
||||
throw new CacheException(\sprintf('Cache directory is not writable (%s).', $this->directory));
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
$this->values = [];
|
||||
return $this->doCommonClear($namespace);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
foreach ($ids as $id) {
|
||||
unset($this->values[$id]);
|
||||
}
|
||||
return $this->doCommonDelete($ids);
|
||||
}
|
||||
protected function doUnlink(string $file)
|
||||
{
|
||||
unset(self::$valuesCache[$file]);
|
||||
if (self::isSupported()) {
|
||||
@\opcache_invalidate($file, \true);
|
||||
}
|
||||
return @\unlink($file);
|
||||
}
|
||||
private function getFileKey(string $file) : string
|
||||
{
|
||||
if (!($h = @\fopen($file, 'r'))) {
|
||||
return '';
|
||||
}
|
||||
$encodedKey = \substr(\fgets($h), 8);
|
||||
\fclose($h);
|
||||
return \rawurldecode(\rtrim($encodedKey));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class LazyValue
|
||||
{
|
||||
public $file;
|
||||
public function __construct(string $file)
|
||||
{
|
||||
$this->file = $file;
|
||||
}
|
||||
}
|
230
dependencies/symfony/cache/Adapter/ProxyAdapter.php
vendored
Normal file
230
dependencies/symfony/cache/Adapter/ProxyAdapter.php
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemPoolInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ProxyAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use ContractsTrait;
|
||||
use ProxyTrait;
|
||||
private $namespace = '';
|
||||
private $namespaceLen;
|
||||
private $poolHash;
|
||||
private $defaultLifetime;
|
||||
private static $createCacheItem;
|
||||
private static $setInnerItem;
|
||||
public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
$this->poolHash = $poolHash = \spl_object_hash($pool);
|
||||
if ('' !== $namespace) {
|
||||
\assert('' !== CacheItem::validateKey($namespace));
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
$this->namespaceLen = \strlen($namespace);
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key, $innerItem, $poolHash) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
if (null === $innerItem) {
|
||||
return $item;
|
||||
}
|
||||
$item->value = $v = $innerItem->get();
|
||||
$item->isHit = $innerItem->isHit();
|
||||
$item->innerItem = $innerItem;
|
||||
$item->poolHash = $poolHash;
|
||||
// Detect wrapped values that encode for their expiry and creation duration
|
||||
// For compactness, these values are packed in the key of an array using
|
||||
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||
if (\is_array($v) && 1 === \count($v) && 10 === \strlen($k = (string) \array_key_first($v)) && "\x9d" === $k[0] && "\x00" === $k[5] && "_" === $k[9]) {
|
||||
$item->value = $v[$k];
|
||||
$v = \unpack('Ve/Nc', \substr($k, 1, -1));
|
||||
$item->metadata[CacheItem::METADATA_EXPIRY] = $v['e'] + CacheItem::METADATA_EXPIRY_OFFSET;
|
||||
$item->metadata[CacheItem::METADATA_CTIME] = $v['c'];
|
||||
} elseif ($innerItem instanceof CacheItem) {
|
||||
$item->metadata = $innerItem->metadata;
|
||||
}
|
||||
$innerItem->set(null);
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
self::$setInnerItem ?? (self::$setInnerItem = \Closure::bind(
|
||||
/**
|
||||
* @param array $item A CacheItem cast to (array); accessing protected properties requires adding the "\0*\0" PHP prefix
|
||||
*/
|
||||
static function (CacheItemInterface $innerItem, array $item) {
|
||||
// Tags are stored separately, no need to account for them when considering this item's newly set metadata
|
||||
if (isset(($metadata = $item["\x00*\x00newMetadata"])[CacheItem::METADATA_TAGS])) {
|
||||
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||
}
|
||||
if ($metadata) {
|
||||
// For compactness, expiry and creation duration are packed in the key of an array, using magic numbers as separators
|
||||
$item["\x00*\x00value"] = ["\x9d" . \pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME]) . "_" => $item["\x00*\x00value"]];
|
||||
}
|
||||
$innerItem->set($item["\x00*\x00value"]);
|
||||
$innerItem->expiresAt(null !== $item["\x00*\x00expiry"] ? \DateTime::createFromFormat('U.u', \sprintf('%.6F', $item["\x00*\x00expiry"])) : null);
|
||||
},
|
||||
null,
|
||||
CacheItem::class
|
||||
));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
if (!$this->pool instanceof CacheInterface) {
|
||||
return $this->doGet($this, $key, $callback, $beta, $metadata);
|
||||
}
|
||||
return $this->pool->get($this->getId($key), function ($innerItem, bool &$save) use($key, $callback) {
|
||||
$item = (self::$createCacheItem)($key, $innerItem, $this->poolHash);
|
||||
$item->set($value = $callback($item, $save));
|
||||
(self::$setInnerItem)($innerItem, (array) $item);
|
||||
return $value;
|
||||
}, $beta, $metadata);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$item = $this->pool->getItem($this->getId($key));
|
||||
return (self::$createCacheItem)($key, $item, $this->poolHash);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
if ($this->namespaceLen) {
|
||||
foreach ($keys as $i => $key) {
|
||||
$keys[$i] = $this->getId($key);
|
||||
}
|
||||
}
|
||||
return $this->generateItems($this->pool->getItems($keys));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
return $this->pool->hasItem($this->getId($key));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
if ($this->pool instanceof AdapterInterface) {
|
||||
return $this->pool->clear($this->namespace . $prefix);
|
||||
}
|
||||
return $this->pool->clear();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
return $this->pool->deleteItem($this->getId($key));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
if ($this->namespaceLen) {
|
||||
foreach ($keys as $i => $key) {
|
||||
$keys[$i] = $this->getId($key);
|
||||
}
|
||||
}
|
||||
return $this->pool->deleteItems($keys);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
return $this->doSave($item, __FUNCTION__);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
return $this->doSave($item, __FUNCTION__);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
return $this->pool->commit();
|
||||
}
|
||||
private function doSave(CacheItemInterface $item, string $method)
|
||||
{
|
||||
if (!$item instanceof CacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$item = (array) $item;
|
||||
if (null === $item["\x00*\x00expiry"] && 0 < $this->defaultLifetime) {
|
||||
$item["\x00*\x00expiry"] = \microtime(\true) + $this->defaultLifetime;
|
||||
}
|
||||
if ($item["\x00*\x00poolHash"] === $this->poolHash && $item["\x00*\x00innerItem"]) {
|
||||
$innerItem = $item["\x00*\x00innerItem"];
|
||||
} elseif ($this->pool instanceof AdapterInterface) {
|
||||
// this is an optimization specific for AdapterInterface implementations
|
||||
// so we can save a round-trip to the backend by just creating a new item
|
||||
$innerItem = (self::$createCacheItem)($this->namespace . $item["\x00*\x00key"], null, $this->poolHash);
|
||||
} else {
|
||||
$innerItem = $this->pool->getItem($this->namespace . $item["\x00*\x00key"]);
|
||||
}
|
||||
(self::$setInnerItem)($innerItem, $item);
|
||||
return $this->pool->{$method}($innerItem);
|
||||
}
|
||||
private function generateItems(iterable $items) : \Generator
|
||||
{
|
||||
$f = self::$createCacheItem;
|
||||
foreach ($items as $key => $item) {
|
||||
if ($this->namespaceLen) {
|
||||
$key = \substr($key, $this->namespaceLen);
|
||||
}
|
||||
(yield $key => $f($key, $item, $this->poolHash));
|
||||
}
|
||||
}
|
||||
private function getId($key) : string
|
||||
{
|
||||
\assert('' !== CacheItem::validateKey($key));
|
||||
return $this->namespace . $key;
|
||||
}
|
||||
}
|
75
dependencies/symfony/cache/Adapter/Psr16Adapter.php
vendored
Normal file
75
dependencies/symfony/cache/Adapter/Psr16Adapter.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\SimpleCache\CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
/**
|
||||
* Turns a PSR-16 cache into a PSR-6 one.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class Psr16Adapter extends AbstractAdapter implements PruneableInterface, ResettableInterface
|
||||
{
|
||||
use ProxyTrait;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected const NS_SEPARATOR = '_';
|
||||
private $miss;
|
||||
public function __construct(CacheInterface $pool, string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
$this->pool = $pool;
|
||||
$this->miss = new \stdClass();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
foreach ($this->pool->getMultiple($ids, $this->miss) as $key => $value) {
|
||||
if ($this->miss !== $value) {
|
||||
(yield $key => $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
return $this->pool->has($id);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
return $this->pool->deleteMultiple($ids);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
|
||||
}
|
||||
}
|
29
dependencies/symfony/cache/Adapter/RedisAdapter.php
vendored
Normal file
29
dependencies/symfony/cache/Adapter/RedisAdapter.php
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\RedisProxy;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\RedisTrait;
|
||||
class RedisAdapter extends AbstractAdapter
|
||||
{
|
||||
use RedisTrait;
|
||||
/**
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client
|
||||
* @param string $namespace The default namespace
|
||||
* @param int $defaultLifetime The default lifetime
|
||||
*/
|
||||
public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
$this->init($redis, $namespace, $defaultLifetime, $marshaller);
|
||||
}
|
||||
}
|
278
dependencies/symfony/cache/Adapter/RedisTagAwareAdapter.php
vendored
Normal file
278
dependencies/symfony/cache/Adapter/RedisTagAwareAdapter.php
vendored
Normal file
@ -0,0 +1,278 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Predis\Connection\Aggregate\ClusterInterface;
|
||||
use WP_Ultimo\Dependencies\Predis\Connection\Aggregate\PredisCluster;
|
||||
use WP_Ultimo\Dependencies\Predis\Connection\Aggregate\ReplicationInterface;
|
||||
use WP_Ultimo\Dependencies\Predis\Response\ErrorInterface;
|
||||
use WP_Ultimo\Dependencies\Predis\Response\Status;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\LogicException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DeflateMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\RedisProxy;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\RedisTrait;
|
||||
/**
|
||||
* Stores tag id <> cache id relationship as a Redis Set.
|
||||
*
|
||||
* Set (tag relation info) is stored without expiry (non-volatile), while cache always gets an expiry (volatile) even
|
||||
* if not set by caller. Thus if you configure redis with the right eviction policy you can be safe this tag <> cache
|
||||
* relationship survives eviction (cache cleanup when Redis runs out of memory).
|
||||
*
|
||||
* Redis server 2.8+ with any `volatile-*` eviction policy, OR `noeviction` if you're sure memory will NEVER fill up
|
||||
*
|
||||
* Design limitations:
|
||||
* - Max 4 billion cache keys per cache tag as limited by Redis Set datatype.
|
||||
* E.g. If you use a "all" items tag for expiry instead of clear(), that limits you to 4 billion cache items also.
|
||||
*
|
||||
* @see https://redis.io/topics/lru-cache#eviction-policies Documentation for Redis eviction policies.
|
||||
* @see https://redis.io/topics/data-types#sets Documentation for Redis Set datatype.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author André Rømcke <andre.romcke+symfony@gmail.com>
|
||||
*/
|
||||
class RedisTagAwareAdapter extends AbstractTagAwareAdapter
|
||||
{
|
||||
use RedisTrait;
|
||||
/**
|
||||
* On cache items without a lifetime set, we set it to 100 days. This is to make sure cache items are
|
||||
* preferred to be evicted over tag Sets, if eviction policy is configured according to requirements.
|
||||
*/
|
||||
private const DEFAULT_CACHE_TTL = 8640000;
|
||||
/**
|
||||
* @var string|null detected eviction policy used on Redis server
|
||||
*/
|
||||
private $redisEvictionPolicy;
|
||||
private $namespace;
|
||||
/**
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis The redis client
|
||||
* @param string $namespace The default namespace
|
||||
* @param int $defaultLifetime The default lifetime
|
||||
*/
|
||||
public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if ($redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && $redis->getConnection() instanceof ClusterInterface && !$redis->getConnection() instanceof PredisCluster) {
|
||||
throw new InvalidArgumentException(\sprintf('Unsupported Predis cluster connection: only "%s" is, "%s" given.', PredisCluster::class, \get_debug_type($redis->getConnection())));
|
||||
}
|
||||
if (\defined('Redis::OPT_COMPRESSION') && ($redis instanceof \Redis || $redis instanceof \RedisArray || $redis instanceof \RedisCluster)) {
|
||||
$compression = $redis->getOption(\Redis::OPT_COMPRESSION);
|
||||
foreach (\is_array($compression) ? $compression : [$compression] as $c) {
|
||||
if (\Redis::COMPRESSION_NONE !== $c) {
|
||||
throw new InvalidArgumentException(\sprintf('phpredis compression must be disabled when using "%s", use "%s" instead.', static::class, DeflateMarshaller::class));
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->init($redis, $namespace, $defaultLifetime, new TagAwareMarshaller($marshaller));
|
||||
$this->namespace = $namespace;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime, array $addTagData = [], array $delTagData = []) : array
|
||||
{
|
||||
$eviction = $this->getRedisEvictionPolicy();
|
||||
if ('noeviction' !== $eviction && !\str_starts_with($eviction, 'volatile-')) {
|
||||
throw new LogicException(\sprintf('Redis maxmemory-policy setting "%s" is *not* supported by RedisTagAwareAdapter, use "noeviction" or "volatile-*" eviction policies.', $eviction));
|
||||
}
|
||||
// serialize values
|
||||
if (!($serialized = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
// While pipeline isn't supported on RedisCluster, other setups will at least benefit from doing this in one op
|
||||
$results = $this->pipeline(static function () use($serialized, $lifetime, $addTagData, $delTagData, $failed) {
|
||||
// Store cache items, force a ttl if none is set, as there is no MSETEX we need to set each one
|
||||
foreach ($serialized as $id => $value) {
|
||||
(yield 'setEx' => [$id, 0 >= $lifetime ? self::DEFAULT_CACHE_TTL : $lifetime, $value]);
|
||||
}
|
||||
// Add and Remove Tags
|
||||
foreach ($addTagData as $tagId => $ids) {
|
||||
if (!$failed || ($ids = \array_diff($ids, $failed))) {
|
||||
(yield 'sAdd' => \array_merge([$tagId], $ids));
|
||||
}
|
||||
}
|
||||
foreach ($delTagData as $tagId => $ids) {
|
||||
if (!$failed || ($ids = \array_diff($ids, $failed))) {
|
||||
(yield 'sRem' => \array_merge([$tagId], $ids));
|
||||
}
|
||||
}
|
||||
});
|
||||
foreach ($results as $id => $result) {
|
||||
// Skip results of SADD/SREM operations, they'll be 1 or 0 depending on if set value already existed or not
|
||||
if (\is_numeric($result)) {
|
||||
continue;
|
||||
}
|
||||
// setEx results
|
||||
if (\true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) {
|
||||
$failed[] = $id;
|
||||
}
|
||||
}
|
||||
return $failed;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteYieldTags(array $ids) : iterable
|
||||
{
|
||||
$lua = <<<'EOLUA'
|
||||
local v = redis.call('GET', KEYS[1])
|
||||
local e = redis.pcall('UNLINK', KEYS[1])
|
||||
|
||||
if type(e) ~= 'number' then
|
||||
redis.call('DEL', KEYS[1])
|
||||
end
|
||||
|
||||
if not v or v:len() <= 13 or v:byte(1) ~= 0x9D or v:byte(6) ~= 0 or v:byte(10) ~= 0x5F then
|
||||
return ''
|
||||
end
|
||||
|
||||
return v:sub(14, 13 + v:byte(13) + v:byte(12) * 256 + v:byte(11) * 65536)
|
||||
EOLUA;
|
||||
$results = $this->pipeline(function () use($ids, $lua) {
|
||||
foreach ($ids as $id) {
|
||||
(yield 'eval' => $this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface ? [$lua, 1, $id] : [$lua, [$id], 1]);
|
||||
}
|
||||
});
|
||||
foreach ($results as $id => $result) {
|
||||
if ($result instanceof \RedisException || $result instanceof ErrorInterface) {
|
||||
CacheItem::log($this->logger, 'Failed to delete key "{key}": ' . $result->getMessage(), ['key' => \substr($id, \strlen($this->namespace)), 'exception' => $result]);
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
(yield $id => !\is_string($result) || '' === $result ? [] : $this->marshaller->unmarshall($result));
|
||||
} catch (\Exception $e) {
|
||||
(yield $id => []);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDeleteTagRelations(array $tagData) : bool
|
||||
{
|
||||
$results = $this->pipeline(static function () use($tagData) {
|
||||
foreach ($tagData as $tagId => $idList) {
|
||||
\array_unshift($idList, $tagId);
|
||||
(yield 'sRem' => $idList);
|
||||
}
|
||||
});
|
||||
foreach ($results as $result) {
|
||||
// no-op
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doInvalidate(array $tagIds) : bool
|
||||
{
|
||||
// This script scans the set of items linked to tag: it empties the set
|
||||
// and removes the linked items. When the set is still not empty after
|
||||
// the scan, it means we're in cluster mode and that the linked items
|
||||
// are on other nodes: we move the links to a temporary set and we
|
||||
// garbage collect that set from the client side.
|
||||
$lua = <<<'EOLUA'
|
||||
redis.replicate_commands()
|
||||
|
||||
local cursor = '0'
|
||||
local id = KEYS[1]
|
||||
repeat
|
||||
local result = redis.call('SSCAN', id, cursor, 'COUNT', 5000);
|
||||
cursor = result[1];
|
||||
local rems = {}
|
||||
|
||||
for _, v in ipairs(result[2]) do
|
||||
local ok, _ = pcall(redis.call, 'DEL', ARGV[1]..v)
|
||||
if ok then
|
||||
table.insert(rems, v)
|
||||
end
|
||||
end
|
||||
if 0 < #rems then
|
||||
redis.call('SREM', id, unpack(rems))
|
||||
end
|
||||
until '0' == cursor;
|
||||
|
||||
redis.call('SUNIONSTORE', '{'..id..'}'..id, id)
|
||||
redis.call('DEL', id)
|
||||
|
||||
return redis.call('SSCAN', '{'..id..'}'..id, '0', 'COUNT', 5000)
|
||||
EOLUA;
|
||||
$results = $this->pipeline(function () use($tagIds, $lua) {
|
||||
if ($this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface) {
|
||||
$prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : '';
|
||||
} elseif (\is_array($prefix = $this->redis->getOption(\Redis::OPT_PREFIX) ?? '')) {
|
||||
$prefix = \current($prefix);
|
||||
}
|
||||
foreach ($tagIds as $id) {
|
||||
(yield 'eval' => $this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface ? [$lua, 1, $id, $prefix] : [$lua, [$id, $prefix], 1]);
|
||||
}
|
||||
});
|
||||
$lua = <<<'EOLUA'
|
||||
redis.replicate_commands()
|
||||
|
||||
local id = KEYS[1]
|
||||
local cursor = table.remove(ARGV)
|
||||
redis.call('SREM', '{'..id..'}'..id, unpack(ARGV))
|
||||
|
||||
return redis.call('SSCAN', '{'..id..'}'..id, cursor, 'COUNT', 5000)
|
||||
EOLUA;
|
||||
$success = \true;
|
||||
foreach ($results as $id => $values) {
|
||||
if ($values instanceof \RedisException || $values instanceof ErrorInterface) {
|
||||
CacheItem::log($this->logger, 'Failed to invalidate key "{key}": ' . $values->getMessage(), ['key' => \substr($id, \strlen($this->namespace)), 'exception' => $values]);
|
||||
$success = \false;
|
||||
continue;
|
||||
}
|
||||
[$cursor, $ids] = $values;
|
||||
while ($ids || '0' !== $cursor) {
|
||||
$this->doDelete($ids);
|
||||
$evalArgs = [$id, $cursor];
|
||||
\array_splice($evalArgs, 1, 0, $ids);
|
||||
if ($this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface) {
|
||||
\array_unshift($evalArgs, $lua, 1);
|
||||
} else {
|
||||
$evalArgs = [$lua, $evalArgs, 1];
|
||||
}
|
||||
$results = $this->pipeline(function () use($evalArgs) {
|
||||
(yield 'eval' => $evalArgs);
|
||||
});
|
||||
foreach ($results as [$cursor, $ids]) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
private function getRedisEvictionPolicy() : string
|
||||
{
|
||||
if (null !== $this->redisEvictionPolicy) {
|
||||
return $this->redisEvictionPolicy;
|
||||
}
|
||||
$hosts = $this->getHosts();
|
||||
$host = \reset($hosts);
|
||||
if ($host instanceof \WP_Ultimo\Dependencies\Predis\Client && $host->getConnection() instanceof ReplicationInterface) {
|
||||
// Predis supports info command only on the master in replication environments
|
||||
$hosts = [$host->getClientFor('master')];
|
||||
}
|
||||
foreach ($hosts as $host) {
|
||||
$info = $host->info('Memory');
|
||||
if ($info instanceof ErrorInterface) {
|
||||
continue;
|
||||
}
|
||||
$info = $info['Memory'] ?? $info;
|
||||
return $this->redisEvictionPolicy = $info['maxmemory_policy'];
|
||||
}
|
||||
return $this->redisEvictionPolicy = '';
|
||||
}
|
||||
}
|
350
dependencies/symfony/cache/Adapter/TagAwareAdapter.php
vendored
Normal file
350
dependencies/symfony/cache/Adapter/TagAwareAdapter.php
vendored
Normal file
@ -0,0 +1,350 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TagAwareAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface, PruneableInterface, ResettableInterface, LoggerAwareInterface
|
||||
{
|
||||
use ContractsTrait;
|
||||
use LoggerAwareTrait;
|
||||
use ProxyTrait;
|
||||
public const TAGS_PREFIX = "\x00tags\x00";
|
||||
private $deferred = [];
|
||||
private $tags;
|
||||
private $knownTagVersions = [];
|
||||
private $knownTagVersionsTtl;
|
||||
private static $createCacheItem;
|
||||
private static $setCacheItemTags;
|
||||
private static $getTagsByKey;
|
||||
private static $saveTags;
|
||||
public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15)
|
||||
{
|
||||
$this->pool = $itemsPool;
|
||||
$this->tags = $tagsPool ?: $itemsPool;
|
||||
$this->knownTagVersionsTtl = $knownTagVersionsTtl;
|
||||
self::$createCacheItem ?? (self::$createCacheItem = \Closure::bind(static function ($key, $value, CacheItem $protoItem) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->value = $value;
|
||||
$item->expiry = $protoItem->expiry;
|
||||
$item->poolHash = $protoItem->poolHash;
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
self::$setCacheItemTags ?? (self::$setCacheItemTags = \Closure::bind(static function (CacheItem $item, $key, array &$itemTags) {
|
||||
$item->isTaggable = \true;
|
||||
if (!$item->isHit) {
|
||||
return $item;
|
||||
}
|
||||
if (isset($itemTags[$key])) {
|
||||
foreach ($itemTags[$key] as $tag => $version) {
|
||||
$item->metadata[CacheItem::METADATA_TAGS][$tag] = $tag;
|
||||
}
|
||||
unset($itemTags[$key]);
|
||||
} else {
|
||||
$item->value = null;
|
||||
$item->isHit = \false;
|
||||
}
|
||||
return $item;
|
||||
}, null, CacheItem::class));
|
||||
self::$getTagsByKey ?? (self::$getTagsByKey = \Closure::bind(static function ($deferred) {
|
||||
$tagsByKey = [];
|
||||
foreach ($deferred as $key => $item) {
|
||||
$tagsByKey[$key] = $item->newMetadata[CacheItem::METADATA_TAGS] ?? [];
|
||||
$item->metadata = $item->newMetadata;
|
||||
}
|
||||
return $tagsByKey;
|
||||
}, null, CacheItem::class));
|
||||
self::$saveTags ?? (self::$saveTags = \Closure::bind(static function (AdapterInterface $tagsAdapter, array $tags) {
|
||||
\ksort($tags);
|
||||
foreach ($tags as $v) {
|
||||
$v->expiry = 0;
|
||||
$tagsAdapter->saveDeferred($v);
|
||||
}
|
||||
return $tagsAdapter->commit();
|
||||
}, null, CacheItem::class));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags)
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($tags as $tag) {
|
||||
\assert('' !== CacheItem::validateKey($tag));
|
||||
unset($this->knownTagVersions[$tag]);
|
||||
$ids[] = $tag . static::TAGS_PREFIX;
|
||||
}
|
||||
return !$tags || $this->tags->deleteItems($ids);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
if (\is_string($key) && isset($this->deferred[$key])) {
|
||||
$this->commit();
|
||||
}
|
||||
if (!$this->pool->hasItem($key)) {
|
||||
return \false;
|
||||
}
|
||||
$itemTags = $this->pool->getItem(static::TAGS_PREFIX . $key);
|
||||
if (!$itemTags->isHit()) {
|
||||
return \false;
|
||||
}
|
||||
if (!($itemTags = $itemTags->get())) {
|
||||
return \true;
|
||||
}
|
||||
foreach ($this->getTagVersions([$itemTags]) as $tag => $version) {
|
||||
if ($itemTags[$tag] !== $version) {
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
foreach ($this->getItems([$key]) as $item) {
|
||||
return $item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
$tagKeys = [];
|
||||
$commit = \false;
|
||||
foreach ($keys as $key) {
|
||||
if ('' !== $key && \is_string($key)) {
|
||||
$commit = $commit || isset($this->deferred[$key]);
|
||||
$key = static::TAGS_PREFIX . $key;
|
||||
$tagKeys[$key] = $key;
|
||||
}
|
||||
}
|
||||
if ($commit) {
|
||||
$this->commit();
|
||||
}
|
||||
try {
|
||||
$items = $this->pool->getItems($tagKeys + $keys);
|
||||
} catch (InvalidArgumentException $e) {
|
||||
$this->pool->getItems($keys);
|
||||
// Should throw an exception
|
||||
throw $e;
|
||||
}
|
||||
return $this->generateItems($items, $tagKeys);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
if ('' !== $prefix) {
|
||||
foreach ($this->deferred as $key => $item) {
|
||||
if (\str_starts_with($key, $prefix)) {
|
||||
unset($this->deferred[$key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->deferred = [];
|
||||
}
|
||||
if ($this->pool instanceof AdapterInterface) {
|
||||
return $this->pool->clear($prefix);
|
||||
}
|
||||
return $this->pool->clear();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
return $this->deleteItems([$key]);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if ('' !== $key && \is_string($key)) {
|
||||
$keys[] = static::TAGS_PREFIX . $key;
|
||||
}
|
||||
}
|
||||
return $this->pool->deleteItems($keys);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
if (!$item instanceof CacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$this->deferred[$item->getKey()] = $item;
|
||||
return $this->commit();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
if (!$item instanceof CacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$this->deferred[$item->getKey()] = $item;
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
if (!$this->deferred) {
|
||||
return \true;
|
||||
}
|
||||
$ok = \true;
|
||||
foreach ($this->deferred as $key => $item) {
|
||||
if (!$this->pool->saveDeferred($item)) {
|
||||
unset($this->deferred[$key]);
|
||||
$ok = \false;
|
||||
}
|
||||
}
|
||||
$items = $this->deferred;
|
||||
$tagsByKey = (self::$getTagsByKey)($items);
|
||||
$this->deferred = [];
|
||||
$tagVersions = $this->getTagVersions($tagsByKey);
|
||||
$f = self::$createCacheItem;
|
||||
foreach ($tagsByKey as $key => $tags) {
|
||||
$this->pool->saveDeferred($f(static::TAGS_PREFIX . $key, \array_intersect_key($tagVersions, $tags), $items[$key]));
|
||||
}
|
||||
return $this->pool->commit() && $ok;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize ' . __CLASS__);
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__);
|
||||
}
|
||||
public function __destruct()
|
||||
{
|
||||
$this->commit();
|
||||
}
|
||||
private function generateItems(iterable $items, array $tagKeys) : \Generator
|
||||
{
|
||||
$bufferedItems = $itemTags = [];
|
||||
$f = self::$setCacheItemTags;
|
||||
foreach ($items as $key => $item) {
|
||||
if (!$tagKeys) {
|
||||
(yield $key => $f($item, static::TAGS_PREFIX . $key, $itemTags));
|
||||
continue;
|
||||
}
|
||||
if (!isset($tagKeys[$key])) {
|
||||
$bufferedItems[$key] = $item;
|
||||
continue;
|
||||
}
|
||||
unset($tagKeys[$key]);
|
||||
if ($item->isHit()) {
|
||||
$itemTags[$key] = $item->get() ?: [];
|
||||
}
|
||||
if (!$tagKeys) {
|
||||
$tagVersions = $this->getTagVersions($itemTags);
|
||||
foreach ($itemTags as $key => $tags) {
|
||||
foreach ($tags as $tag => $version) {
|
||||
if ($tagVersions[$tag] !== $version) {
|
||||
unset($itemTags[$key]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
$tagVersions = $tagKeys = null;
|
||||
foreach ($bufferedItems as $key => $item) {
|
||||
(yield $key => $f($item, static::TAGS_PREFIX . $key, $itemTags));
|
||||
}
|
||||
$bufferedItems = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
private function getTagVersions(array $tagsByKey)
|
||||
{
|
||||
$tagVersions = [];
|
||||
$fetchTagVersions = \false;
|
||||
foreach ($tagsByKey as $tags) {
|
||||
$tagVersions += $tags;
|
||||
foreach ($tags as $tag => $version) {
|
||||
if ($tagVersions[$tag] !== $version) {
|
||||
unset($this->knownTagVersions[$tag]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$tagVersions) {
|
||||
return [];
|
||||
}
|
||||
$now = \microtime(\true);
|
||||
$tags = [];
|
||||
foreach ($tagVersions as $tag => $version) {
|
||||
$tags[$tag . static::TAGS_PREFIX] = $tag;
|
||||
if ($fetchTagVersions || ($this->knownTagVersions[$tag][1] ?? null) !== $version || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
|
||||
// reuse previously fetched tag versions up to the ttl
|
||||
$fetchTagVersions = \true;
|
||||
}
|
||||
}
|
||||
if (!$fetchTagVersions) {
|
||||
return $tagVersions;
|
||||
}
|
||||
$newTags = [];
|
||||
$newVersion = null;
|
||||
foreach ($this->tags->getItems(\array_keys($tags)) as $tag => $version) {
|
||||
if (!$version->isHit()) {
|
||||
$newTags[$tag] = $version->set($newVersion ?? ($newVersion = \random_int(\PHP_INT_MIN, \PHP_INT_MAX)));
|
||||
}
|
||||
$tagVersions[$tag = $tags[$tag]] = $version->get();
|
||||
$this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]];
|
||||
}
|
||||
if ($newTags) {
|
||||
(self::$saveTags)($this->tags, $newTags);
|
||||
}
|
||||
return $tagVersions;
|
||||
}
|
||||
}
|
31
dependencies/symfony/cache/Adapter/TagAwareAdapterInterface.php
vendored
Normal file
31
dependencies/symfony/cache/Adapter/TagAwareAdapterInterface.php
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\InvalidArgumentException;
|
||||
/**
|
||||
* Interface for invalidating cached items using tags.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface TagAwareAdapterInterface extends AdapterInterface
|
||||
{
|
||||
/**
|
||||
* Invalidates cached items using tags.
|
||||
*
|
||||
* @param string[] $tags An array of tags to invalidate
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws InvalidArgumentException When $tags is not valid
|
||||
*/
|
||||
public function invalidateTags(array $tags);
|
||||
}
|
266
dependencies/symfony/cache/Adapter/TraceableAdapter.php
vendored
Normal file
266
dependencies/symfony/cache/Adapter/TraceableAdapter.php
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\ResettableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Service\ResetInterface;
|
||||
/**
|
||||
* An adapter that collects data about all cache calls.
|
||||
*
|
||||
* @author Aaron Scherer <aequasi@gmail.com>
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TraceableAdapter implements AdapterInterface, CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
protected $pool;
|
||||
private $calls = [];
|
||||
public function __construct(AdapterInterface $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
if (!$this->pool instanceof CacheInterface) {
|
||||
throw new \BadMethodCallException(\sprintf('Cannot call "%s::get()": this class doesn\'t implement "%s".', \get_debug_type($this->pool), CacheInterface::class));
|
||||
}
|
||||
$isHit = \true;
|
||||
$callback = function (CacheItem $item, bool &$save) use($callback, &$isHit) {
|
||||
$isHit = $item->isHit();
|
||||
return $callback($item, $save);
|
||||
};
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$value = $this->pool->get($key, $callback, $beta, $metadata);
|
||||
$event->result[$key] = \get_debug_type($value);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
if ($isHit) {
|
||||
++$event->hits;
|
||||
} else {
|
||||
++$event->misses;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$item = $this->pool->getItem($key);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
if ($event->result[$key] = $item->isHit()) {
|
||||
++$event->hits;
|
||||
} else {
|
||||
++$event->misses;
|
||||
}
|
||||
return $item;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$key] = $this->pool->hasItem($key);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$key] = $this->pool->deleteItem($key);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$item->getKey()] = $this->pool->save($item);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$item->getKey()] = $this->pool->saveDeferred($item);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$result = $this->pool->getItems($keys);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
$f = function () use($result, $event) {
|
||||
$event->result = [];
|
||||
foreach ($result as $key => $item) {
|
||||
if ($event->result[$key] = $item->isHit()) {
|
||||
++$event->hits;
|
||||
} else {
|
||||
++$event->misses;
|
||||
}
|
||||
(yield $key => $item);
|
||||
}
|
||||
};
|
||||
return $f();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
if ($this->pool instanceof AdapterInterface) {
|
||||
return $event->result = $this->pool->clear($prefix);
|
||||
}
|
||||
return $event->result = $this->pool->clear();
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
$event->result['keys'] = $keys;
|
||||
try {
|
||||
return $event->result['result'] = $this->pool->deleteItems($keys);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result = $this->pool->commit();
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
if (!$this->pool instanceof PruneableInterface) {
|
||||
return \false;
|
||||
}
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result = $this->pool->prune();
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->pool instanceof ResetInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
$this->clearCalls();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(string $key) : bool
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$key] = $this->pool->deleteItem($key);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
public function getCalls()
|
||||
{
|
||||
return $this->calls;
|
||||
}
|
||||
public function clearCalls()
|
||||
{
|
||||
$this->calls = [];
|
||||
}
|
||||
protected function start(string $name)
|
||||
{
|
||||
$this->calls[] = $event = new TraceableAdapterEvent();
|
||||
$event->name = $name;
|
||||
$event->start = \microtime(\true);
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
class TraceableAdapterEvent
|
||||
{
|
||||
public $name;
|
||||
public $start;
|
||||
public $end;
|
||||
public $result;
|
||||
public $hits = 0;
|
||||
public $misses = 0;
|
||||
}
|
35
dependencies/symfony/cache/Adapter/TraceableTagAwareAdapter.php
vendored
Normal file
35
dependencies/symfony/cache/Adapter/TraceableTagAwareAdapter.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\TagAwareCacheInterface;
|
||||
/**
|
||||
* @author Robin Chalas <robin.chalas@gmail.com>
|
||||
*/
|
||||
class TraceableTagAwareAdapter extends TraceableAdapter implements TagAwareAdapterInterface, TagAwareCacheInterface
|
||||
{
|
||||
public function __construct(TagAwareAdapterInterface $pool)
|
||||
{
|
||||
parent::__construct($pool);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result = $this->pool->invalidateTags($tags);
|
||||
} finally {
|
||||
$event->end = \microtime(\true);
|
||||
}
|
||||
}
|
||||
}
|
174
dependencies/symfony/cache/CacheItem.php
vendored
Normal file
174
dependencies/symfony/cache/CacheItem.php
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\LogicException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\ItemInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
final class CacheItem implements ItemInterface
|
||||
{
|
||||
private const METADATA_EXPIRY_OFFSET = 1527506807;
|
||||
protected $key;
|
||||
protected $value;
|
||||
protected $isHit = \false;
|
||||
protected $expiry;
|
||||
protected $metadata = [];
|
||||
protected $newMetadata = [];
|
||||
protected $innerItem;
|
||||
protected $poolHash;
|
||||
protected $isTaggable = \false;
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getKey() : string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isHit() : bool
|
||||
{
|
||||
return $this->isHit;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function set($value) : self
|
||||
{
|
||||
$this->value = $value;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function expiresAt($expiration) : self
|
||||
{
|
||||
if (null === $expiration) {
|
||||
$this->expiry = null;
|
||||
} elseif ($expiration instanceof \DateTimeInterface) {
|
||||
$this->expiry = (float) $expiration->format('U.u');
|
||||
} else {
|
||||
throw new InvalidArgumentException(\sprintf('Expiration date must implement DateTimeInterface or be null, "%s" given.', \get_debug_type($expiration)));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function expiresAfter($time) : self
|
||||
{
|
||||
if (null === $time) {
|
||||
$this->expiry = null;
|
||||
} elseif ($time instanceof \DateInterval) {
|
||||
$this->expiry = \microtime(\true) + \DateTime::createFromFormat('U', 0)->add($time)->format('U.u');
|
||||
} elseif (\is_int($time)) {
|
||||
$this->expiry = $time + \microtime(\true);
|
||||
} else {
|
||||
throw new InvalidArgumentException(\sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \get_debug_type($time)));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tag($tags) : ItemInterface
|
||||
{
|
||||
if (!$this->isTaggable) {
|
||||
throw new LogicException(\sprintf('Cache item "%s" comes from a non tag-aware pool: you cannot tag it.', $this->key));
|
||||
}
|
||||
if (!\is_iterable($tags)) {
|
||||
$tags = [$tags];
|
||||
}
|
||||
foreach ($tags as $tag) {
|
||||
if (!\is_string($tag) && !(\is_object($tag) && \method_exists($tag, '__toString'))) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache tag must be string or object that implements __toString(), "%s" given.', \is_object($tag) ? \get_class($tag) : \gettype($tag)));
|
||||
}
|
||||
$tag = (string) $tag;
|
||||
if (isset($this->newMetadata[self::METADATA_TAGS][$tag])) {
|
||||
continue;
|
||||
}
|
||||
if ('' === $tag) {
|
||||
throw new InvalidArgumentException('Cache tag length must be greater than zero.');
|
||||
}
|
||||
if (\false !== \strpbrk($tag, self::RESERVED_CHARACTERS)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache tag "%s" contains reserved characters "%s".', $tag, self::RESERVED_CHARACTERS));
|
||||
}
|
||||
$this->newMetadata[self::METADATA_TAGS][$tag] = $tag;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getMetadata() : array
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
/**
|
||||
* Validates a cache key according to PSR-6.
|
||||
*
|
||||
* @param mixed $key The key to validate
|
||||
*
|
||||
* @throws InvalidArgumentException When $key is not valid
|
||||
*/
|
||||
public static function validateKey($key) : string
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key must be string, "%s" given.', \get_debug_type($key)));
|
||||
}
|
||||
if ('' === $key) {
|
||||
throw new InvalidArgumentException('Cache key length must be greater than zero.');
|
||||
}
|
||||
if (\false !== \strpbrk($key, self::RESERVED_CHARACTERS)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache key "%s" contains reserved characters "%s".', $key, self::RESERVED_CHARACTERS));
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
/**
|
||||
* Internal logging helper.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function log(?LoggerInterface $logger, string $message, array $context = [])
|
||||
{
|
||||
if ($logger) {
|
||||
$logger->warning($message, $context);
|
||||
} else {
|
||||
$replace = [];
|
||||
foreach ($context as $k => $v) {
|
||||
if (\is_scalar($v)) {
|
||||
$replace['{' . $k . '}'] = $v;
|
||||
}
|
||||
}
|
||||
@\trigger_error(\strtr($message, $replace), \E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
152
dependencies/symfony/cache/DataCollector/CacheDataCollector.php
vendored
Normal file
152
dependencies/symfony/cache/DataCollector/CacheDataCollector.php
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\DataCollector;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\TraceableAdapterEvent;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpFoundation\Request;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpFoundation\Response;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||
/**
|
||||
* @author Aaron Scherer <aequasi@gmail.com>
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class CacheDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||
{
|
||||
/**
|
||||
* @var TraceableAdapter[]
|
||||
*/
|
||||
private $instances = [];
|
||||
public function addInstance(string $name, TraceableAdapter $instance)
|
||||
{
|
||||
$this->instances[$name] = $instance;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function collect(Request $request, Response $response, \Throwable $exception = null)
|
||||
{
|
||||
$empty = ['calls' => [], 'config' => [], 'options' => [], 'statistics' => []];
|
||||
$this->data = ['instances' => $empty, 'total' => $empty];
|
||||
foreach ($this->instances as $name => $instance) {
|
||||
$this->data['instances']['calls'][$name] = $instance->getCalls();
|
||||
}
|
||||
$this->data['instances']['statistics'] = $this->calculateStatistics();
|
||||
$this->data['total']['statistics'] = $this->calculateTotalStatistics();
|
||||
}
|
||||
public function reset()
|
||||
{
|
||||
$this->data = [];
|
||||
foreach ($this->instances as $instance) {
|
||||
$instance->clearCalls();
|
||||
}
|
||||
}
|
||||
public function lateCollect()
|
||||
{
|
||||
$this->data['instances']['calls'] = $this->cloneVar($this->data['instances']['calls']);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName() : string
|
||||
{
|
||||
return 'cache';
|
||||
}
|
||||
/**
|
||||
* Method returns amount of logged Cache reads: "get" calls.
|
||||
*/
|
||||
public function getStatistics() : array
|
||||
{
|
||||
return $this->data['instances']['statistics'];
|
||||
}
|
||||
/**
|
||||
* Method returns the statistic totals.
|
||||
*/
|
||||
public function getTotals() : array
|
||||
{
|
||||
return $this->data['total']['statistics'];
|
||||
}
|
||||
/**
|
||||
* Method returns all logged Cache call objects.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCalls()
|
||||
{
|
||||
return $this->data['instances']['calls'];
|
||||
}
|
||||
private function calculateStatistics() : array
|
||||
{
|
||||
$statistics = [];
|
||||
foreach ($this->data['instances']['calls'] as $name => $calls) {
|
||||
$statistics[$name] = ['calls' => 0, 'time' => 0, 'reads' => 0, 'writes' => 0, 'deletes' => 0, 'hits' => 0, 'misses' => 0];
|
||||
/** @var TraceableAdapterEvent $call */
|
||||
foreach ($calls as $call) {
|
||||
++$statistics[$name]['calls'];
|
||||
$statistics[$name]['time'] += ($call->end ?? \microtime(\true)) - $call->start;
|
||||
if ('get' === $call->name) {
|
||||
++$statistics[$name]['reads'];
|
||||
if ($call->hits) {
|
||||
++$statistics[$name]['hits'];
|
||||
} else {
|
||||
++$statistics[$name]['misses'];
|
||||
++$statistics[$name]['writes'];
|
||||
}
|
||||
} elseif ('getItem' === $call->name) {
|
||||
++$statistics[$name]['reads'];
|
||||
if ($call->hits) {
|
||||
++$statistics[$name]['hits'];
|
||||
} else {
|
||||
++$statistics[$name]['misses'];
|
||||
}
|
||||
} elseif ('getItems' === $call->name) {
|
||||
$statistics[$name]['reads'] += $call->hits + $call->misses;
|
||||
$statistics[$name]['hits'] += $call->hits;
|
||||
$statistics[$name]['misses'] += $call->misses;
|
||||
} elseif ('hasItem' === $call->name) {
|
||||
++$statistics[$name]['reads'];
|
||||
foreach ($call->result ?? [] as $result) {
|
||||
++$statistics[$name][$result ? 'hits' : 'misses'];
|
||||
}
|
||||
} elseif ('save' === $call->name) {
|
||||
++$statistics[$name]['writes'];
|
||||
} elseif ('deleteItem' === $call->name) {
|
||||
++$statistics[$name]['deletes'];
|
||||
}
|
||||
}
|
||||
if ($statistics[$name]['reads']) {
|
||||
$statistics[$name]['hit_read_ratio'] = \round(100 * $statistics[$name]['hits'] / $statistics[$name]['reads'], 2);
|
||||
} else {
|
||||
$statistics[$name]['hit_read_ratio'] = null;
|
||||
}
|
||||
}
|
||||
return $statistics;
|
||||
}
|
||||
private function calculateTotalStatistics() : array
|
||||
{
|
||||
$statistics = $this->getStatistics();
|
||||
$totals = ['calls' => 0, 'time' => 0, 'reads' => 0, 'writes' => 0, 'deletes' => 0, 'hits' => 0, 'misses' => 0];
|
||||
foreach ($statistics as $name => $values) {
|
||||
foreach ($totals as $key => $value) {
|
||||
$totals[$key] += $statistics[$name][$key];
|
||||
}
|
||||
}
|
||||
if ($totals['reads']) {
|
||||
$totals['hit_read_ratio'] = \round(100 * $totals['hits'] / $totals['reads'], 2);
|
||||
} else {
|
||||
$totals['hit_read_ratio'] = null;
|
||||
}
|
||||
return $totals;
|
||||
}
|
||||
}
|
81
dependencies/symfony/cache/DependencyInjection/CacheCollectorPass.php
vendored
Normal file
81
dependencies/symfony/cache/DependencyInjection/CacheCollectorPass.php
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Definition;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
/**
|
||||
* Inject a data collector to all the cache services to be able to get detailed statistics.
|
||||
*
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*/
|
||||
class CacheCollectorPass implements CompilerPassInterface
|
||||
{
|
||||
private $dataCollectorCacheId;
|
||||
private $cachePoolTag;
|
||||
private $cachePoolRecorderInnerSuffix;
|
||||
public function __construct(string $dataCollectorCacheId = 'data_collector.cache', string $cachePoolTag = 'cache.pool', string $cachePoolRecorderInnerSuffix = '.recorder_inner')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
$this->dataCollectorCacheId = $dataCollectorCacheId;
|
||||
$this->cachePoolTag = $cachePoolTag;
|
||||
$this->cachePoolRecorderInnerSuffix = $cachePoolRecorderInnerSuffix;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition($this->dataCollectorCacheId)) {
|
||||
return;
|
||||
}
|
||||
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $attributes) {
|
||||
$poolName = $attributes[0]['name'] ?? $id;
|
||||
$this->addToCollector($id, $poolName, $container);
|
||||
}
|
||||
}
|
||||
private function addToCollector(string $id, string $name, ContainerBuilder $container)
|
||||
{
|
||||
$definition = $container->getDefinition($id);
|
||||
if ($definition->isAbstract()) {
|
||||
return;
|
||||
}
|
||||
$collectorDefinition = $container->getDefinition($this->dataCollectorCacheId);
|
||||
$recorder = new Definition(\is_subclass_of($definition->getClass(), TagAwareAdapterInterface::class) ? TraceableTagAwareAdapter::class : TraceableAdapter::class);
|
||||
$recorder->setTags($definition->getTags());
|
||||
if (!$definition->isPublic() || !$definition->isPrivate()) {
|
||||
$recorder->setPublic($definition->isPublic());
|
||||
}
|
||||
$recorder->setArguments([new Reference($innerId = $id . $this->cachePoolRecorderInnerSuffix)]);
|
||||
foreach ($definition->getMethodCalls() as [$method, $args]) {
|
||||
if ('setCallbackWrapper' !== $method || !$args[0] instanceof Definition || !($args[0]->getArguments()[2] ?? null) instanceof Definition) {
|
||||
continue;
|
||||
}
|
||||
if ([new Reference($id), 'setCallbackWrapper'] == $args[0]->getArguments()[2]->getFactory()) {
|
||||
$args[0]->getArguments()[2]->setFactory([new Reference($innerId), 'setCallbackWrapper']);
|
||||
}
|
||||
}
|
||||
$definition->setTags([]);
|
||||
$definition->setPublic(\false);
|
||||
$container->setDefinition($innerId, $definition);
|
||||
$container->setDefinition($id, $recorder);
|
||||
// Tell the collector to add the new instance
|
||||
$collectorDefinition->addMethodCall('addInstance', [$name, new Reference($id)]);
|
||||
$collectorDefinition->setPublic(\false);
|
||||
}
|
||||
}
|
46
dependencies/symfony/cache/DependencyInjection/CachePoolClearerPass.php
vendored
Normal file
46
dependencies/symfony/cache/DependencyInjection/CachePoolClearerPass.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CachePoolClearerPass implements CompilerPassInterface
|
||||
{
|
||||
private $cachePoolClearerTag;
|
||||
public function __construct(string $cachePoolClearerTag = 'cache.pool.clearer')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
$this->cachePoolClearerTag = $cachePoolClearerTag;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
$container->getParameterBag()->remove('cache.prefix.seed');
|
||||
foreach ($container->findTaggedServiceIds($this->cachePoolClearerTag) as $id => $attr) {
|
||||
$clearer = $container->getDefinition($id);
|
||||
$pools = [];
|
||||
foreach ($clearer->getArgument(0) as $name => $ref) {
|
||||
if ($container->hasDefinition($ref)) {
|
||||
$pools[$name] = new Reference($ref);
|
||||
}
|
||||
}
|
||||
$clearer->replaceArgument(0, $pools);
|
||||
}
|
||||
}
|
||||
}
|
223
dependencies/symfony/cache/DependencyInjection/CachePoolPass.php
vendored
Normal file
223
dependencies/symfony/cache/DependencyInjection/CachePoolPass.php
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\NullAdapter;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\ParameterNormalizer;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Messenger\EarlyExpirationDispatcher;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Definition;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CachePoolPass implements CompilerPassInterface
|
||||
{
|
||||
private $cachePoolTag;
|
||||
private $kernelResetTag;
|
||||
private $cacheClearerId;
|
||||
private $cachePoolClearerTag;
|
||||
private $cacheSystemClearerId;
|
||||
private $cacheSystemClearerTag;
|
||||
private $reverseContainerId;
|
||||
private $reversibleTag;
|
||||
private $messageHandlerId;
|
||||
public function __construct(string $cachePoolTag = 'cache.pool', string $kernelResetTag = 'kernel.reset', string $cacheClearerId = 'cache.global_clearer', string $cachePoolClearerTag = 'cache.pool.clearer', string $cacheSystemClearerId = 'cache.system_clearer', string $cacheSystemClearerTag = 'kernel.cache_clearer', string $reverseContainerId = 'reverse_container', string $reversibleTag = 'container.reversible', string $messageHandlerId = 'cache.early_expiration_handler')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
$this->cachePoolTag = $cachePoolTag;
|
||||
$this->kernelResetTag = $kernelResetTag;
|
||||
$this->cacheClearerId = $cacheClearerId;
|
||||
$this->cachePoolClearerTag = $cachePoolClearerTag;
|
||||
$this->cacheSystemClearerId = $cacheSystemClearerId;
|
||||
$this->cacheSystemClearerTag = $cacheSystemClearerTag;
|
||||
$this->reverseContainerId = $reverseContainerId;
|
||||
$this->reversibleTag = $reversibleTag;
|
||||
$this->messageHandlerId = $messageHandlerId;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if ($container->hasParameter('cache.prefix.seed')) {
|
||||
$seed = $container->getParameterBag()->resolveValue($container->getParameter('cache.prefix.seed'));
|
||||
} else {
|
||||
$seed = '_' . $container->getParameter('kernel.project_dir');
|
||||
$seed .= '.' . $container->getParameter('kernel.container_class');
|
||||
}
|
||||
$needsMessageHandler = \false;
|
||||
$allPools = [];
|
||||
$clearers = [];
|
||||
$attributes = ['provider', 'name', 'namespace', 'default_lifetime', 'early_expiration_message_bus', 'reset'];
|
||||
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) {
|
||||
$adapter = $pool = $container->getDefinition($id);
|
||||
if ($pool->isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
$class = $adapter->getClass();
|
||||
while ($adapter instanceof ChildDefinition) {
|
||||
$adapter = $container->findDefinition($adapter->getParent());
|
||||
$class = $class ?: $adapter->getClass();
|
||||
if ($t = $adapter->getTag($this->cachePoolTag)) {
|
||||
$tags[0] += $t[0];
|
||||
}
|
||||
}
|
||||
$name = $tags[0]['name'] ?? $id;
|
||||
if (!isset($tags[0]['namespace'])) {
|
||||
$namespaceSeed = $seed;
|
||||
if (null !== $class) {
|
||||
$namespaceSeed .= '.' . $class;
|
||||
}
|
||||
$tags[0]['namespace'] = $this->getNamespace($namespaceSeed, $name);
|
||||
}
|
||||
if (isset($tags[0]['clearer'])) {
|
||||
$clearer = $tags[0]['clearer'];
|
||||
while ($container->hasAlias($clearer)) {
|
||||
$clearer = (string) $container->getAlias($clearer);
|
||||
}
|
||||
} else {
|
||||
$clearer = null;
|
||||
}
|
||||
unset($tags[0]['clearer'], $tags[0]['name']);
|
||||
if (isset($tags[0]['provider'])) {
|
||||
$tags[0]['provider'] = new Reference(static::getServiceProvider($container, $tags[0]['provider']));
|
||||
}
|
||||
if (ChainAdapter::class === $class) {
|
||||
$adapters = [];
|
||||
foreach ($adapter->getArgument(0) as $provider => $adapter) {
|
||||
if ($adapter instanceof ChildDefinition) {
|
||||
$chainedPool = $adapter;
|
||||
} else {
|
||||
$chainedPool = $adapter = new ChildDefinition($adapter);
|
||||
}
|
||||
$chainedTags = [\is_int($provider) ? [] : ['provider' => $provider]];
|
||||
$chainedClass = '';
|
||||
while ($adapter instanceof ChildDefinition) {
|
||||
$adapter = $container->findDefinition($adapter->getParent());
|
||||
$chainedClass = $chainedClass ?: $adapter->getClass();
|
||||
if ($t = $adapter->getTag($this->cachePoolTag)) {
|
||||
$chainedTags[0] += $t[0];
|
||||
}
|
||||
}
|
||||
if (ChainAdapter::class === $chainedClass) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid service "%s": chain of adapters cannot reference another chain, found "%s".', $id, $chainedPool->getParent()));
|
||||
}
|
||||
$i = 0;
|
||||
if (isset($chainedTags[0]['provider'])) {
|
||||
$chainedPool->replaceArgument($i++, new Reference(static::getServiceProvider($container, $chainedTags[0]['provider'])));
|
||||
}
|
||||
if (isset($tags[0]['namespace']) && !\in_array($adapter->getClass(), [ArrayAdapter::class, NullAdapter::class], \true)) {
|
||||
$chainedPool->replaceArgument($i++, $tags[0]['namespace']);
|
||||
}
|
||||
if (isset($tags[0]['default_lifetime'])) {
|
||||
$chainedPool->replaceArgument($i++, $tags[0]['default_lifetime']);
|
||||
}
|
||||
$adapters[] = $chainedPool;
|
||||
}
|
||||
$pool->replaceArgument(0, $adapters);
|
||||
unset($tags[0]['provider'], $tags[0]['namespace']);
|
||||
$i = 1;
|
||||
} else {
|
||||
$i = 0;
|
||||
}
|
||||
foreach ($attributes as $attr) {
|
||||
if (!isset($tags[0][$attr])) {
|
||||
// no-op
|
||||
} elseif ('reset' === $attr) {
|
||||
if ($tags[0][$attr]) {
|
||||
$pool->addTag($this->kernelResetTag, ['method' => $tags[0][$attr]]);
|
||||
}
|
||||
} elseif ('early_expiration_message_bus' === $attr) {
|
||||
$needsMessageHandler = \true;
|
||||
$pool->addMethodCall('setCallbackWrapper', [(new Definition(EarlyExpirationDispatcher::class))->addArgument(new Reference($tags[0]['early_expiration_message_bus']))->addArgument(new Reference($this->reverseContainerId))->addArgument((new Definition('callable'))->setFactory([new Reference($id), 'setCallbackWrapper'])->addArgument(null))]);
|
||||
$pool->addTag($this->reversibleTag);
|
||||
} elseif ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], \true)) {
|
||||
$argument = $tags[0][$attr];
|
||||
if ('default_lifetime' === $attr && !\is_numeric($argument)) {
|
||||
$argument = (new Definition('int', [$argument]))->setFactory([ParameterNormalizer::class, 'normalizeDuration']);
|
||||
}
|
||||
$pool->replaceArgument($i++, $argument);
|
||||
}
|
||||
unset($tags[0][$attr]);
|
||||
}
|
||||
if (!empty($tags[0])) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid "%s" tag for service "%s": accepted attributes are "clearer", "provider", "name", "namespace", "default_lifetime", "early_expiration_message_bus" and "reset", found "%s".', $this->cachePoolTag, $id, \implode('", "', \array_keys($tags[0]))));
|
||||
}
|
||||
if (null !== $clearer) {
|
||||
$clearers[$clearer][$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE);
|
||||
}
|
||||
$allPools[$name] = new Reference($id, $container::IGNORE_ON_UNINITIALIZED_REFERENCE);
|
||||
}
|
||||
if (!$needsMessageHandler) {
|
||||
$container->removeDefinition($this->messageHandlerId);
|
||||
}
|
||||
$notAliasedCacheClearerId = $this->cacheClearerId;
|
||||
while ($container->hasAlias($this->cacheClearerId)) {
|
||||
$this->cacheClearerId = (string) $container->getAlias($this->cacheClearerId);
|
||||
}
|
||||
if ($container->hasDefinition($this->cacheClearerId)) {
|
||||
$clearers[$notAliasedCacheClearerId] = $allPools;
|
||||
}
|
||||
foreach ($clearers as $id => $pools) {
|
||||
$clearer = $container->getDefinition($id);
|
||||
if ($clearer instanceof ChildDefinition) {
|
||||
$clearer->replaceArgument(0, $pools);
|
||||
} else {
|
||||
$clearer->setArgument(0, $pools);
|
||||
}
|
||||
$clearer->addTag($this->cachePoolClearerTag);
|
||||
if ($this->cacheSystemClearerId === $id) {
|
||||
$clearer->addTag($this->cacheSystemClearerTag);
|
||||
}
|
||||
}
|
||||
$allPoolsKeys = \array_keys($allPools);
|
||||
if ($container->hasDefinition('console.command.cache_pool_list')) {
|
||||
$container->getDefinition('console.command.cache_pool_list')->replaceArgument(0, $allPoolsKeys);
|
||||
}
|
||||
if ($container->hasDefinition('console.command.cache_pool_clear')) {
|
||||
$container->getDefinition('console.command.cache_pool_clear')->addArgument($allPoolsKeys);
|
||||
}
|
||||
if ($container->hasDefinition('console.command.cache_pool_delete')) {
|
||||
$container->getDefinition('console.command.cache_pool_delete')->addArgument($allPoolsKeys);
|
||||
}
|
||||
}
|
||||
private function getNamespace(string $seed, string $id)
|
||||
{
|
||||
return \substr(\str_replace('/', '-', \base64_encode(\hash('sha256', $id . $seed, \true))), 0, 10);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function getServiceProvider(ContainerBuilder $container, string $name)
|
||||
{
|
||||
$container->resolveEnvPlaceholders($name, null, $usedEnvs);
|
||||
if ($usedEnvs || \preg_match('#^[a-z]++:#', $name)) {
|
||||
$dsn = $name;
|
||||
if (!$container->hasDefinition($name = '.cache_connection.' . ContainerBuilder::hash($dsn))) {
|
||||
$definition = new Definition(AbstractAdapter::class);
|
||||
$definition->setPublic(\false);
|
||||
$definition->setFactory([AbstractAdapter::class, 'createConnection']);
|
||||
$definition->setArguments([$dsn, ['lazy' => \true]]);
|
||||
$container->setDefinition($name, $definition);
|
||||
}
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
}
|
54
dependencies/symfony/cache/DependencyInjection/CachePoolPrunerPass.php
vendored
Normal file
54
dependencies/symfony/cache/DependencyInjection/CachePoolPrunerPass.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\Reference;
|
||||
/**
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
*/
|
||||
class CachePoolPrunerPass implements CompilerPassInterface
|
||||
{
|
||||
private $cacheCommandServiceId;
|
||||
private $cachePoolTag;
|
||||
public function __construct(string $cacheCommandServiceId = 'console.command.cache_pool_prune', string $cachePoolTag = 'cache.pool')
|
||||
{
|
||||
if (0 < \func_num_args()) {
|
||||
trigger_deprecation('symfony/cache', '5.3', 'Configuring "%s" is deprecated.', __CLASS__);
|
||||
}
|
||||
$this->cacheCommandServiceId = $cacheCommandServiceId;
|
||||
$this->cachePoolTag = $cachePoolTag;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition($this->cacheCommandServiceId)) {
|
||||
return;
|
||||
}
|
||||
$services = [];
|
||||
foreach ($container->findTaggedServiceIds($this->cachePoolTag) as $id => $tags) {
|
||||
$class = $container->getParameterBag()->resolveValue($container->getDefinition($id)->getClass());
|
||||
if (!($reflection = $container->getReflectionClass($class))) {
|
||||
throw new InvalidArgumentException(\sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
|
||||
}
|
||||
if ($reflection->implementsInterface(PruneableInterface::class)) {
|
||||
$services[$id] = new Reference($id);
|
||||
}
|
||||
}
|
||||
$container->getDefinition($this->cacheCommandServiceId)->replaceArgument(0, new IteratorArgument($services));
|
||||
}
|
||||
}
|
108
dependencies/symfony/cache/DoctrineProvider.php
vendored
Normal file
108
dependencies/symfony/cache/DoctrineProvider.php
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache;
|
||||
|
||||
use WP_Ultimo\Dependencies\Doctrine\Common\Cache\CacheProvider;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemPoolInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Service\ResetInterface;
|
||||
if (!\class_exists(CacheProvider::class)) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated Use Doctrine\Common\Cache\Psr6\DoctrineProvider instead
|
||||
*/
|
||||
class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
public function __construct(CacheItemPoolInterface $pool)
|
||||
{
|
||||
trigger_deprecation('symfony/cache', '5.4', '"%s" is deprecated, use "Doctrine\\Common\\Cache\\Psr6\\DoctrineProvider" instead.', __CLASS__);
|
||||
$this->pool = $pool;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
return $this->pool instanceof PruneableInterface && $this->pool->prune();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->pool instanceof ResetInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
$this->setNamespace($this->getNamespace());
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
protected function doFetch($id)
|
||||
{
|
||||
$item = $this->pool->getItem(\rawurlencode($id));
|
||||
return $item->isHit() ? $item->get() : \false;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->pool->hasItem(\rawurlencode($id));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
$item = $this->pool->getItem(\rawurlencode($id));
|
||||
if (0 < $lifeTime) {
|
||||
$item->expiresAfter($lifeTime);
|
||||
}
|
||||
return $this->pool->save($item->set($data));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->pool->deleteItem(\rawurlencode($id));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
protected function doGetStats()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
23
dependencies/symfony/cache/Exception/CacheException.php
vendored
Normal file
23
dependencies/symfony/cache/Exception/CacheException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheException as Psr6CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\SimpleCache\CacheException as SimpleCacheInterface;
|
||||
if (\interface_exists(SimpleCacheInterface::class)) {
|
||||
class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class CacheException extends \Exception implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
23
dependencies/symfony/cache/Exception/InvalidArgumentException.php
vendored
Normal file
23
dependencies/symfony/cache/Exception/InvalidArgumentException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\InvalidArgumentException as Psr6CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface;
|
||||
if (\interface_exists(SimpleCacheInterface::class)) {
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
23
dependencies/symfony/cache/Exception/LogicException.php
vendored
Normal file
23
dependencies/symfony/cache/Exception/LogicException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheException as Psr6CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\SimpleCache\CacheException as SimpleCacheInterface;
|
||||
if (\interface_exists(SimpleCacheInterface::class)) {
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
120
dependencies/symfony/cache/LockRegistry.php
vendored
Normal file
120
dependencies/symfony/cache/LockRegistry.php
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\ItemInterface;
|
||||
/**
|
||||
* LockRegistry is used internally by existing adapters to protect against cache stampede.
|
||||
*
|
||||
* It does so by wrapping the computation of items in a pool of locks.
|
||||
* Foreach each apps, there can be at most 20 concurrent processes that
|
||||
* compute items at the same time and only one per cache-key.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
final class LockRegistry
|
||||
{
|
||||
private static $openedFiles = [];
|
||||
private static $lockedFiles;
|
||||
private static $signalingException;
|
||||
private static $signalingCallback;
|
||||
/**
|
||||
* The number of items in this list controls the max number of concurrent processes.
|
||||
*/
|
||||
private static $files = [__DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'AbstractAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'AbstractTagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'AdapterInterface.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ApcuAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ArrayAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ChainAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'CouchbaseBucketAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'CouchbaseCollectionAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'DoctrineAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'DoctrineDbalAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'FilesystemAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'FilesystemTagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'MemcachedAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'NullAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ParameterNormalizer.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'PdoAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'PhpArrayAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'PhpFilesAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'ProxyAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'Psr16Adapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'RedisAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'RedisTagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TagAwareAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TagAwareAdapterInterface.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TraceableAdapter.php', __DIR__ . \DIRECTORY_SEPARATOR . 'Adapter' . \DIRECTORY_SEPARATOR . 'TraceableTagAwareAdapter.php'];
|
||||
/**
|
||||
* Defines a set of existing files that will be used as keys to acquire locks.
|
||||
*
|
||||
* @return array The previously defined set of files
|
||||
*/
|
||||
public static function setFiles(array $files) : array
|
||||
{
|
||||
$previousFiles = self::$files;
|
||||
self::$files = $files;
|
||||
foreach (self::$openedFiles as $file) {
|
||||
if ($file) {
|
||||
\flock($file, \LOCK_UN);
|
||||
\fclose($file);
|
||||
}
|
||||
}
|
||||
self::$openedFiles = self::$lockedFiles = [];
|
||||
return $previousFiles;
|
||||
}
|
||||
public static function compute(callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata = null, LoggerInterface $logger = null)
|
||||
{
|
||||
if ('\\' === \DIRECTORY_SEPARATOR && null === self::$lockedFiles) {
|
||||
// disable locking on Windows by default
|
||||
self::$files = self::$lockedFiles = [];
|
||||
}
|
||||
$key = self::$files ? \abs(\crc32($item->getKey())) % \count(self::$files) : -1;
|
||||
if ($key < 0 || self::$lockedFiles || !($lock = self::open($key))) {
|
||||
return $callback($item, $save);
|
||||
}
|
||||
self::$signalingException ?? (self::$signalingException = \unserialize("O:9:\"Exception\":1:{s:16:\"\x00Exception\x00trace\";a:0:{}}"));
|
||||
self::$signalingCallback ?? (self::$signalingCallback = function () {
|
||||
throw self::$signalingException;
|
||||
});
|
||||
while (\true) {
|
||||
try {
|
||||
$locked = \false;
|
||||
// race to get the lock in non-blocking mode
|
||||
$locked = \flock($lock, \LOCK_EX | \LOCK_NB, $wouldBlock);
|
||||
if ($locked || !$wouldBlock) {
|
||||
$logger && $logger->info(\sprintf('Lock %s, now computing item "{key}"', $locked ? 'acquired' : 'not supported'), ['key' => $item->getKey()]);
|
||||
self::$lockedFiles[$key] = \true;
|
||||
$value = $callback($item, $save);
|
||||
if ($save) {
|
||||
if ($setMetadata) {
|
||||
$setMetadata($item);
|
||||
}
|
||||
$pool->save($item->set($value));
|
||||
$save = \false;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
// if we failed the race, retry locking in blocking mode to wait for the winner
|
||||
$logger && $logger->info('Item "{key}" is locked, waiting for it to be released', ['key' => $item->getKey()]);
|
||||
\flock($lock, \LOCK_SH);
|
||||
} finally {
|
||||
\flock($lock, \LOCK_UN);
|
||||
unset(self::$lockedFiles[$key]);
|
||||
}
|
||||
try {
|
||||
$value = $pool->get($item->getKey(), self::$signalingCallback, 0);
|
||||
$logger && $logger->info('Item "{key}" retrieved after lock was released', ['key' => $item->getKey()]);
|
||||
$save = \false;
|
||||
return $value;
|
||||
} catch (\Exception $e) {
|
||||
if (self::$signalingException !== $e) {
|
||||
throw $e;
|
||||
}
|
||||
$logger && $logger->info('Item "{key}" not found while lock was released, now retrying', ['key' => $item->getKey()]);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static function open(int $key)
|
||||
{
|
||||
if (null !== ($h = self::$openedFiles[$key] ?? null)) {
|
||||
return $h;
|
||||
}
|
||||
\set_error_handler(function () {
|
||||
});
|
||||
try {
|
||||
$h = \fopen(self::$files[$key], 'r+');
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
return self::$openedFiles[$key] = $h ?: @\fopen(self::$files[$key], 'r');
|
||||
}
|
||||
}
|
95
dependencies/symfony/cache/Marshaller/DefaultMarshaller.php
vendored
Normal file
95
dependencies/symfony/cache/Marshaller/DefaultMarshaller.php
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
/**
|
||||
* Serializes/unserializes values using igbinary_serialize() if available, serialize() otherwise.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DefaultMarshaller implements MarshallerInterface
|
||||
{
|
||||
private $useIgbinarySerialize = \true;
|
||||
private $throwOnSerializationFailure;
|
||||
public function __construct(bool $useIgbinarySerialize = null, bool $throwOnSerializationFailure = \false)
|
||||
{
|
||||
if (null === $useIgbinarySerialize) {
|
||||
$useIgbinarySerialize = \extension_loaded('igbinary') && (\PHP_VERSION_ID < 70400 || \version_compare('3.1.6', \phpversion('igbinary'), '<='));
|
||||
} elseif ($useIgbinarySerialize && (!\extension_loaded('igbinary') || \PHP_VERSION_ID >= 70400 && \version_compare('3.1.6', \phpversion('igbinary'), '>'))) {
|
||||
throw new CacheException(\extension_loaded('igbinary') && \PHP_VERSION_ID >= 70400 ? 'Please upgrade the "igbinary" PHP extension to v3.1.6 or higher.' : 'The "igbinary" PHP extension is not loaded.');
|
||||
}
|
||||
$this->useIgbinarySerialize = $useIgbinarySerialize;
|
||||
$this->throwOnSerializationFailure = $throwOnSerializationFailure;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function marshall(array $values, ?array &$failed) : array
|
||||
{
|
||||
$serialized = $failed = [];
|
||||
foreach ($values as $id => $value) {
|
||||
try {
|
||||
if ($this->useIgbinarySerialize) {
|
||||
$serialized[$id] = \igbinary_serialize($value);
|
||||
} else {
|
||||
$serialized[$id] = \serialize($value);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
if ($this->throwOnSerializationFailure) {
|
||||
throw new \ValueError($e->getMessage(), 0, $e);
|
||||
}
|
||||
$failed[] = $id;
|
||||
}
|
||||
}
|
||||
return $serialized;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unmarshall(string $value)
|
||||
{
|
||||
if ('b:0;' === $value) {
|
||||
return \false;
|
||||
}
|
||||
if ('N;' === $value) {
|
||||
return null;
|
||||
}
|
||||
static $igbinaryNull;
|
||||
if ($value === ($igbinaryNull ?? ($igbinaryNull = \extension_loaded('igbinary') ? \igbinary_serialize(null) : \false))) {
|
||||
return null;
|
||||
}
|
||||
$unserializeCallbackHandler = \ini_set('unserialize_callback_func', __CLASS__ . '::handleUnserializeCallback');
|
||||
try {
|
||||
if (':' === ($value[1] ?? ':')) {
|
||||
if (\false !== ($value = \unserialize($value))) {
|
||||
return $value;
|
||||
}
|
||||
} elseif (\false === $igbinaryNull) {
|
||||
throw new \RuntimeException('Failed to unserialize values, did you forget to install the "igbinary" extension?');
|
||||
} elseif (null !== ($value = \igbinary_unserialize($value))) {
|
||||
return $value;
|
||||
}
|
||||
throw new \DomainException(\error_get_last() ? \error_get_last()['message'] : 'Failed to unserialize values.');
|
||||
} catch (\Error $e) {
|
||||
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||
} finally {
|
||||
\ini_set('unserialize_callback_func', $unserializeCallbackHandler);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function handleUnserializeCallback(string $class)
|
||||
{
|
||||
throw new \DomainException('Class not found: ' . $class);
|
||||
}
|
||||
}
|
46
dependencies/symfony/cache/Marshaller/DeflateMarshaller.php
vendored
Normal file
46
dependencies/symfony/cache/Marshaller/DeflateMarshaller.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
/**
|
||||
* Compresses values using gzdeflate().
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DeflateMarshaller implements MarshallerInterface
|
||||
{
|
||||
private $marshaller;
|
||||
public function __construct(MarshallerInterface $marshaller)
|
||||
{
|
||||
if (!\function_exists('gzdeflate')) {
|
||||
throw new CacheException('The "zlib" PHP extension is not loaded.');
|
||||
}
|
||||
$this->marshaller = $marshaller;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function marshall(array $values, ?array &$failed) : array
|
||||
{
|
||||
return \array_map('gzdeflate', $this->marshaller->marshall($values, $failed));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unmarshall(string $value)
|
||||
{
|
||||
if (\false !== ($inflatedValue = @\gzinflate($value))) {
|
||||
$value = $inflatedValue;
|
||||
}
|
||||
return $this->marshaller->unmarshall($value);
|
||||
}
|
||||
}
|
38
dependencies/symfony/cache/Marshaller/MarshallerInterface.php
vendored
Normal file
38
dependencies/symfony/cache/Marshaller/MarshallerInterface.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller;
|
||||
|
||||
/**
|
||||
* Serializes/unserializes PHP values.
|
||||
*
|
||||
* Implementations of this interface MUST deal with errors carefully. They MUST
|
||||
* also deal with forward and backward compatibility at the storage format level.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface MarshallerInterface
|
||||
{
|
||||
/**
|
||||
* Serializes a list of values.
|
||||
*
|
||||
* When serialization fails for a specific value, no exception should be
|
||||
* thrown. Instead, its key should be listed in $failed.
|
||||
*/
|
||||
public function marshall(array $values, ?array &$failed) : array;
|
||||
/**
|
||||
* Unserializes a single value and throws an exception if anything goes wrong.
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception Whenever unserialization fails
|
||||
*/
|
||||
public function unmarshall(string $value);
|
||||
}
|
69
dependencies/symfony/cache/Marshaller/SodiumMarshaller.php
vendored
Normal file
69
dependencies/symfony/cache/Marshaller/SodiumMarshaller.php
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
/**
|
||||
* Encrypt/decrypt values using Libsodium.
|
||||
*
|
||||
* @author Ahmed TAILOULOUTE <ahmed.tailouloute@gmail.com>
|
||||
*/
|
||||
class SodiumMarshaller implements MarshallerInterface
|
||||
{
|
||||
private $marshaller;
|
||||
private $decryptionKeys;
|
||||
/**
|
||||
* @param string[] $decryptionKeys The key at index "0" is required and is used to decrypt and encrypt values;
|
||||
* more rotating keys can be provided to decrypt values;
|
||||
* each key must be generated using sodium_crypto_box_keypair()
|
||||
*/
|
||||
public function __construct(array $decryptionKeys, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
if (!self::isSupported()) {
|
||||
throw new CacheException('The "sodium" PHP extension is not loaded.');
|
||||
}
|
||||
if (!isset($decryptionKeys[0])) {
|
||||
throw new InvalidArgumentException('At least one decryption key must be provided at index "0".');
|
||||
}
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
$this->decryptionKeys = $decryptionKeys;
|
||||
}
|
||||
public static function isSupported() : bool
|
||||
{
|
||||
return \function_exists('sodium_crypto_box_seal');
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function marshall(array $values, ?array &$failed) : array
|
||||
{
|
||||
$encryptionKey = \sodium_crypto_box_publickey($this->decryptionKeys[0]);
|
||||
$encryptedValues = [];
|
||||
foreach ($this->marshaller->marshall($values, $failed) as $k => $v) {
|
||||
$encryptedValues[$k] = \sodium_crypto_box_seal($v, $encryptionKey);
|
||||
}
|
||||
return $encryptedValues;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unmarshall(string $value)
|
||||
{
|
||||
foreach ($this->decryptionKeys as $k) {
|
||||
if (\false !== ($decryptedValue = @\sodium_crypto_box_seal_open($value, $k))) {
|
||||
$value = $decryptedValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->marshaller->unmarshall($value);
|
||||
}
|
||||
}
|
73
dependencies/symfony/cache/Marshaller/TagAwareMarshaller.php
vendored
Normal file
73
dependencies/symfony/cache/Marshaller/TagAwareMarshaller.php
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller;
|
||||
|
||||
/**
|
||||
* A marshaller optimized for data structures generated by AbstractTagAwareAdapter.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class TagAwareMarshaller implements MarshallerInterface
|
||||
{
|
||||
private $marshaller;
|
||||
public function __construct(MarshallerInterface $marshaller = null)
|
||||
{
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function marshall(array $values, ?array &$failed) : array
|
||||
{
|
||||
$failed = $notSerialized = $serialized = [];
|
||||
foreach ($values as $id => $value) {
|
||||
if (\is_array($value) && \is_array($value['tags'] ?? null) && \array_key_exists('value', $value) && \count($value) === 2 + (\is_string($value['meta'] ?? null) && 8 === \strlen($value['meta']))) {
|
||||
// if the value is an array with keys "tags", "value" and "meta", use a compact serialization format
|
||||
// magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F allow detecting this format quickly in unmarshall()
|
||||
$v = $this->marshaller->marshall($value, $f);
|
||||
if ($f) {
|
||||
$f = [];
|
||||
$failed[] = $id;
|
||||
} else {
|
||||
if ([] === $value['tags']) {
|
||||
$v['tags'] = '';
|
||||
}
|
||||
$serialized[$id] = "\x9d" . ($value['meta'] ?? "\x00\x00\x00\x00\x00\x00\x00\x00") . \pack('N', \strlen($v['tags'])) . $v['tags'] . $v['value'];
|
||||
$serialized[$id][9] = "_";
|
||||
}
|
||||
} else {
|
||||
// other arbitrary values are serialized using the decorated marshaller below
|
||||
$notSerialized[$id] = $value;
|
||||
}
|
||||
}
|
||||
if ($notSerialized) {
|
||||
$serialized += $this->marshaller->marshall($notSerialized, $f);
|
||||
$failed = \array_merge($failed, $f);
|
||||
}
|
||||
return $serialized;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function unmarshall(string $value)
|
||||
{
|
||||
// detect the compact format used in marshall() using magic numbers in the form 9D-..-..-..-..-00-..-..-..-5F
|
||||
if (13 >= \strlen($value) || "\x9d" !== $value[0] || "\x00" !== $value[5] || "_" !== $value[9]) {
|
||||
return $this->marshaller->unmarshall($value);
|
||||
}
|
||||
// data consists of value, tags and metadata which we need to unpack
|
||||
$meta = \substr($value, 1, 12);
|
||||
$meta[8] = "\x00";
|
||||
$tagLen = \unpack('Nlen', $meta, 8)['len'];
|
||||
$meta = \substr($meta, 0, 8);
|
||||
return ['value' => $this->marshaller->unmarshall(\substr($value, 13 + $tagLen)), 'tags' => $tagLen ? $this->marshaller->unmarshall(\substr($value, 13, $tagLen)) : [], 'meta' => "\x00\x00\x00\x00\x00\x00\x00\x00" === $meta ? null : $meta];
|
||||
}
|
||||
}
|
52
dependencies/symfony/cache/Messenger/EarlyExpirationDispatcher.php
vendored
Normal file
52
dependencies/symfony/cache/Messenger/EarlyExpirationDispatcher.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Messenger;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ReverseContainer;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Messenger\MessageBusInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Messenger\Stamp\HandledStamp;
|
||||
/**
|
||||
* Sends the computation of cached values to a message bus.
|
||||
*/
|
||||
class EarlyExpirationDispatcher
|
||||
{
|
||||
private $bus;
|
||||
private $reverseContainer;
|
||||
private $callbackWrapper;
|
||||
public function __construct(MessageBusInterface $bus, ReverseContainer $reverseContainer, callable $callbackWrapper = null)
|
||||
{
|
||||
$this->bus = $bus;
|
||||
$this->reverseContainer = $reverseContainer;
|
||||
$this->callbackWrapper = $callbackWrapper;
|
||||
}
|
||||
public function __invoke(callable $callback, CacheItem $item, bool &$save, AdapterInterface $pool, \Closure $setMetadata, LoggerInterface $logger = null)
|
||||
{
|
||||
if (!$item->isHit() || null === ($message = EarlyExpirationMessage::create($this->reverseContainer, $callback, $item, $pool))) {
|
||||
// The item is stale or the callback cannot be reversed: we must compute the value now
|
||||
$logger && $logger->info('Computing item "{key}" online: ' . ($item->isHit() ? 'callback cannot be reversed' : 'item is stale'), ['key' => $item->getKey()]);
|
||||
return null !== $this->callbackWrapper ? ($this->callbackWrapper)($callback, $item, $save, $pool, $setMetadata, $logger) : $callback($item, $save);
|
||||
}
|
||||
$envelope = $this->bus->dispatch($message);
|
||||
if ($logger) {
|
||||
if ($envelope->last(HandledStamp::class)) {
|
||||
$logger->info('Item "{key}" was computed online', ['key' => $item->getKey()]);
|
||||
} else {
|
||||
$logger->info('Item "{key}" sent for recomputation', ['key' => $item->getKey()]);
|
||||
}
|
||||
}
|
||||
// The item's value is not stale, no need to write it to the backend
|
||||
$save = \false;
|
||||
return $message->getItem()->get() ?? $item->get();
|
||||
}
|
||||
}
|
64
dependencies/symfony/cache/Messenger/EarlyExpirationHandler.php
vendored
Normal file
64
dependencies/symfony/cache/Messenger/EarlyExpirationHandler.php
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Messenger;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ReverseContainer;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Messenger\Handler\MessageHandlerInterface;
|
||||
/**
|
||||
* Computes cached values sent to a message bus.
|
||||
*/
|
||||
class EarlyExpirationHandler implements MessageHandlerInterface
|
||||
{
|
||||
private $reverseContainer;
|
||||
private $processedNonces = [];
|
||||
public function __construct(ReverseContainer $reverseContainer)
|
||||
{
|
||||
$this->reverseContainer = $reverseContainer;
|
||||
}
|
||||
public function __invoke(EarlyExpirationMessage $message)
|
||||
{
|
||||
$item = $message->getItem();
|
||||
$metadata = $item->getMetadata();
|
||||
$expiry = $metadata[CacheItem::METADATA_EXPIRY] ?? 0;
|
||||
$ctime = $metadata[CacheItem::METADATA_CTIME] ?? 0;
|
||||
if ($expiry && $ctime) {
|
||||
// skip duplicate or expired messages
|
||||
$processingNonce = [$expiry, $ctime];
|
||||
$pool = $message->getPool();
|
||||
$key = $item->getKey();
|
||||
if (($this->processedNonces[$pool][$key] ?? null) === $processingNonce) {
|
||||
return;
|
||||
}
|
||||
if (\microtime(\true) >= $expiry) {
|
||||
return;
|
||||
}
|
||||
$this->processedNonces[$pool] = [$key => $processingNonce] + ($this->processedNonces[$pool] ?? []);
|
||||
if (\count($this->processedNonces[$pool]) > 100) {
|
||||
\array_pop($this->processedNonces[$pool]);
|
||||
}
|
||||
}
|
||||
static $setMetadata;
|
||||
$setMetadata ?? ($setMetadata = \Closure::bind(function (CacheItem $item, float $startTime) {
|
||||
if ($item->expiry > ($endTime = \microtime(\true))) {
|
||||
$item->newMetadata[CacheItem::METADATA_EXPIRY] = $item->expiry;
|
||||
$item->newMetadata[CacheItem::METADATA_CTIME] = (int) \ceil(1000 * ($endTime - $startTime));
|
||||
}
|
||||
}, null, CacheItem::class));
|
||||
$startTime = \microtime(\true);
|
||||
$pool = $message->findPool($this->reverseContainer);
|
||||
$callback = $message->findCallback($this->reverseContainer);
|
||||
$save = \true;
|
||||
$value = $callback($item, $save);
|
||||
$setMetadata($item, $startTime);
|
||||
$pool->save($item->set($value));
|
||||
}
|
||||
}
|
82
dependencies/symfony/cache/Messenger/EarlyExpirationMessage.php
vendored
Normal file
82
dependencies/symfony/cache/Messenger/EarlyExpirationMessage.php
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Messenger;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\DependencyInjection\ReverseContainer;
|
||||
/**
|
||||
* Conveys a cached value that needs to be computed.
|
||||
*/
|
||||
final class EarlyExpirationMessage
|
||||
{
|
||||
private $item;
|
||||
private $pool;
|
||||
private $callback;
|
||||
public static function create(ReverseContainer $reverseContainer, callable $callback, CacheItem $item, AdapterInterface $pool) : ?self
|
||||
{
|
||||
try {
|
||||
$item = clone $item;
|
||||
$item->set(null);
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
$pool = $reverseContainer->getId($pool);
|
||||
if (\is_object($callback)) {
|
||||
if (null === ($id = $reverseContainer->getId($callback))) {
|
||||
return null;
|
||||
}
|
||||
$callback = '@' . $id;
|
||||
} elseif (!\is_array($callback)) {
|
||||
$callback = (string) $callback;
|
||||
} elseif (!\is_object($callback[0])) {
|
||||
$callback = [(string) $callback[0], (string) $callback[1]];
|
||||
} else {
|
||||
if (null === ($id = $reverseContainer->getId($callback[0]))) {
|
||||
return null;
|
||||
}
|
||||
$callback = ['@' . $id, (string) $callback[1]];
|
||||
}
|
||||
return new self($item, $pool, $callback);
|
||||
}
|
||||
public function getItem() : CacheItem
|
||||
{
|
||||
return $this->item;
|
||||
}
|
||||
public function getPool() : string
|
||||
{
|
||||
return $this->pool;
|
||||
}
|
||||
public function getCallback()
|
||||
{
|
||||
return $this->callback;
|
||||
}
|
||||
public function findPool(ReverseContainer $reverseContainer) : AdapterInterface
|
||||
{
|
||||
return $reverseContainer->getService($this->pool);
|
||||
}
|
||||
public function findCallback(ReverseContainer $reverseContainer) : callable
|
||||
{
|
||||
if (\is_string($callback = $this->callback)) {
|
||||
return '@' === $callback[0] ? $reverseContainer->getService(\substr($callback, 1)) : $callback;
|
||||
}
|
||||
if ('@' === $callback[0][0]) {
|
||||
$callback[0] = $reverseContainer->getService(\substr($callback[0], 1));
|
||||
}
|
||||
return $callback;
|
||||
}
|
||||
private function __construct(CacheItem $item, string $pool, $callback)
|
||||
{
|
||||
$this->item = $item;
|
||||
$this->pool = $pool;
|
||||
$this->callback = $callback;
|
||||
}
|
||||
}
|
22
dependencies/symfony/cache/PruneableInterface.php
vendored
Normal file
22
dependencies/symfony/cache/PruneableInterface.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache;
|
||||
|
||||
/**
|
||||
* Interface extends psr-6 and psr-16 caches to allow for pruning (deletion) of all expired cache items.
|
||||
*/
|
||||
interface PruneableInterface
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function prune();
|
||||
}
|
255
dependencies/symfony/cache/Psr16Cache.php
vendored
Normal file
255
dependencies/symfony/cache/Psr16Cache.php
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheException as Psr6CacheException;
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemPoolInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\SimpleCache\CacheException as SimpleCacheException;
|
||||
use WP_Ultimo\Dependencies\Psr\SimpleCache\CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
if (null !== (new \ReflectionMethod(CacheInterface::class, 'get'))->getReturnType()) {
|
||||
throw new \LogicException('psr/simple-cache 3.0+ is not compatible with this version of symfony/cache. Please upgrade symfony/cache to 6.0+ or downgrade psr/simple-cache to 1.x or 2.x.');
|
||||
}
|
||||
/**
|
||||
* Turns a PSR-6 cache into a PSR-16 one.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class Psr16Cache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use ProxyTrait;
|
||||
private const METADATA_EXPIRY_OFFSET = 1527506807;
|
||||
private $createCacheItem;
|
||||
private $cacheItemPrototype;
|
||||
public function __construct(CacheItemPoolInterface $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
if (!$pool instanceof AdapterInterface) {
|
||||
return;
|
||||
}
|
||||
$cacheItemPrototype =& $this->cacheItemPrototype;
|
||||
$createCacheItem = \Closure::bind(static function ($key, $value, $allowInt = \false) use(&$cacheItemPrototype) {
|
||||
$item = clone $cacheItemPrototype;
|
||||
$item->poolHash = $item->innerItem = null;
|
||||
if ($allowInt && \is_int($key)) {
|
||||
$item->key = (string) $key;
|
||||
} else {
|
||||
\assert('' !== CacheItem::validateKey($key));
|
||||
$item->key = $key;
|
||||
}
|
||||
$item->value = $value;
|
||||
$item->isHit = \false;
|
||||
return $item;
|
||||
}, null, CacheItem::class);
|
||||
$this->createCacheItem = function ($key, $value, $allowInt = \false) use($createCacheItem) {
|
||||
if (null === $this->cacheItemPrototype) {
|
||||
$this->get($allowInt && \is_int($key) ? (string) $key : $key);
|
||||
}
|
||||
$this->createCacheItem = $createCacheItem;
|
||||
return $createCacheItem($key, null, $allowInt)->set($value);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
try {
|
||||
$item = $this->pool->getItem($key);
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
if (null === $this->cacheItemPrototype) {
|
||||
$this->cacheItemPrototype = clone $item;
|
||||
$this->cacheItemPrototype->set(null);
|
||||
}
|
||||
return $item->isHit() ? $item->get() : $default;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
try {
|
||||
if (null !== ($f = $this->createCacheItem)) {
|
||||
$item = $f($key, $value);
|
||||
} else {
|
||||
$item = $this->pool->getItem($key)->set($value);
|
||||
}
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
if (null !== $ttl) {
|
||||
$item->expiresAfter($ttl);
|
||||
}
|
||||
return $this->pool->save($item);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
try {
|
||||
return $this->pool->deleteItem($key);
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
if ($keys instanceof \Traversable) {
|
||||
$keys = \iterator_to_array($keys, \false);
|
||||
} elseif (!\is_array($keys)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache keys must be array or Traversable, "%s" given.', \get_debug_type($keys)));
|
||||
}
|
||||
try {
|
||||
$items = $this->pool->getItems($keys);
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
$values = [];
|
||||
if (!$this->pool instanceof AdapterInterface) {
|
||||
foreach ($items as $key => $item) {
|
||||
$values[$key] = $item->isHit() ? $item->get() : $default;
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
foreach ($items as $key => $item) {
|
||||
if (!$item->isHit()) {
|
||||
$values[$key] = $default;
|
||||
continue;
|
||||
}
|
||||
$values[$key] = $item->get();
|
||||
if (!($metadata = $item->getMetadata())) {
|
||||
continue;
|
||||
}
|
||||
unset($metadata[CacheItem::METADATA_TAGS]);
|
||||
if ($metadata) {
|
||||
$values[$key] = ["\x9d" . \pack('VN', (int) (0.1 + $metadata[CacheItem::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[CacheItem::METADATA_CTIME]) . "_" => $values[$key]];
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
$valuesIsArray = \is_array($values);
|
||||
if (!$valuesIsArray && !$values instanceof \Traversable) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache values must be array or Traversable, "%s" given.', \get_debug_type($values)));
|
||||
}
|
||||
$items = [];
|
||||
try {
|
||||
if (null !== ($f = $this->createCacheItem)) {
|
||||
$valuesIsArray = \false;
|
||||
foreach ($values as $key => $value) {
|
||||
$items[$key] = $f($key, $value, \true);
|
||||
}
|
||||
} elseif ($valuesIsArray) {
|
||||
$items = [];
|
||||
foreach ($values as $key => $value) {
|
||||
$items[] = (string) $key;
|
||||
}
|
||||
$items = $this->pool->getItems($items);
|
||||
} else {
|
||||
foreach ($values as $key => $value) {
|
||||
if (\is_int($key)) {
|
||||
$key = (string) $key;
|
||||
}
|
||||
$items[$key] = $this->pool->getItem($key)->set($value);
|
||||
}
|
||||
}
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
$ok = \true;
|
||||
foreach ($items as $key => $item) {
|
||||
if ($valuesIsArray) {
|
||||
$item->set($values[$key]);
|
||||
}
|
||||
if (null !== $ttl) {
|
||||
$item->expiresAfter($ttl);
|
||||
}
|
||||
$ok = $this->pool->saveDeferred($item) && $ok;
|
||||
}
|
||||
return $this->pool->commit() && $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
if ($keys instanceof \Traversable) {
|
||||
$keys = \iterator_to_array($keys, \false);
|
||||
} elseif (!\is_array($keys)) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache keys must be array or Traversable, "%s" given.', \get_debug_type($keys)));
|
||||
}
|
||||
try {
|
||||
return $this->pool->deleteItems($keys);
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
try {
|
||||
return $this->pool->hasItem($key);
|
||||
} catch (SimpleCacheException $e) {
|
||||
throw $e;
|
||||
} catch (Psr6CacheException $e) {
|
||||
throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
}
|
19
dependencies/symfony/cache/ResettableInterface.php
vendored
Normal file
19
dependencies/symfony/cache/ResettableInterface.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Service\ResetInterface;
|
||||
/**
|
||||
* Resets a pool's local state.
|
||||
*/
|
||||
interface ResettableInterface extends ResetInterface
|
||||
{
|
||||
}
|
372
dependencies/symfony/cache/Traits/AbstractAdapterTrait.php
vendored
Normal file
372
dependencies/symfony/cache/Traits/AbstractAdapterTrait.php
vendored
Normal file
@ -0,0 +1,372 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Cache\CacheItemInterface;
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerAwareTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait AbstractAdapterTrait
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
/**
|
||||
* @var \Closure needs to be set by class, signature is function(string <key>, mixed <value>, bool <isHit>)
|
||||
*/
|
||||
private static $createCacheItem;
|
||||
/**
|
||||
* @var \Closure needs to be set by class, signature is function(array <deferred>, string <namespace>, array <&expiredIds>)
|
||||
*/
|
||||
private static $mergeByLifetime;
|
||||
private $namespace = '';
|
||||
private $defaultLifetime;
|
||||
private $namespaceVersion = '';
|
||||
private $versioningIsEnabled = \false;
|
||||
private $deferred = [];
|
||||
private $ids = [];
|
||||
/**
|
||||
* @var int|null The maximum length to enforce for identifiers or null when no limit applies
|
||||
*/
|
||||
protected $maxIdLength;
|
||||
/**
|
||||
* Fetches several cache items.
|
||||
*
|
||||
* @param array $ids The cache identifiers to fetch
|
||||
*
|
||||
* @return array|\Traversable
|
||||
*/
|
||||
protected abstract function doFetch(array $ids);
|
||||
/**
|
||||
* Confirms if the cache contains specified cache item.
|
||||
*
|
||||
* @param string $id The identifier for which to check existence
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected abstract function doHave(string $id);
|
||||
/**
|
||||
* Deletes all items in the pool.
|
||||
*
|
||||
* @param string $namespace The prefix used for all identifiers managed by this pool
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected abstract function doClear(string $namespace);
|
||||
/**
|
||||
* Removes multiple items from the pool.
|
||||
*
|
||||
* @param array $ids An array of identifiers that should be removed from the pool
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected abstract function doDelete(array $ids);
|
||||
/**
|
||||
* Persists several cache items immediately.
|
||||
*
|
||||
* @param array $values The values to cache, indexed by their cache identifier
|
||||
* @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning
|
||||
*
|
||||
* @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not
|
||||
*/
|
||||
protected abstract function doSave(array $values, int $lifetime);
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
$id = $this->getId($key);
|
||||
if (isset($this->deferred[$key])) {
|
||||
$this->commit();
|
||||
}
|
||||
try {
|
||||
return $this->doHave($id);
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to check if key "{key}" is cached: ' . $e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(string $prefix = '')
|
||||
{
|
||||
$this->deferred = [];
|
||||
if ($cleared = $this->versioningIsEnabled) {
|
||||
if ('' === ($namespaceVersionToClear = $this->namespaceVersion)) {
|
||||
foreach ($this->doFetch([static::NS_SEPARATOR . $this->namespace]) as $v) {
|
||||
$namespaceVersionToClear = $v;
|
||||
}
|
||||
}
|
||||
$namespaceToClear = $this->namespace . $namespaceVersionToClear;
|
||||
$namespaceVersion = self::formatNamespaceVersion(\mt_rand());
|
||||
try {
|
||||
$e = $this->doSave([static::NS_SEPARATOR . $this->namespace => $namespaceVersion], 0);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (\true !== $e && [] !== $e) {
|
||||
$cleared = \false;
|
||||
$message = 'Failed to save the new namespace' . ($e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => \get_debug_type($this)]);
|
||||
} else {
|
||||
$this->namespaceVersion = $namespaceVersion;
|
||||
$this->ids = [];
|
||||
}
|
||||
} else {
|
||||
$namespaceToClear = $this->namespace . $prefix;
|
||||
}
|
||||
try {
|
||||
return $this->doClear($namespaceToClear) || $cleared;
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to clear the cache: ' . $e->getMessage(), ['exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
return \false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
return $this->deleteItems([$key]);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItems(array $keys)
|
||||
{
|
||||
$ids = [];
|
||||
foreach ($keys as $key) {
|
||||
$ids[$key] = $this->getId($key);
|
||||
unset($this->deferred[$key]);
|
||||
}
|
||||
try {
|
||||
if ($this->doDelete($ids)) {
|
||||
return \true;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
$ok = \true;
|
||||
// When bulk-delete failed, retry each item individually
|
||||
foreach ($ids as $key => $id) {
|
||||
try {
|
||||
$e = null;
|
||||
if ($this->doDelete([$id])) {
|
||||
continue;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
$message = 'Failed to delete key "{key}"' . ($e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
$ok = \false;
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$id = $this->getId($key);
|
||||
if (isset($this->deferred[$key])) {
|
||||
$this->commit();
|
||||
}
|
||||
$isHit = \false;
|
||||
$value = null;
|
||||
try {
|
||||
foreach ($this->doFetch([$id]) as $value) {
|
||||
$isHit = \true;
|
||||
}
|
||||
return (self::$createCacheItem)($key, $value, $isHit);
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch key "{key}": ' . $e->getMessage(), ['key' => $key, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
return (self::$createCacheItem)($key, null, \false);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
$ids = [];
|
||||
$commit = \false;
|
||||
foreach ($keys as $key) {
|
||||
$ids[] = $this->getId($key);
|
||||
$commit = $commit || isset($this->deferred[$key]);
|
||||
}
|
||||
if ($commit) {
|
||||
$this->commit();
|
||||
}
|
||||
try {
|
||||
$items = $this->doFetch($ids);
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch items: ' . $e->getMessage(), ['keys' => $keys, 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
$items = [];
|
||||
}
|
||||
$ids = \array_combine($ids, $keys);
|
||||
return $this->generateItems($items, $ids);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function save(CacheItemInterface $item)
|
||||
{
|
||||
if (!$item instanceof CacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$this->deferred[$item->getKey()] = $item;
|
||||
return $this->commit();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function saveDeferred(CacheItemInterface $item)
|
||||
{
|
||||
if (!$item instanceof CacheItem) {
|
||||
return \false;
|
||||
}
|
||||
$this->deferred[$item->getKey()] = $item;
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* Enables/disables versioning of items.
|
||||
*
|
||||
* When versioning is enabled, clearing the cache is atomic and doesn't require listing existing keys to proceed,
|
||||
* but old keys may need garbage collection and extra round-trips to the back-end are required.
|
||||
*
|
||||
* Calling this method also clears the memoized namespace version and thus forces a resynchronization of it.
|
||||
*
|
||||
* @return bool the previous state of versioning
|
||||
*/
|
||||
public function enableVersioning(bool $enable = \true)
|
||||
{
|
||||
$wasEnabled = $this->versioningIsEnabled;
|
||||
$this->versioningIsEnabled = $enable;
|
||||
$this->namespaceVersion = '';
|
||||
$this->ids = [];
|
||||
return $wasEnabled;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->deferred) {
|
||||
$this->commit();
|
||||
}
|
||||
$this->namespaceVersion = '';
|
||||
$this->ids = [];
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize ' . __CLASS__);
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__);
|
||||
}
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->deferred) {
|
||||
$this->commit();
|
||||
}
|
||||
}
|
||||
private function generateItems(iterable $items, array &$keys) : \Generator
|
||||
{
|
||||
$f = self::$createCacheItem;
|
||||
try {
|
||||
foreach ($items as $id => $value) {
|
||||
if (!isset($keys[$id])) {
|
||||
throw new InvalidArgumentException(\sprintf('Could not match value id "%s" to keys "%s".', $id, \implode('", "', $keys)));
|
||||
}
|
||||
$key = $keys[$id];
|
||||
unset($keys[$id]);
|
||||
(yield $key => $f($key, $value, \true));
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch items: ' . $e->getMessage(), ['keys' => \array_values($keys), 'exception' => $e, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
(yield $key => $f($key, null, \false));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function getId($key)
|
||||
{
|
||||
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
|
||||
$this->ids = [];
|
||||
$this->namespaceVersion = '1' . static::NS_SEPARATOR;
|
||||
try {
|
||||
foreach ($this->doFetch([static::NS_SEPARATOR . $this->namespace]) as $v) {
|
||||
$this->namespaceVersion = $v;
|
||||
}
|
||||
$e = \true;
|
||||
if ('1' . static::NS_SEPARATOR === $this->namespaceVersion) {
|
||||
$this->namespaceVersion = self::formatNamespaceVersion(\time());
|
||||
$e = $this->doSave([static::NS_SEPARATOR . $this->namespace => $this->namespaceVersion], 0);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (\true !== $e && [] !== $e) {
|
||||
$message = 'Failed to save the new namespace' . ($e instanceof \Exception ? ': ' . $e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['exception' => $e instanceof \Exception ? $e : null, 'cache-adapter' => \get_debug_type($this)]);
|
||||
}
|
||||
}
|
||||
if (\is_string($key) && isset($this->ids[$key])) {
|
||||
return $this->namespace . $this->namespaceVersion . $this->ids[$key];
|
||||
}
|
||||
\assert('' !== CacheItem::validateKey($key));
|
||||
$this->ids[$key] = $key;
|
||||
if (\count($this->ids) > 1000) {
|
||||
$this->ids = \array_slice($this->ids, 500, null, \true);
|
||||
// stop memory leak if there are many keys
|
||||
}
|
||||
if (null === $this->maxIdLength) {
|
||||
return $this->namespace . $this->namespaceVersion . $key;
|
||||
}
|
||||
if (\strlen($id = $this->namespace . $this->namespaceVersion . $key) > $this->maxIdLength) {
|
||||
// Use MD5 to favor speed over security, which is not an issue here
|
||||
$this->ids[$key] = $id = \substr_replace(\base64_encode(\hash('md5', $key, \true)), static::NS_SEPARATOR, -(\strlen($this->namespaceVersion) + 2));
|
||||
$id = $this->namespace . $this->namespaceVersion . $id;
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function handleUnserializeCallback(string $class)
|
||||
{
|
||||
throw new \DomainException('Class not found: ' . $class);
|
||||
}
|
||||
private static function formatNamespaceVersion(int $value) : string
|
||||
{
|
||||
return \strtr(\substr_replace(\base64_encode(\pack('V', $value)), static::NS_SEPARATOR, 5), '/', '_');
|
||||
}
|
||||
}
|
89
dependencies/symfony/cache/Traits/ContractsTrait.php
vendored
Normal file
89
dependencies/symfony/cache/Traits/ContractsTrait.php
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
use WP_Ultimo\Dependencies\Psr\Log\LoggerInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\CacheItem;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\LockRegistry;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\CacheTrait;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Cache\ItemInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait ContractsTrait
|
||||
{
|
||||
use CacheTrait {
|
||||
doGet as private contractsGet;
|
||||
}
|
||||
private $callbackWrapper;
|
||||
private $computing = [];
|
||||
/**
|
||||
* Wraps the callback passed to ->get() in a callable.
|
||||
*
|
||||
* @return callable the previous callback wrapper
|
||||
*/
|
||||
public function setCallbackWrapper(?callable $callbackWrapper) : callable
|
||||
{
|
||||
if (!isset($this->callbackWrapper)) {
|
||||
$this->callbackWrapper = \Closure::fromCallable([LockRegistry::class, 'compute']);
|
||||
if (\in_array(\PHP_SAPI, ['cli', 'phpdbg'], \true)) {
|
||||
$this->setCallbackWrapper(null);
|
||||
}
|
||||
}
|
||||
$previousWrapper = $this->callbackWrapper;
|
||||
$this->callbackWrapper = $callbackWrapper ?? static function (callable $callback, ItemInterface $item, bool &$save, CacheInterface $pool, \Closure $setMetadata, ?LoggerInterface $logger) {
|
||||
return $callback($item, $save);
|
||||
};
|
||||
return $previousWrapper;
|
||||
}
|
||||
private function doGet(AdapterInterface $pool, string $key, callable $callback, ?float $beta, array &$metadata = null)
|
||||
{
|
||||
if (0 > ($beta = $beta ?? 1.0)) {
|
||||
throw new InvalidArgumentException(\sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta));
|
||||
}
|
||||
static $setMetadata;
|
||||
$setMetadata ?? ($setMetadata = \Closure::bind(static function (CacheItem $item, float $startTime, ?array &$metadata) {
|
||||
if ($item->expiry > ($endTime = \microtime(\true))) {
|
||||
$item->newMetadata[CacheItem::METADATA_EXPIRY] = $metadata[CacheItem::METADATA_EXPIRY] = $item->expiry;
|
||||
$item->newMetadata[CacheItem::METADATA_CTIME] = $metadata[CacheItem::METADATA_CTIME] = (int) \ceil(1000 * ($endTime - $startTime));
|
||||
} else {
|
||||
unset($metadata[CacheItem::METADATA_EXPIRY], $metadata[CacheItem::METADATA_CTIME]);
|
||||
}
|
||||
}, null, CacheItem::class));
|
||||
return $this->contractsGet($pool, $key, function (CacheItem $item, bool &$save) use($pool, $callback, $setMetadata, &$metadata, $key) {
|
||||
// don't wrap nor save recursive calls
|
||||
if (isset($this->computing[$key])) {
|
||||
$value = $callback($item, $save);
|
||||
$save = \false;
|
||||
return $value;
|
||||
}
|
||||
$this->computing[$key] = $key;
|
||||
$startTime = \microtime(\true);
|
||||
if (!isset($this->callbackWrapper)) {
|
||||
$this->setCallbackWrapper($this->setCallbackWrapper(null));
|
||||
}
|
||||
try {
|
||||
$value = ($this->callbackWrapper)($callback, $item, $save, $pool, function (CacheItem $item) use($setMetadata, $startTime, &$metadata) {
|
||||
$setMetadata($item, $startTime, $metadata);
|
||||
}, $this->logger ?? null);
|
||||
$setMetadata($item, $startTime, $metadata);
|
||||
return $value;
|
||||
} finally {
|
||||
unset($this->computing[$key]);
|
||||
}
|
||||
}, $beta, $metadata, $this->logger ?? null);
|
||||
}
|
||||
}
|
168
dependencies/symfony/cache/Traits/FilesystemCommonTrait.php
vendored
Normal file
168
dependencies/symfony/cache/Traits/FilesystemCommonTrait.php
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait FilesystemCommonTrait
|
||||
{
|
||||
private $directory;
|
||||
private $tmp;
|
||||
private function init(string $namespace, ?string $directory)
|
||||
{
|
||||
if (!isset($directory[0])) {
|
||||
$directory = \sys_get_temp_dir() . \DIRECTORY_SEPARATOR . 'symfony-cache';
|
||||
} else {
|
||||
$directory = \realpath($directory) ?: $directory;
|
||||
}
|
||||
if (isset($namespace[0])) {
|
||||
if (\preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
||||
throw new InvalidArgumentException(\sprintf('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
||||
}
|
||||
$directory .= \DIRECTORY_SEPARATOR . $namespace;
|
||||
} else {
|
||||
$directory .= \DIRECTORY_SEPARATOR . '@';
|
||||
}
|
||||
if (!\is_dir($directory)) {
|
||||
@\mkdir($directory, 0777, \true);
|
||||
}
|
||||
$directory .= \DIRECTORY_SEPARATOR;
|
||||
// On Windows the whole path is limited to 258 chars
|
||||
if ('\\' === \DIRECTORY_SEPARATOR && \strlen($directory) > 234) {
|
||||
throw new InvalidArgumentException(\sprintf('Cache directory too long (%s).', $directory));
|
||||
}
|
||||
$this->directory = $directory;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
$ok = \true;
|
||||
foreach ($this->scanHashDir($this->directory) as $file) {
|
||||
if ('' !== $namespace && !\str_starts_with($this->getFileKey($file), $namespace)) {
|
||||
continue;
|
||||
}
|
||||
$ok = ($this->doUnlink($file) || !\file_exists($file)) && $ok;
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
$ok = \true;
|
||||
foreach ($ids as $id) {
|
||||
$file = $this->getFile($id);
|
||||
$ok = (!\is_file($file) || $this->doUnlink($file) || !\file_exists($file)) && $ok;
|
||||
}
|
||||
return $ok;
|
||||
}
|
||||
protected function doUnlink(string $file)
|
||||
{
|
||||
return @\unlink($file);
|
||||
}
|
||||
private function write(string $file, string $data, int $expiresAt = null)
|
||||
{
|
||||
\set_error_handler(__CLASS__ . '::throwError');
|
||||
try {
|
||||
if (null === $this->tmp) {
|
||||
$this->tmp = $this->directory . \bin2hex(\random_bytes(6));
|
||||
}
|
||||
try {
|
||||
$h = \fopen($this->tmp, 'x');
|
||||
} catch (\ErrorException $e) {
|
||||
if (!\str_contains($e->getMessage(), 'File exists')) {
|
||||
throw $e;
|
||||
}
|
||||
$this->tmp = $this->directory . \bin2hex(\random_bytes(6));
|
||||
$h = \fopen($this->tmp, 'x');
|
||||
}
|
||||
\fwrite($h, $data);
|
||||
\fclose($h);
|
||||
if (null !== $expiresAt) {
|
||||
\touch($this->tmp, $expiresAt ?: \time() + 31556952);
|
||||
// 1 year in seconds
|
||||
}
|
||||
return \rename($this->tmp, $file);
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
}
|
||||
private function getFile(string $id, bool $mkdir = \false, string $directory = null)
|
||||
{
|
||||
// Use MD5 to favor speed over security, which is not an issue here
|
||||
$hash = \str_replace('/', '-', \base64_encode(\hash('md5', static::class . $id, \true)));
|
||||
$dir = ($directory ?? $this->directory) . \strtoupper($hash[0] . \DIRECTORY_SEPARATOR . $hash[1] . \DIRECTORY_SEPARATOR);
|
||||
if ($mkdir && !\is_dir($dir)) {
|
||||
@\mkdir($dir, 0777, \true);
|
||||
}
|
||||
return $dir . \substr($hash, 2, 20);
|
||||
}
|
||||
private function getFileKey(string $file) : string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
private function scanHashDir(string $directory) : \Generator
|
||||
{
|
||||
if (!\is_dir($directory)) {
|
||||
return;
|
||||
}
|
||||
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
for ($i = 0; $i < 38; ++$i) {
|
||||
if (!\is_dir($directory . $chars[$i])) {
|
||||
continue;
|
||||
}
|
||||
for ($j = 0; $j < 38; ++$j) {
|
||||
if (!\is_dir($dir = $directory . $chars[$i] . \DIRECTORY_SEPARATOR . $chars[$j])) {
|
||||
continue;
|
||||
}
|
||||
foreach (@\scandir($dir, \SCANDIR_SORT_NONE) ?: [] as $file) {
|
||||
if ('.' !== $file && '..' !== $file) {
|
||||
(yield $dir . \DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function throwError(int $type, string $message, string $file, int $line)
|
||||
{
|
||||
throw new \ErrorException($message, 0, $type, $file, $line);
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function __sleep()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot serialize ' . __CLASS__);
|
||||
}
|
||||
public function __wakeup()
|
||||
{
|
||||
throw new \BadMethodCallException('Cannot unserialize ' . __CLASS__);
|
||||
}
|
||||
public function __destruct()
|
||||
{
|
||||
if (\method_exists(parent::class, '__destruct')) {
|
||||
parent::__destruct();
|
||||
}
|
||||
if (null !== $this->tmp && \is_file($this->tmp)) {
|
||||
\unlink($this->tmp);
|
||||
}
|
||||
}
|
||||
}
|
106
dependencies/symfony/cache/Traits/FilesystemTrait.php
vendored
Normal file
106
dependencies/symfony/cache/Traits/FilesystemTrait.php
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait FilesystemTrait
|
||||
{
|
||||
use FilesystemCommonTrait;
|
||||
private $marshaller;
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
$time = \time();
|
||||
$pruned = \true;
|
||||
foreach ($this->scanHashDir($this->directory) as $file) {
|
||||
if (!($h = @\fopen($file, 'r'))) {
|
||||
continue;
|
||||
}
|
||||
if (($expiresAt = (int) \fgets($h)) && $time >= $expiresAt) {
|
||||
\fclose($h);
|
||||
$pruned = (@\unlink($file) || !\file_exists($file)) && $pruned;
|
||||
} else {
|
||||
\fclose($h);
|
||||
}
|
||||
}
|
||||
return $pruned;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
$values = [];
|
||||
$now = \time();
|
||||
foreach ($ids as $id) {
|
||||
$file = $this->getFile($id);
|
||||
if (!\is_file($file) || !($h = @\fopen($file, 'r'))) {
|
||||
continue;
|
||||
}
|
||||
if (($expiresAt = (int) \fgets($h)) && $now >= $expiresAt) {
|
||||
\fclose($h);
|
||||
@\unlink($file);
|
||||
} else {
|
||||
$i = \rawurldecode(\rtrim(\fgets($h)));
|
||||
$value = \stream_get_contents($h);
|
||||
\fclose($h);
|
||||
if ($i === $id) {
|
||||
$values[$id] = $this->marshaller->unmarshall($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $values;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
$file = $this->getFile($id);
|
||||
return \is_file($file) && (@\filemtime($file) > \time() || $this->doFetch([$id]));
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
$expiresAt = $lifetime ? \time() + $lifetime : 0;
|
||||
$values = $this->marshaller->marshall($values, $failed);
|
||||
foreach ($values as $id => $value) {
|
||||
if (!$this->write($this->getFile($id, \true), $expiresAt . "\n" . \rawurlencode($id) . "\n" . $value, $expiresAt)) {
|
||||
$failed[] = $id;
|
||||
}
|
||||
}
|
||||
if ($failed && !\is_writable($this->directory)) {
|
||||
throw new CacheException(\sprintf('Cache directory is not writable (%s).', $this->directory));
|
||||
}
|
||||
return $failed;
|
||||
}
|
||||
private function getFileKey(string $file) : string
|
||||
{
|
||||
if (!($h = @\fopen($file, 'r'))) {
|
||||
return '';
|
||||
}
|
||||
\fgets($h);
|
||||
// expiry
|
||||
$encodedKey = \fgets($h);
|
||||
\fclose($h);
|
||||
return \rawurldecode(\rtrim($encodedKey));
|
||||
}
|
||||
}
|
39
dependencies/symfony/cache/Traits/ProxyTrait.php
vendored
Normal file
39
dependencies/symfony/cache/Traits/ProxyTrait.php
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\PruneableInterface;
|
||||
use WP_Ultimo\Dependencies\Symfony\Contracts\Service\ResetInterface;
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait ProxyTrait
|
||||
{
|
||||
private $pool;
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
return $this->pool instanceof PruneableInterface && $this->pool->prune();
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->pool instanceof ResetInterface) {
|
||||
$this->pool->reset();
|
||||
}
|
||||
}
|
||||
}
|
48
dependencies/symfony/cache/Traits/RedisClusterNodeProxy.php
vendored
Normal file
48
dependencies/symfony/cache/Traits/RedisClusterNodeProxy.php
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
/**
|
||||
* This file acts as a wrapper to the \RedisCluster implementation so it can accept the same type of calls as
|
||||
* individual \Redis objects.
|
||||
*
|
||||
* Calls are made to individual nodes via: RedisCluster->{method}($host, ...args)'
|
||||
* according to https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#directed-node-commands
|
||||
*
|
||||
* @author Jack Thomas <jack.thomas@solidalpha.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RedisClusterNodeProxy
|
||||
{
|
||||
private $host;
|
||||
private $redis;
|
||||
/**
|
||||
* @param \RedisCluster|RedisClusterProxy $redis
|
||||
*/
|
||||
public function __construct(array $host, $redis)
|
||||
{
|
||||
$this->host = $host;
|
||||
$this->redis = $redis;
|
||||
}
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
return $this->redis->{$method}($this->host, ...$args);
|
||||
}
|
||||
public function scan(&$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
return $this->redis->scan($iIterator, $this->host, $strPattern, $iCount);
|
||||
}
|
||||
public function getOption($name)
|
||||
{
|
||||
return $this->redis->getOption($name);
|
||||
}
|
||||
}
|
51
dependencies/symfony/cache/Traits/RedisClusterProxy.php
vendored
Normal file
51
dependencies/symfony/cache/Traits/RedisClusterProxy.php
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
/**
|
||||
* @author Alessandro Chitolina <alekitto@gmail.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RedisClusterProxy
|
||||
{
|
||||
private $redis;
|
||||
private $initializer;
|
||||
public function __construct(\Closure $initializer)
|
||||
{
|
||||
$this->initializer = $initializer;
|
||||
}
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
$this->redis ?: ($this->redis = $this->initializer->__invoke());
|
||||
return $this->redis->{$method}(...$args);
|
||||
}
|
||||
public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->redis ?: ($this->redis = $this->initializer->__invoke());
|
||||
return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount);
|
||||
}
|
||||
public function scan(&$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->redis ?: ($this->redis = $this->initializer->__invoke());
|
||||
return $this->redis->scan($iIterator, $strPattern, $iCount);
|
||||
}
|
||||
public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->redis ?: ($this->redis = $this->initializer->__invoke());
|
||||
return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount);
|
||||
}
|
||||
public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->redis ?: ($this->redis = $this->initializer->__invoke());
|
||||
return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount);
|
||||
}
|
||||
}
|
53
dependencies/symfony/cache/Traits/RedisProxy.php
vendored
Normal file
53
dependencies/symfony/cache/Traits/RedisProxy.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class RedisProxy
|
||||
{
|
||||
private $redis;
|
||||
private $initializer;
|
||||
private $ready = \false;
|
||||
public function __construct(\Redis $redis, \Closure $initializer)
|
||||
{
|
||||
$this->redis = $redis;
|
||||
$this->initializer = $initializer;
|
||||
}
|
||||
public function __call(string $method, array $args)
|
||||
{
|
||||
$this->ready ?: ($this->ready = $this->initializer->__invoke($this->redis));
|
||||
return $this->redis->{$method}(...$args);
|
||||
}
|
||||
public function hscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->ready ?: ($this->ready = $this->initializer->__invoke($this->redis));
|
||||
return $this->redis->hscan($strKey, $iIterator, $strPattern, $iCount);
|
||||
}
|
||||
public function scan(&$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->ready ?: ($this->ready = $this->initializer->__invoke($this->redis));
|
||||
return $this->redis->scan($iIterator, $strPattern, $iCount);
|
||||
}
|
||||
public function sscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->ready ?: ($this->ready = $this->initializer->__invoke($this->redis));
|
||||
return $this->redis->sscan($strKey, $iIterator, $strPattern, $iCount);
|
||||
}
|
||||
public function zscan($strKey, &$iIterator, $strPattern = null, $iCount = null)
|
||||
{
|
||||
$this->ready ?: ($this->ready = $this->initializer->__invoke($this->redis));
|
||||
return $this->redis->zscan($strKey, $iIterator, $strPattern, $iCount);
|
||||
}
|
||||
}
|
557
dependencies/symfony/cache/Traits/RedisTrait.php
vendored
Normal file
557
dependencies/symfony/cache/Traits/RedisTrait.php
vendored
Normal file
@ -0,0 +1,557 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
namespace WP_Ultimo\Dependencies\Symfony\Component\Cache\Traits;
|
||||
|
||||
use WP_Ultimo\Dependencies\Predis\Command\Redis\UNLINK;
|
||||
use WP_Ultimo\Dependencies\Predis\Connection\Aggregate\ClusterInterface;
|
||||
use WP_Ultimo\Dependencies\Predis\Connection\Aggregate\RedisCluster;
|
||||
use WP_Ultimo\Dependencies\Predis\Connection\Aggregate\ReplicationInterface;
|
||||
use WP_Ultimo\Dependencies\Predis\Response\ErrorInterface;
|
||||
use WP_Ultimo\Dependencies\Predis\Response\Status;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\CacheException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use WP_Ultimo\Dependencies\Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
/**
|
||||
* @author Aurimas Niekis <aurimas@niekis.lt>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait RedisTrait
|
||||
{
|
||||
private static $defaultConnectionOptions = ['class' => null, 'persistent' => 0, 'persistent_id' => null, 'timeout' => 30, 'read_timeout' => 0, 'retry_interval' => 0, 'tcp_keepalive' => 0, 'lazy' => null, 'redis_cluster' => \false, 'redis_sentinel' => null, 'dbindex' => 0, 'failover' => 'none', 'ssl' => null];
|
||||
private $redis;
|
||||
private $marshaller;
|
||||
/**
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis
|
||||
*/
|
||||
private function init($redis, string $namespace, int $defaultLifetime, ?MarshallerInterface $marshaller)
|
||||
{
|
||||
parent::__construct($namespace, $defaultLifetime);
|
||||
if (\preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
|
||||
throw new InvalidArgumentException(\sprintf('RedisAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
|
||||
}
|
||||
if (!$redis instanceof \Redis && !$redis instanceof \RedisArray && !$redis instanceof \RedisCluster && !$redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && !$redis instanceof RedisProxy && !$redis instanceof RedisClusterProxy) {
|
||||
throw new InvalidArgumentException(\sprintf('"%s()" expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\\ClientInterface, "%s" given.', __METHOD__, \get_debug_type($redis)));
|
||||
}
|
||||
if ($redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && $redis->getOptions()->exceptions) {
|
||||
$options = clone $redis->getOptions();
|
||||
\Closure::bind(function () {
|
||||
$this->options['exceptions'] = \false;
|
||||
}, $options, $options)();
|
||||
$redis = new $redis($redis->getConnection(), $options);
|
||||
}
|
||||
$this->redis = $redis;
|
||||
$this->marshaller = $marshaller ?? new DefaultMarshaller();
|
||||
}
|
||||
/**
|
||||
* Creates a Redis connection using a DSN configuration.
|
||||
*
|
||||
* Example DSN:
|
||||
* - redis://localhost
|
||||
* - redis://example.com:1234
|
||||
* - redis://secret@example.com/13
|
||||
* - redis:///var/run/redis.sock
|
||||
* - redis://secret@/var/run/redis.sock/13
|
||||
*
|
||||
* @param array $options See self::$defaultConnectionOptions
|
||||
*
|
||||
* @return \Redis|\RedisArray|\RedisCluster|RedisClusterProxy|RedisProxy|\Predis\ClientInterface According to the "class" option
|
||||
*
|
||||
* @throws InvalidArgumentException when the DSN is invalid
|
||||
*/
|
||||
public static function createConnection(string $dsn, array $options = [])
|
||||
{
|
||||
if (\str_starts_with($dsn, 'redis:')) {
|
||||
$scheme = 'redis';
|
||||
} elseif (\str_starts_with($dsn, 'rediss:')) {
|
||||
$scheme = 'rediss';
|
||||
} else {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Redis DSN: "%s" does not start with "redis:" or "rediss".', $dsn));
|
||||
}
|
||||
if (!\extension_loaded('redis') && !\class_exists(\WP_Ultimo\Dependencies\Predis\Client::class)) {
|
||||
throw new CacheException(\sprintf('Cannot find the "redis" extension nor the "predis/predis" package: "%s".', $dsn));
|
||||
}
|
||||
$params = \preg_replace_callback('#^' . $scheme . ':(//)?(?:(?:[^:@]*+:)?([^@]*+)@)?#', function ($m) use(&$auth) {
|
||||
if (isset($m[2])) {
|
||||
$auth = $m[2];
|
||||
if ('' === $auth) {
|
||||
$auth = null;
|
||||
}
|
||||
}
|
||||
return 'file:' . ($m[1] ?? '');
|
||||
}, $dsn);
|
||||
if (\false === ($params = \parse_url($params))) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Redis DSN: "%s".', $dsn));
|
||||
}
|
||||
$query = $hosts = [];
|
||||
$tls = 'rediss' === $scheme;
|
||||
$tcpScheme = $tls ? 'tls' : 'tcp';
|
||||
if (isset($params['query'])) {
|
||||
\parse_str($params['query'], $query);
|
||||
if (isset($query['host'])) {
|
||||
if (!\is_array($hosts = $query['host'])) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Redis DSN: "%s".', $dsn));
|
||||
}
|
||||
foreach ($hosts as $host => $parameters) {
|
||||
if (\is_string($parameters)) {
|
||||
\parse_str($parameters, $parameters);
|
||||
}
|
||||
if (\false === ($i = \strrpos($host, ':'))) {
|
||||
$hosts[$host] = ['scheme' => $tcpScheme, 'host' => $host, 'port' => 6379] + $parameters;
|
||||
} elseif ($port = (int) \substr($host, 1 + $i)) {
|
||||
$hosts[$host] = ['scheme' => $tcpScheme, 'host' => \substr($host, 0, $i), 'port' => $port] + $parameters;
|
||||
} else {
|
||||
$hosts[$host] = ['scheme' => 'unix', 'path' => \substr($host, 0, $i)] + $parameters;
|
||||
}
|
||||
}
|
||||
$hosts = \array_values($hosts);
|
||||
}
|
||||
}
|
||||
if (isset($params['host']) || isset($params['path'])) {
|
||||
if (!isset($params['dbindex']) && isset($params['path'])) {
|
||||
if (\preg_match('#/(\\d+)?$#', $params['path'], $m)) {
|
||||
$params['dbindex'] = $m[1] ?? '0';
|
||||
$params['path'] = \substr($params['path'], 0, -\strlen($m[0]));
|
||||
} elseif (isset($params['host'])) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Redis DSN: "%s", the "dbindex" parameter must be a number.', $dsn));
|
||||
}
|
||||
}
|
||||
if (isset($params['host'])) {
|
||||
\array_unshift($hosts, ['scheme' => $tcpScheme, 'host' => $params['host'], 'port' => $params['port'] ?? 6379]);
|
||||
} else {
|
||||
\array_unshift($hosts, ['scheme' => 'unix', 'path' => $params['path']]);
|
||||
}
|
||||
}
|
||||
if (!$hosts) {
|
||||
throw new InvalidArgumentException(\sprintf('Invalid Redis DSN: "%s".', $dsn));
|
||||
}
|
||||
$params += $query + $options + self::$defaultConnectionOptions;
|
||||
if (isset($params['redis_sentinel']) && !\class_exists(\WP_Ultimo\Dependencies\Predis\Client::class) && !\class_exists(\RedisSentinel::class)) {
|
||||
throw new CacheException(\sprintf('Redis Sentinel support requires the "predis/predis" package or the "redis" extension v5.2 or higher: "%s".', $dsn));
|
||||
}
|
||||
if (isset($params['lazy'])) {
|
||||
$params['lazy'] = \filter_var($params['lazy'], \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
$params['redis_cluster'] = \filter_var($params['redis_cluster'], \FILTER_VALIDATE_BOOLEAN);
|
||||
if ($params['redis_cluster'] && isset($params['redis_sentinel'])) {
|
||||
throw new InvalidArgumentException(\sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: "%s".', $dsn));
|
||||
}
|
||||
if (null === $params['class'] && \extension_loaded('redis')) {
|
||||
$class = $params['redis_cluster'] ? \RedisCluster::class : (1 < \count($hosts) && !isset($params['redis_sentinel']) ? \RedisArray::class : \Redis::class);
|
||||
} else {
|
||||
$class = $params['class'] ?? \WP_Ultimo\Dependencies\Predis\Client::class;
|
||||
if (isset($params['redis_sentinel']) && !\is_a($class, \WP_Ultimo\Dependencies\Predis\Client::class, \true) && !\class_exists(\RedisSentinel::class)) {
|
||||
throw new CacheException(\sprintf('Cannot use Redis Sentinel: class "%s" does not extend "Predis\\Client" and ext-redis >= 5.2 not found: "%s".', $class, $dsn));
|
||||
}
|
||||
}
|
||||
if (\is_a($class, \Redis::class, \true)) {
|
||||
$connect = $params['persistent'] || $params['persistent_id'] ? 'pconnect' : 'connect';
|
||||
$redis = new $class();
|
||||
$initializer = static function ($redis) use($connect, $params, $dsn, $auth, $hosts, $tls) {
|
||||
$hostIndex = 0;
|
||||
do {
|
||||
$host = $hosts[$hostIndex]['host'] ?? $hosts[$hostIndex]['path'];
|
||||
$port = $hosts[$hostIndex]['port'] ?? 0;
|
||||
$passAuth = \defined('Redis::OPT_NULL_MULTIBULK_AS_NULL') && isset($params['auth']);
|
||||
$address = \false;
|
||||
if (isset($hosts[$hostIndex]['host']) && $tls) {
|
||||
$host = 'tls://' . $host;
|
||||
}
|
||||
if (!isset($params['redis_sentinel'])) {
|
||||
break;
|
||||
}
|
||||
if (\version_compare(\phpversion('redis'), '6.0.0', '>=')) {
|
||||
$options = ['host' => $host, 'port' => $port, 'connectTimeout' => $params['timeout'], 'persistent' => $params['persistent_id'], 'retryInterval' => $params['retry_interval'], 'readTimeout' => $params['read_timeout']];
|
||||
if ($passAuth) {
|
||||
$options['auth'] = $params['auth'];
|
||||
}
|
||||
$sentinel = new \RedisSentinel($options);
|
||||
} else {
|
||||
$extra = $passAuth ? [$params['auth']] : [];
|
||||
$sentinel = new \RedisSentinel($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...$extra);
|
||||
}
|
||||
try {
|
||||
if ($address = $sentinel->getMasterAddrByName($params['redis_sentinel'])) {
|
||||
[$host, $port] = $address;
|
||||
}
|
||||
} catch (\RedisException $e) {
|
||||
}
|
||||
} while (++$hostIndex < \count($hosts) && !$address);
|
||||
if (isset($params['redis_sentinel']) && !$address) {
|
||||
throw new InvalidArgumentException(\sprintf('Failed to retrieve master information from sentinel "%s" and dsn "%s".', $params['redis_sentinel'], $dsn));
|
||||
}
|
||||
try {
|
||||
$extra = ['stream' => $params['ssl'] ?? null];
|
||||
if (isset($params['auth'])) {
|
||||
$extra['auth'] = $params['auth'];
|
||||
}
|
||||
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...\defined('Redis::SCAN_PREFIX') ? [$extra] : []);
|
||||
\set_error_handler(function ($type, $msg) use(&$error) {
|
||||
$error = $msg;
|
||||
});
|
||||
try {
|
||||
$isConnected = $redis->isConnected();
|
||||
} finally {
|
||||
\restore_error_handler();
|
||||
}
|
||||
if (!$isConnected) {
|
||||
$error = \preg_match('/^Redis::p?connect\\(\\): (.*)/', $error ?? '', $error) ? \sprintf(' (%s)', $error[1]) : '';
|
||||
throw new InvalidArgumentException(\sprintf('Redis connection "%s" failed: ', $dsn) . $error . '.');
|
||||
}
|
||||
if (null !== $auth && !$redis->auth($auth) || $params['dbindex'] && !$redis->select($params['dbindex'])) {
|
||||
$e = \preg_replace('/^ERR /', '', $redis->getLastError());
|
||||
throw new InvalidArgumentException(\sprintf('Redis connection "%s" failed: ', $dsn) . $e . '.');
|
||||
}
|
||||
if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) {
|
||||
$redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']);
|
||||
}
|
||||
} catch (\RedisException $e) {
|
||||
throw new InvalidArgumentException(\sprintf('Redis connection "%s" failed: ', $dsn) . $e->getMessage());
|
||||
}
|
||||
return \true;
|
||||
};
|
||||
if ($params['lazy']) {
|
||||
$redis = new RedisProxy($redis, $initializer);
|
||||
} else {
|
||||
$initializer($redis);
|
||||
}
|
||||
} elseif (\is_a($class, \RedisArray::class, \true)) {
|
||||
foreach ($hosts as $i => $host) {
|
||||
switch ($host['scheme']) {
|
||||
case 'tcp':
|
||||
$hosts[$i] = $host['host'] . ':' . $host['port'];
|
||||
break;
|
||||
case 'tls':
|
||||
$hosts[$i] = 'tls://' . $host['host'] . ':' . $host['port'];
|
||||
break;
|
||||
default:
|
||||
$hosts[$i] = $host['path'];
|
||||
}
|
||||
}
|
||||
$params['lazy_connect'] = $params['lazy'] ?? \true;
|
||||
$params['connect_timeout'] = $params['timeout'];
|
||||
try {
|
||||
$redis = new $class($hosts, $params);
|
||||
} catch (\RedisClusterException $e) {
|
||||
throw new InvalidArgumentException(\sprintf('Redis connection "%s" failed: ', $dsn) . $e->getMessage());
|
||||
}
|
||||
if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) {
|
||||
$redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']);
|
||||
}
|
||||
} elseif (\is_a($class, \RedisCluster::class, \true)) {
|
||||
$initializer = static function () use($class, $params, $dsn, $hosts) {
|
||||
foreach ($hosts as $i => $host) {
|
||||
switch ($host['scheme']) {
|
||||
case 'tcp':
|
||||
$hosts[$i] = $host['host'] . ':' . $host['port'];
|
||||
break;
|
||||
case 'tls':
|
||||
$hosts[$i] = 'tls://' . $host['host'] . ':' . $host['port'];
|
||||
break;
|
||||
default:
|
||||
$hosts[$i] = $host['path'];
|
||||
}
|
||||
}
|
||||
try {
|
||||
$redis = new $class(null, $hosts, $params['timeout'], $params['read_timeout'], (bool) $params['persistent'], $params['auth'] ?? '', ...\defined('Redis::SCAN_PREFIX') ? [$params['ssl'] ?? null] : []);
|
||||
} catch (\RedisClusterException $e) {
|
||||
throw new InvalidArgumentException(\sprintf('Redis connection "%s" failed: ', $dsn) . $e->getMessage());
|
||||
}
|
||||
if (0 < $params['tcp_keepalive'] && \defined('Redis::OPT_TCP_KEEPALIVE')) {
|
||||
$redis->setOption(\Redis::OPT_TCP_KEEPALIVE, $params['tcp_keepalive']);
|
||||
}
|
||||
switch ($params['failover']) {
|
||||
case 'error':
|
||||
$redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_ERROR);
|
||||
break;
|
||||
case 'distribute':
|
||||
$redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE);
|
||||
break;
|
||||
case 'slaves':
|
||||
$redis->setOption(\RedisCluster::OPT_SLAVE_FAILOVER, \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES);
|
||||
break;
|
||||
}
|
||||
return $redis;
|
||||
};
|
||||
$redis = $params['lazy'] ? new RedisClusterProxy($initializer) : $initializer();
|
||||
} elseif (\is_a($class, \WP_Ultimo\Dependencies\Predis\ClientInterface::class, \true)) {
|
||||
if ($params['redis_cluster']) {
|
||||
$params['cluster'] = 'redis';
|
||||
} elseif (isset($params['redis_sentinel'])) {
|
||||
$params['replication'] = 'sentinel';
|
||||
$params['service'] = $params['redis_sentinel'];
|
||||
}
|
||||
$params += ['parameters' => []];
|
||||
$params['parameters'] += ['persistent' => $params['persistent'], 'timeout' => $params['timeout'], 'read_write_timeout' => $params['read_timeout'], 'tcp_nodelay' => \true];
|
||||
if ($params['dbindex']) {
|
||||
$params['parameters']['database'] = $params['dbindex'];
|
||||
}
|
||||
if (null !== $auth) {
|
||||
$params['parameters']['password'] = $auth;
|
||||
}
|
||||
if (isset($params['ssl'])) {
|
||||
foreach ($hosts as $i => $host) {
|
||||
if (!isset($host['ssl'])) {
|
||||
$hosts[$i]['ssl'] = $params['ssl'];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (1 === \count($hosts) && !($params['redis_cluster'] || $params['redis_sentinel'])) {
|
||||
$hosts = $hosts[0];
|
||||
} elseif (\in_array($params['failover'], ['slaves', 'distribute'], \true) && !isset($params['replication'])) {
|
||||
$params['replication'] = \true;
|
||||
$hosts[0] += ['alias' => 'master'];
|
||||
}
|
||||
$params['exceptions'] = \false;
|
||||
$redis = new $class($hosts, \array_diff_key($params, self::$defaultConnectionOptions));
|
||||
if (isset($params['redis_sentinel'])) {
|
||||
$redis->getConnection()->setSentinelTimeout($params['timeout']);
|
||||
}
|
||||
} elseif (\class_exists($class, \false)) {
|
||||
throw new InvalidArgumentException(\sprintf('"%s" is not a subclass of "Redis", "RedisArray", "RedisCluster" nor "Predis\\ClientInterface".', $class));
|
||||
} else {
|
||||
throw new InvalidArgumentException(\sprintf('Class "%s" does not exist.', $class));
|
||||
}
|
||||
return $redis;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
if (!$ids) {
|
||||
return [];
|
||||
}
|
||||
$result = [];
|
||||
if ($this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && $this->redis->getConnection() instanceof ClusterInterface) {
|
||||
$values = $this->pipeline(function () use($ids) {
|
||||
foreach ($ids as $id) {
|
||||
(yield 'get' => [$id]);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$values = $this->redis->mget($ids);
|
||||
if (!\is_array($values) || \count($values) !== \count($ids)) {
|
||||
return [];
|
||||
}
|
||||
$values = \array_combine($ids, $values);
|
||||
}
|
||||
foreach ($values as $id => $v) {
|
||||
if ($v) {
|
||||
$result[$id] = $this->marshaller->unmarshall($v);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave(string $id)
|
||||
{
|
||||
return (bool) $this->redis->exists($id);
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear(string $namespace)
|
||||
{
|
||||
if ($this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface) {
|
||||
$prefix = $this->redis->getOptions()->prefix ? $this->redis->getOptions()->prefix->getPrefix() : '';
|
||||
$prefixLen = \strlen($prefix ?? '');
|
||||
}
|
||||
$cleared = \true;
|
||||
$hosts = $this->getHosts();
|
||||
$host = \reset($hosts);
|
||||
if ($host instanceof \WP_Ultimo\Dependencies\Predis\Client && $host->getConnection() instanceof ReplicationInterface) {
|
||||
// Predis supports info command only on the master in replication environments
|
||||
$hosts = [$host->getClientFor('master')];
|
||||
}
|
||||
foreach ($hosts as $host) {
|
||||
if (!isset($namespace[0])) {
|
||||
$cleared = $host->flushDb() && $cleared;
|
||||
continue;
|
||||
}
|
||||
$info = $host->info('Server');
|
||||
$info = !$info instanceof ErrorInterface ? $info['Server'] ?? $info : ['redis_version' => '2.0'];
|
||||
if (!$host instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface) {
|
||||
$prefix = \defined('Redis::SCAN_PREFIX') && \Redis::SCAN_PREFIX & $host->getOption(\Redis::OPT_SCAN) ? '' : $host->getOption(\Redis::OPT_PREFIX);
|
||||
$prefixLen = \strlen($host->getOption(\Redis::OPT_PREFIX) ?? '');
|
||||
}
|
||||
$pattern = $prefix . $namespace . '*';
|
||||
if (!\version_compare($info['redis_version'], '2.8', '>=')) {
|
||||
// As documented in Redis documentation (http://redis.io/commands/keys) using KEYS
|
||||
// can hang your server when it is executed against large databases (millions of items).
|
||||
// Whenever you hit this scale, you should really consider upgrading to Redis 2.8 or above.
|
||||
$unlink = \version_compare($info['redis_version'], '4.0', '>=') ? 'UNLINK' : 'DEL';
|
||||
$args = $this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface ? [0, $pattern] : [[$pattern], 0];
|
||||
$cleared = $host->eval("local keys=redis.call('KEYS',ARGV[1]) for i=1,#keys,5000 do redis.call('{$unlink}',unpack(keys,i,math.min(i+4999,#keys))) end return 1", $args[0], $args[1]) && $cleared;
|
||||
continue;
|
||||
}
|
||||
$cursor = null;
|
||||
do {
|
||||
$keys = $host instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface ? $host->scan($cursor, 'MATCH', $pattern, 'COUNT', 1000) : $host->scan($cursor, $pattern, 1000);
|
||||
if (isset($keys[1]) && \is_array($keys[1])) {
|
||||
$cursor = $keys[0];
|
||||
$keys = $keys[1];
|
||||
}
|
||||
if ($keys) {
|
||||
if ($prefixLen) {
|
||||
foreach ($keys as $i => $key) {
|
||||
$keys[$i] = \substr($key, $prefixLen);
|
||||
}
|
||||
}
|
||||
$this->doDelete($keys);
|
||||
}
|
||||
} while ($cursor = (int) $cursor);
|
||||
}
|
||||
return $cleared;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doDelete(array $ids)
|
||||
{
|
||||
if (!$ids) {
|
||||
return \true;
|
||||
}
|
||||
if ($this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && $this->redis->getConnection() instanceof ClusterInterface) {
|
||||
static $del;
|
||||
$del = $del ?? (\class_exists(UNLINK::class) ? 'unlink' : 'del');
|
||||
$this->pipeline(function () use($ids, $del) {
|
||||
foreach ($ids as $id) {
|
||||
(yield $del => [$id]);
|
||||
}
|
||||
})->rewind();
|
||||
} else {
|
||||
static $unlink = \true;
|
||||
if ($unlink) {
|
||||
try {
|
||||
$unlink = \false !== $this->redis->unlink($ids);
|
||||
} catch (\Throwable $e) {
|
||||
$unlink = \false;
|
||||
}
|
||||
}
|
||||
if (!$unlink) {
|
||||
$this->redis->del($ids);
|
||||
}
|
||||
}
|
||||
return \true;
|
||||
}
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doSave(array $values, int $lifetime)
|
||||
{
|
||||
if (!($values = $this->marshaller->marshall($values, $failed))) {
|
||||
return $failed;
|
||||
}
|
||||
$results = $this->pipeline(function () use($values, $lifetime) {
|
||||
foreach ($values as $id => $value) {
|
||||
if (0 >= $lifetime) {
|
||||
(yield 'set' => [$id, $value]);
|
||||
} else {
|
||||
(yield 'setEx' => [$id, $lifetime, $value]);
|
||||
}
|
||||
}
|
||||
});
|
||||
foreach ($results as $id => $result) {
|
||||
if (\true !== $result && (!$result instanceof Status || Status::get('OK') !== $result)) {
|
||||
$failed[] = $id;
|
||||
}
|
||||
}
|
||||
return $failed;
|
||||
}
|
||||
private function pipeline(\Closure $generator, object $redis = null) : \Generator
|
||||
{
|
||||
$ids = [];
|
||||
$redis = $redis ?? $this->redis;
|
||||
if ($redis instanceof RedisClusterProxy || $redis instanceof \RedisCluster || $redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && $redis->getConnection() instanceof RedisCluster) {
|
||||
// phpredis & predis don't support pipelining with RedisCluster
|
||||
// see https://github.com/phpredis/phpredis/blob/develop/cluster.markdown#pipelining
|
||||
// see https://github.com/nrk/predis/issues/267#issuecomment-123781423
|
||||
$results = [];
|
||||
foreach ($generator() as $command => $args) {
|
||||
$results[] = $redis->{$command}(...$args);
|
||||
$ids[] = 'eval' === $command ? $redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface ? $args[2] : $args[1][0] : $args[0];
|
||||
}
|
||||
} elseif ($redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface) {
|
||||
$results = $redis->pipeline(static function ($redis) use($generator, &$ids) {
|
||||
foreach ($generator() as $command => $args) {
|
||||
$redis->{$command}(...$args);
|
||||
$ids[] = 'eval' === $command ? $args[2] : $args[0];
|
||||
}
|
||||
});
|
||||
} elseif ($redis instanceof \RedisArray) {
|
||||
$connections = $results = $ids = [];
|
||||
foreach ($generator() as $command => $args) {
|
||||
$id = 'eval' === $command ? $args[1][0] : $args[0];
|
||||
if (!isset($connections[$h = $redis->_target($id)])) {
|
||||
$connections[$h] = [$redis->_instance($h), -1];
|
||||
$connections[$h][0]->multi(\Redis::PIPELINE);
|
||||
}
|
||||
$connections[$h][0]->{$command}(...$args);
|
||||
$results[] = [$h, ++$connections[$h][1]];
|
||||
$ids[] = $id;
|
||||
}
|
||||
foreach ($connections as $h => $c) {
|
||||
$connections[$h] = $c[0]->exec();
|
||||
}
|
||||
foreach ($results as $k => [$h, $c]) {
|
||||
$results[$k] = $connections[$h][$c];
|
||||
}
|
||||
} else {
|
||||
$redis->multi(\Redis::PIPELINE);
|
||||
foreach ($generator() as $command => $args) {
|
||||
$redis->{$command}(...$args);
|
||||
$ids[] = 'eval' === $command ? $args[1][0] : $args[0];
|
||||
}
|
||||
$results = $redis->exec();
|
||||
}
|
||||
if (!$redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface && 'eval' === $command && $redis->getLastError()) {
|
||||
$e = new \RedisException($redis->getLastError());
|
||||
$results = \array_map(function ($v) use($e) {
|
||||
return \false === $v ? $e : $v;
|
||||
}, (array) $results);
|
||||
}
|
||||
if (\is_bool($results)) {
|
||||
return;
|
||||
}
|
||||
foreach ($ids as $k => $id) {
|
||||
(yield $id => $results[$k]);
|
||||
}
|
||||
}
|
||||
private function getHosts() : array
|
||||
{
|
||||
$hosts = [$this->redis];
|
||||
if ($this->redis instanceof \WP_Ultimo\Dependencies\Predis\ClientInterface) {
|
||||
$connection = $this->redis->getConnection();
|
||||
if ($connection instanceof ClusterInterface && $connection instanceof \Traversable) {
|
||||
$hosts = [];
|
||||
foreach ($connection as $c) {
|
||||
$hosts[] = new \WP_Ultimo\Dependencies\Predis\Client($c);
|
||||
}
|
||||
}
|
||||
} elseif ($this->redis instanceof \RedisArray) {
|
||||
$hosts = [];
|
||||
foreach ($this->redis->_hosts() as $host) {
|
||||
$hosts[] = $this->redis->_instance($host);
|
||||
}
|
||||
} elseif ($this->redis instanceof RedisClusterProxy || $this->redis instanceof \RedisCluster) {
|
||||
$hosts = [];
|
||||
foreach ($this->redis->_masters() as $host) {
|
||||
$hosts[] = new RedisClusterNodeProxy($host, $this->redis);
|
||||
}
|
||||
}
|
||||
return $hosts;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user