update
This commit is contained in:
3
vendor/symfony/cache-contracts/.gitignore
vendored
Normal file
3
vendor/symfony/cache-contracts/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
57
vendor/symfony/cache-contracts/CacheInterface.php
vendored
Normal file
57
vendor/symfony/cache-contracts/CacheInterface.php
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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 Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Covers most simple to advanced caching needs.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface CacheInterface
|
||||
{
|
||||
/**
|
||||
* Fetches a value from the pool or computes it if not found.
|
||||
*
|
||||
* On cache misses, a callback is called that should return the missing value.
|
||||
* This callback is given a PSR-6 CacheItemInterface instance corresponding to the
|
||||
* requested key, that could be used e.g. for expiration control. It could also
|
||||
* be an ItemInterface instance when its additional features are needed.
|
||||
*
|
||||
* @param string $key The key of the item to retrieve from the cache
|
||||
* @param callable|CallbackInterface $callback Should return the computed value for the given key/item
|
||||
* @param float|null $beta A float that, as it grows, controls the likeliness of triggering
|
||||
* early expiration. 0 disables it, INF forces immediate expiration.
|
||||
* The default (or providing null) is implementation dependent but should
|
||||
* typically be 1.0, which should provide optimal stampede protection.
|
||||
* See https://en.wikipedia.org/wiki/Cache_stampede#Probabilistic_early_expiration
|
||||
* @param array &$metadata The metadata of the cached item {@see ItemInterface::getMetadata()}
|
||||
*
|
||||
* @return mixed The value corresponding to the provided key
|
||||
*
|
||||
* @throws InvalidArgumentException When $key is not valid or when $beta is negative
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null);
|
||||
|
||||
/**
|
||||
* Removes an item from the pool.
|
||||
*
|
||||
* @param string $key The key to delete
|
||||
*
|
||||
* @throws InvalidArgumentException When $key is not valid
|
||||
*
|
||||
* @return bool True if the item was successfully removed, false if there was any error
|
||||
*/
|
||||
public function delete(string $key): bool;
|
||||
}
|
||||
80
vendor/symfony/cache-contracts/CacheTrait.php
vendored
Normal file
80
vendor/symfony/cache-contracts/CacheTrait.php
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
<?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 Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
// Help opcache.preload discover always-needed symbols
|
||||
class_exists(InvalidArgumentException::class);
|
||||
|
||||
/**
|
||||
* An implementation of CacheInterface for PSR-6 CacheItemPoolInterface classes.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
trait CacheTrait
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
return $this->doGet($this, $key, $callback, $beta, $metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function delete(string $key): bool
|
||||
{
|
||||
return $this->deleteItem($key);
|
||||
}
|
||||
|
||||
private function doGet(CacheItemPoolInterface $pool, string $key, callable $callback, ?float $beta, array &$metadata = null, LoggerInterface $logger = null)
|
||||
{
|
||||
if (0 > $beta = $beta ?? 1.0) {
|
||||
throw new class(sprintf('Argument "$beta" provided to "%s::get()" must be a positive number, %f given.', static::class, $beta)) extends \InvalidArgumentException implements InvalidArgumentException { };
|
||||
}
|
||||
|
||||
$item = $pool->getItem($key);
|
||||
$recompute = !$item->isHit() || \INF === $beta;
|
||||
$metadata = $item instanceof ItemInterface ? $item->getMetadata() : [];
|
||||
|
||||
if (!$recompute && $metadata) {
|
||||
$expiry = $metadata[ItemInterface::METADATA_EXPIRY] ?? false;
|
||||
$ctime = $metadata[ItemInterface::METADATA_CTIME] ?? false;
|
||||
|
||||
if ($recompute = $ctime && $expiry && $expiry <= ($now = microtime(true)) - $ctime / 1000 * $beta * log(random_int(1, \PHP_INT_MAX) / \PHP_INT_MAX)) {
|
||||
// force applying defaultLifetime to expiry
|
||||
$item->expiresAt(null);
|
||||
$logger && $logger->info('Item "{key}" elected for early recomputation {delta}s before its expiration', [
|
||||
'key' => $key,
|
||||
'delta' => sprintf('%.1f', $expiry - $now),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($recompute) {
|
||||
$save = true;
|
||||
$item->set($callback($item, $save));
|
||||
if ($save) {
|
||||
$pool->save($item);
|
||||
}
|
||||
}
|
||||
|
||||
return $item->get();
|
||||
}
|
||||
}
|
||||
30
vendor/symfony/cache-contracts/CallbackInterface.php
vendored
Normal file
30
vendor/symfony/cache-contracts/CallbackInterface.php
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<?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 Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
|
||||
/**
|
||||
* Computes and returns the cached value of an item.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface CallbackInterface
|
||||
{
|
||||
/**
|
||||
* @param CacheItemInterface|ItemInterface $item The item to compute the value for
|
||||
* @param bool &$save Should be set to false when the value should not be saved in the pool
|
||||
*
|
||||
* @return mixed The computed value for the passed item
|
||||
*/
|
||||
public function __invoke(CacheItemInterface $item, bool &$save);
|
||||
}
|
||||
65
vendor/symfony/cache-contracts/ItemInterface.php
vendored
Normal file
65
vendor/symfony/cache-contracts/ItemInterface.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?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 Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\CacheException;
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Augments PSR-6's CacheItemInterface with support for tags and metadata.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface ItemInterface extends CacheItemInterface
|
||||
{
|
||||
/**
|
||||
* References the Unix timestamp stating when the item will expire.
|
||||
*/
|
||||
public const METADATA_EXPIRY = 'expiry';
|
||||
|
||||
/**
|
||||
* References the time the item took to be created, in milliseconds.
|
||||
*/
|
||||
public const METADATA_CTIME = 'ctime';
|
||||
|
||||
/**
|
||||
* References the list of tags that were assigned to the item, as string[].
|
||||
*/
|
||||
public const METADATA_TAGS = 'tags';
|
||||
|
||||
/**
|
||||
* Reserved characters that cannot be used in a key or tag.
|
||||
*/
|
||||
public const RESERVED_CHARACTERS = '{}()/\@:';
|
||||
|
||||
/**
|
||||
* Adds a tag to a cache item.
|
||||
*
|
||||
* Tags are strings that follow the same validation rules as keys.
|
||||
*
|
||||
* @param string|string[] $tags A tag or array of tags
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws InvalidArgumentException When $tag is not valid
|
||||
* @throws CacheException When the item comes from a pool that is not tag-aware
|
||||
*/
|
||||
public function tag($tags): self;
|
||||
|
||||
/**
|
||||
* Returns a list of metadata info that were saved alongside with the cached value.
|
||||
*
|
||||
* See ItemInterface::METADATA_* consts for keys potentially found in the returned array.
|
||||
*/
|
||||
public function getMetadata(): array;
|
||||
}
|
||||
19
vendor/symfony/cache-contracts/LICENSE
vendored
Normal file
19
vendor/symfony/cache-contracts/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2018-2022 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
9
vendor/symfony/cache-contracts/README.md
vendored
Normal file
9
vendor/symfony/cache-contracts/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Symfony Cache Contracts
|
||||
=======================
|
||||
|
||||
A set of abstractions extracted out of the Symfony components.
|
||||
|
||||
Can be used to build on semantics that the Symfony components proved useful - and
|
||||
that already have battle tested implementations.
|
||||
|
||||
See https://github.com/symfony/contracts/blob/main/README.md for more information.
|
||||
38
vendor/symfony/cache-contracts/TagAwareCacheInterface.php
vendored
Normal file
38
vendor/symfony/cache-contracts/TagAwareCacheInterface.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 Symfony\Contracts\Cache;
|
||||
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Allows invalidating cached items using tags.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
interface TagAwareCacheInterface extends CacheInterface
|
||||
{
|
||||
/**
|
||||
* Invalidates cached items using tags.
|
||||
*
|
||||
* When implemented on a PSR-6 pool, invalidation should not apply
|
||||
* to deferred items. Instead, they should be committed as usual.
|
||||
* This allows replacing old tagged values by new ones without
|
||||
* race conditions.
|
||||
*
|
||||
* @param string[] $tags An array of tags to invalidate
|
||||
*
|
||||
* @return bool True on success
|
||||
*
|
||||
* @throws InvalidArgumentException When $tags is not valid
|
||||
*/
|
||||
public function invalidateTags(array $tags);
|
||||
}
|
||||
38
vendor/symfony/cache-contracts/composer.json
vendored
Normal file
38
vendor/symfony/cache-contracts/composer.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "symfony/cache-contracts",
|
||||
"type": "library",
|
||||
"description": "Generic abstractions related to caching",
|
||||
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"psr/cache": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"symfony/cache-implementation": ""
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Contracts\\Cache\\": "" }
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.1-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
}
|
||||
}
|
||||
210
vendor/symfony/cache/Adapter/AbstractAdapter.php
vendored
Normal file
210
vendor/symfony/cache/Adapter/AbstractAdapter.php
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
|
||||
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use 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;
|
||||
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));
|
||||
}
|
||||
$this->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] && "\0" === $k[5] && "\x5F" === $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
|
||||
);
|
||||
$getId = \Closure::fromCallable([$this, 'getId']);
|
||||
$this->mergeByLifetime = \Closure::bind(
|
||||
static function ($deferred, $namespace, &$expiredIds) use ($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])."\x5F" => $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.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param int $defaultLifetime
|
||||
* @param string $version
|
||||
* @param string $directory
|
||||
*
|
||||
* @return AdapterInterface
|
||||
*/
|
||||
public static function createSystemCache($namespace, $defaultLifetime, $version, $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($dsn, array $options = [])
|
||||
{
|
||||
if (!\is_string($dsn)) {
|
||||
throw new InvalidArgumentException(sprintf('The "%s()" method expect argument #1 to be string, "%s" given.', __METHOD__, \gettype($dsn)));
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Unsupported DSN: "%s".', $dsn));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function commit()
|
||||
{
|
||||
$ok = true;
|
||||
$byLifetime = $this->mergeByLifetime;
|
||||
$byLifetime = $byLifetime($this->deferred, $this->namespace, $expiredIds);
|
||||
$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 = \is_object($v) ? \get_class($v) : \gettype($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]);
|
||||
}
|
||||
} 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 = \is_object($v) ? \get_class($v) : \gettype($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]);
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
}
|
||||
334
vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php
vendored
Normal file
334
vendor/symfony/cache/Adapter/AbstractTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\AbstractAdapterTrait;
|
||||
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use 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 = "\0tags\0";
|
||||
|
||||
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
|
||||
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));
|
||||
}
|
||||
$this->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
|
||||
);
|
||||
$getId = \Closure::fromCallable([$this, 'getId']);
|
||||
$tagPrefix = self::TAGS_PREFIX;
|
||||
$this->mergeByLifetime = \Closure::bind(
|
||||
static function ($deferred, &$expiredIds) use ($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
|
||||
*/
|
||||
abstract protected 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 True if the items were successfully removed, false otherwise
|
||||
*/
|
||||
abstract protected 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
|
||||
*/
|
||||
abstract protected 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
|
||||
*
|
||||
* @return bool True on success
|
||||
*/
|
||||
abstract protected 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 = $this->mergeByLifetime;
|
||||
$byLifetime = $byLifetime($this->deferred, $expiredIds);
|
||||
$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 = \is_object($v) ? \get_class($v) : \gettype($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]);
|
||||
}
|
||||
} 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 = \is_object($v) ? \get_class($v) : \gettype($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]);
|
||||
}
|
||||
}
|
||||
|
||||
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]);
|
||||
$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;
|
||||
}
|
||||
}
|
||||
49
vendor/symfony/cache/Adapter/AdapterInterface.php
vendored
Normal file
49
vendor/symfony/cache/Adapter/AdapterInterface.php
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use 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|CacheItem[]
|
||||
*/
|
||||
public function getItems(array $keys = []);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/);
|
||||
}
|
||||
27
vendor/symfony/cache/Adapter/ApcuAdapter.php
vendored
Normal file
27
vendor/symfony/cache/Adapter/ApcuAdapter.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Traits\ApcuTrait;
|
||||
|
||||
class ApcuAdapter extends AbstractAdapter
|
||||
{
|
||||
use ApcuTrait;
|
||||
|
||||
/**
|
||||
* @throws CacheException if APCu is not enabled
|
||||
*/
|
||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null)
|
||||
{
|
||||
$this->init($namespace, $defaultLifetime, $version);
|
||||
}
|
||||
}
|
||||
176
vendor/symfony/cache/Adapter/ArrayAdapter.php
vendored
Normal file
176
vendor/symfony/cache/Adapter/ArrayAdapter.php
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ArrayTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use ArrayTrait;
|
||||
|
||||
private $createCacheItem;
|
||||
private $defaultLifetime;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
$this->storeSerialized = $storeSerialized;
|
||||
$this->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;
|
||||
$this->save($item->set($callback($item, $save)));
|
||||
}
|
||||
|
||||
return $item->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
if (!$isHit = $this->hasItem($key)) {
|
||||
$this->values[$key] = $value = null;
|
||||
} else {
|
||||
$value = $this->storeSerialized ? $this->unfreeze($key, $isHit) : $this->values[$key];
|
||||
}
|
||||
$f = $this->createCacheItem;
|
||||
|
||||
return $f($key, $value, $isHit);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key) || !isset($this->expiries[$key])) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->generateItems($keys, microtime(true), $this->createCacheItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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["\0*\0key"];
|
||||
$value = $item["\0*\0value"];
|
||||
$expiry = $item["\0*\0expiry"];
|
||||
|
||||
if (null !== $expiry) {
|
||||
if (!$expiry) {
|
||||
$expiry = \PHP_INT_MAX;
|
||||
} elseif ($expiry <= microtime(true)) {
|
||||
$this->deleteItem($key);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
|
||||
return false;
|
||||
}
|
||||
if (null === $expiry && 0 < $this->defaultLifetime) {
|
||||
$expiry = microtime(true) + $this->defaultLifetime;
|
||||
}
|
||||
|
||||
$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}
|
||||
*/
|
||||
public function delete(string $key): bool
|
||||
{
|
||||
return $this->deleteItem($key);
|
||||
}
|
||||
}
|
||||
333
vendor/symfony/cache/Adapter/ChainAdapter.php
vendored
Normal file
333
vendor/symfony/cache/Adapter/ChainAdapter.php
vendored
Normal file
@@ -0,0 +1,333 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use 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 $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_class($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->syncItem = \Closure::bind(
|
||||
static function ($sourceItem, $item, $sourceMetadata = null) use ($defaultLifetime) {
|
||||
$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)
|
||||
{
|
||||
$lastItem = null;
|
||||
$i = 0;
|
||||
$wrap = function (CacheItem $item = null) use ($key, $callback, $beta, &$wrap, &$i, &$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) {
|
||||
($this->syncItem)($lastItem = $lastItem ?? $item, $item, $metadata);
|
||||
}
|
||||
|
||||
return $value;
|
||||
};
|
||||
|
||||
return $wrap();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$syncItem = $this->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]));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$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 = $this->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]));
|
||||
}
|
||||
|
||||
yield $k => $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
foreach ($this->adapters as $adapter) {
|
||||
if ($adapter->hasItem($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/)
|
||||
{
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
$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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
vendor/symfony/cache/Adapter/DoctrineAdapter.php
vendored
Normal file
27
vendor/symfony/cache/Adapter/DoctrineAdapter.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Symfony\Component\Cache\Traits\DoctrineTrait;
|
||||
|
||||
class DoctrineAdapter extends AbstractAdapter
|
||||
{
|
||||
use DoctrineTrait;
|
||||
|
||||
public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->provider = $provider;
|
||||
$provider->setNamespace($namespace);
|
||||
}
|
||||
}
|
||||
29
vendor/symfony/cache/Adapter/FilesystemAdapter.php
vendored
Normal file
29
vendor/symfony/cache/Adapter/FilesystemAdapter.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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use 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);
|
||||
}
|
||||
}
|
||||
239
vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php
vendored
Normal file
239
vendor/symfony/cache/Adapter/FilesystemTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,239 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use 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($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 (!file_exists($dir.$chars[$i])) {
|
||||
continue;
|
||||
}
|
||||
for ($j = 0; $j < 38; ++$j) {
|
||||
if (!file_exists($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 (!file_exists($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] && "\0" === $meta[5] && "\x5F" === $meta[9]) {
|
||||
$meta[9] = "\0";
|
||||
$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 (!file_exists($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;
|
||||
}
|
||||
}
|
||||
37
vendor/symfony/cache/Adapter/MemcachedAdapter.php
vendored
Normal file
37
vendor/symfony/cache/Adapter/MemcachedAdapter.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Traits\MemcachedTrait;
|
||||
|
||||
class MemcachedAdapter extends AbstractAdapter
|
||||
{
|
||||
use MemcachedTrait;
|
||||
|
||||
protected $maxIdLength = 250;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->init($client, $namespace, $defaultLifetime, $marshaller);
|
||||
}
|
||||
}
|
||||
156
vendor/symfony/cache/Adapter/NullAdapter.php
vendored
Normal file
156
vendor/symfony/cache/Adapter/NullAdapter.php
vendored
Normal file
@@ -0,0 +1,156 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
*/
|
||||
class NullAdapter implements AdapterInterface, CacheInterface
|
||||
{
|
||||
private $createCacheItem;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->createCacheItem = \Closure::bind(
|
||||
function ($key) {
|
||||
$item = new CacheItem();
|
||||
$item->key = $key;
|
||||
$item->isHit = false;
|
||||
|
||||
return $item;
|
||||
},
|
||||
$this,
|
||||
CacheItem::class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get(string $key, callable $callback, float $beta = null, array &$metadata = null)
|
||||
{
|
||||
$save = true;
|
||||
|
||||
return $callback(($this->createCacheItem)($key), $save);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$f = $this->createCacheItem;
|
||||
|
||||
return $f($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItems(array $keys = [])
|
||||
{
|
||||
return $this->generateItems($keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$f = $this->createCacheItem;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $f($key);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
vendor/symfony/cache/Adapter/PdoAdapter.php
vendored
Normal file
54
vendor/symfony/cache/Adapter/PdoAdapter.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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\Traits\PdoTrait;
|
||||
|
||||
class PdoAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
use PdoTrait;
|
||||
|
||||
protected $maxIdLength = 255;
|
||||
|
||||
/**
|
||||
* You can either pass an existing database connection as PDO instance or
|
||||
* a Doctrine DBAL Connection or a DSN string that will be used to
|
||||
* lazy-connect to the database when the cache is actually used.
|
||||
*
|
||||
* When a Doctrine DBAL Connection is passed, 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]
|
||||
* * 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|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$this->init($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
|
||||
}
|
||||
}
|
||||
339
vendor/symfony/cache/Adapter/PhpArrayAdapter.php
vendored
Normal file
339
vendor/symfony/cache/Adapter/PhpArrayAdapter.php
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
use 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 PhpArrayTrait;
|
||||
|
||||
private $createCacheItem;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
$this->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($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.', \is_object($key) ? \get_class($key) : \gettype($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;
|
||||
}
|
||||
}
|
||||
|
||||
$f = $this->createCacheItem;
|
||||
|
||||
return $f($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.', \is_object($key) ? \get_class($key) : \gettype($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.', \is_object($key) ? \get_class($key) : \gettype($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.', \is_object($key) ? \get_class($key) : \gettype($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.', \is_object($key) ? \get_class($key) : \gettype($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();
|
||||
}
|
||||
|
||||
private function generateItems(array $keys): \Generator
|
||||
{
|
||||
$f = $this->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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \ReflectionException When $class is not found and is required
|
||||
*
|
||||
* @internal to be removed in Symfony 5.0
|
||||
*/
|
||||
public static function throwOnRequiredClass($class)
|
||||
{
|
||||
$e = new \ReflectionException("Class $class does not exist");
|
||||
$trace = debug_backtrace();
|
||||
$autoloadFrame = [
|
||||
'function' => 'spl_autoload_call',
|
||||
'args' => [$class],
|
||||
];
|
||||
|
||||
if (\PHP_VERSION_ID >= 80000 && isset($trace[1])) {
|
||||
$callerFrame = $trace[1];
|
||||
} elseif (false !== $i = array_search($autoloadFrame, $trace, true)) {
|
||||
$callerFrame = $trace[++$i];
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (isset($callerFrame['function']) && !isset($callerFrame['class'])) {
|
||||
switch ($callerFrame['function']) {
|
||||
case 'get_class_methods':
|
||||
case 'get_class_vars':
|
||||
case 'get_parent_class':
|
||||
case 'is_a':
|
||||
case 'is_subclass_of':
|
||||
case 'class_exists':
|
||||
case 'class_implements':
|
||||
case 'class_parents':
|
||||
case 'trait_exists':
|
||||
case 'defined':
|
||||
case 'interface_exists':
|
||||
case 'method_exists':
|
||||
case 'property_exists':
|
||||
case 'is_callable':
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
38
vendor/symfony/cache/Adapter/PhpFilesAdapter.php
vendored
Normal file
38
vendor/symfony/cache/Adapter/PhpFilesAdapter.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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\Traits\PhpFilesTrait;
|
||||
|
||||
class PhpFilesAdapter extends AbstractAdapter implements PruneableInterface
|
||||
{
|
||||
use PhpFilesTrait;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
}
|
||||
}
|
||||
270
vendor/symfony/cache/Adapter/ProxyAdapter.php
vendored
Normal file
270
vendor/symfony/cache/Adapter/ProxyAdapter.php
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
use 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 $createCacheItem;
|
||||
private $setInnerItem;
|
||||
private $poolHash;
|
||||
private $defaultLifetime;
|
||||
|
||||
public function __construct(CacheItemPoolInterface $pool, string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
$this->poolHash = $poolHash = spl_object_hash($pool);
|
||||
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace);
|
||||
$this->namespaceLen = \strlen($namespace);
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
$this->createCacheItem = \Closure::bind(
|
||||
static function ($key, $innerItem) use ($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] && "\0" === $k[5] && "\x5F" === $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
|
||||
);
|
||||
$this->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["\0*\0newMetadata"])[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["\0*\0value"] = ["\x9D".pack('VN', (int) (0.1 + $metadata[self::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET), $metadata[self::METADATA_CTIME])."\x5F" => $item["\0*\0value"]];
|
||||
}
|
||||
$innerItem->set($item["\0*\0value"]);
|
||||
$innerItem->expiresAt(null !== $item["\0*\0expiry"] ? \DateTime::createFromFormat('U.u', sprintf('%.6F', $item["\0*\0expiry"])) : 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 = ($this->createCacheItem)($key, $innerItem);
|
||||
$item->set($value = $callback($item, $save));
|
||||
($this->setInnerItem)($innerItem, (array) $item);
|
||||
|
||||
return $value;
|
||||
}, $beta, $metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$f = $this->createCacheItem;
|
||||
$item = $this->pool->getItem($this->getId($key));
|
||||
|
||||
return $f($key, $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/)
|
||||
{
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
|
||||
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["\0*\0expiry"] && 0 < $this->defaultLifetime) {
|
||||
$item["\0*\0expiry"] = microtime(true) + $this->defaultLifetime;
|
||||
}
|
||||
|
||||
if ($item["\0*\0poolHash"] === $this->poolHash && $item["\0*\0innerItem"]) {
|
||||
$innerItem = $item["\0*\0innerItem"];
|
||||
} 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
|
||||
$f = $this->createCacheItem;
|
||||
$innerItem = $f($this->namespace.$item["\0*\0key"], null);
|
||||
} else {
|
||||
$innerItem = $this->pool->getItem($this->namespace.$item["\0*\0key"]);
|
||||
}
|
||||
|
||||
($this->setInnerItem)($innerItem, $item);
|
||||
|
||||
return $this->pool->$method($innerItem);
|
||||
}
|
||||
|
||||
private function generateItems(iterable $items)
|
||||
{
|
||||
$f = $this->createCacheItem;
|
||||
|
||||
foreach ($items as $key => $item) {
|
||||
if ($this->namespaceLen) {
|
||||
$key = substr($key, $this->namespaceLen);
|
||||
}
|
||||
|
||||
yield $key => $f($key, $item);
|
||||
}
|
||||
}
|
||||
|
||||
private function getId($key): string
|
||||
{
|
||||
CacheItem::validateKey($key);
|
||||
|
||||
return $this->namespace.$key;
|
||||
}
|
||||
}
|
||||
86
vendor/symfony/cache/Adapter/Psr16Adapter.php
vendored
Normal file
86
vendor/symfony/cache/Adapter/Psr16Adapter.php
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use 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($id)
|
||||
{
|
||||
return $this->pool->has($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($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);
|
||||
}
|
||||
}
|
||||
32
vendor/symfony/cache/Adapter/RedisAdapter.php
vendored
Normal file
32
vendor/symfony/cache/Adapter/RedisAdapter.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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||
use 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);
|
||||
}
|
||||
}
|
||||
321
vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php
vendored
Normal file
321
vendor/symfony/cache/Adapter/RedisTagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Predis\Connection\Aggregate\ClusterInterface;
|
||||
use Predis\Connection\Aggregate\PredisCluster;
|
||||
use Predis\Connection\Aggregate\ReplicationInterface;
|
||||
use Predis\Response\ErrorInterface;
|
||||
use Predis\Response\Status;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Exception\LogicException;
|
||||
use Symfony\Component\Cache\Marshaller\DeflateMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Marshaller\TagAwareMarshaller;
|
||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||
use 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 \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_class($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])
|
||||
redis.call('DEL', KEYS[1])
|
||||
|
||||
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 \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
|
||||
// gargage 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 \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 \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 \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 \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 = '';
|
||||
}
|
||||
}
|
||||
21
vendor/symfony/cache/Adapter/SimpleCacheAdapter.php
vendored
Normal file
21
vendor/symfony/cache/Adapter/SimpleCacheAdapter.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is @deprecated since Symfony 4.3, use "Psr16Adapter" instead.', SimpleCacheAdapter::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use Psr16Adapter instead.
|
||||
*/
|
||||
class SimpleCacheAdapter extends Psr16Adapter
|
||||
{
|
||||
}
|
||||
445
vendor/symfony/cache/Adapter/TagAwareAdapter.php
vendored
Normal file
445
vendor/symfony/cache/Adapter/TagAwareAdapter.php
vendored
Normal file
@@ -0,0 +1,445 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Psr\Cache\InvalidArgumentException;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ContractsTrait;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
use 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 = "\0tags\0";
|
||||
|
||||
private $deferred = [];
|
||||
private $createCacheItem;
|
||||
private $setCacheItemTags;
|
||||
private $getTagsByKey;
|
||||
private $invalidateTags;
|
||||
private $tags;
|
||||
private $knownTagVersions = [];
|
||||
private $knownTagVersionsTtl;
|
||||
|
||||
public function __construct(AdapterInterface $itemsPool, AdapterInterface $tagsPool = null, float $knownTagVersionsTtl = 0.15)
|
||||
{
|
||||
$this->pool = $itemsPool;
|
||||
$this->tags = $tagsPool ?: $itemsPool;
|
||||
$this->knownTagVersionsTtl = $knownTagVersionsTtl;
|
||||
$this->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
|
||||
);
|
||||
$this->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
|
||||
);
|
||||
$this->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
|
||||
);
|
||||
$this->invalidateTags = \Closure::bind(
|
||||
static function (AdapterInterface $tagsAdapter, array $tags) {
|
||||
foreach ($tags as $v) {
|
||||
$v->expiry = 0;
|
||||
$tagsAdapter->saveDeferred($v);
|
||||
}
|
||||
|
||||
return $tagsAdapter->commit();
|
||||
},
|
||||
null,
|
||||
CacheItem::class
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function invalidateTags(array $tags)
|
||||
{
|
||||
$ok = true;
|
||||
$tagsByKey = [];
|
||||
$invalidatedTags = [];
|
||||
foreach ($tags as $tag) {
|
||||
CacheItem::validateKey($tag);
|
||||
$invalidatedTags[$tag] = 0;
|
||||
}
|
||||
|
||||
if ($this->deferred) {
|
||||
$items = $this->deferred;
|
||||
foreach ($items as $key => $item) {
|
||||
if (!$this->pool->saveDeferred($item)) {
|
||||
unset($this->deferred[$key]);
|
||||
$ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
$f = $this->getTagsByKey;
|
||||
$tagsByKey = $f($items);
|
||||
$this->deferred = [];
|
||||
}
|
||||
|
||||
$tagVersions = $this->getTagVersions($tagsByKey, $invalidatedTags);
|
||||
$f = $this->createCacheItem;
|
||||
|
||||
foreach ($tagsByKey as $key => $tags) {
|
||||
$this->pool->saveDeferred($f(static::TAGS_PREFIX.$key, array_intersect_key($tagVersions, $tags), $items[$key]));
|
||||
}
|
||||
$ok = $this->pool->commit() && $ok;
|
||||
|
||||
if ($invalidatedTags) {
|
||||
$f = $this->invalidateTags;
|
||||
$ok = $f($this->tags, $invalidatedTags) && $ok;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 || \is_int($itemTags[$tag]) && \is_int($version) && 1 === $itemTags[$tag] - $version) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/)
|
||||
{
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
|
||||
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()
|
||||
{
|
||||
return $this->invalidateTags([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$bufferedItems = $itemTags = [];
|
||||
$f = $this->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 || \is_int($version) && \is_int($tagVersions[$tag]) && 1 === $version - $tagVersions[$tag]) {
|
||||
continue;
|
||||
}
|
||||
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, array &$invalidatedTags = [])
|
||||
{
|
||||
$tagVersions = $invalidatedTags;
|
||||
|
||||
foreach ($tagsByKey as $tags) {
|
||||
$tagVersions += $tags;
|
||||
}
|
||||
|
||||
if (!$tagVersions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!$fetchTagVersions = 1 !== \func_num_args()) {
|
||||
foreach ($tagsByKey as $tags) {
|
||||
foreach ($tags as $tag => $version) {
|
||||
if ($tagVersions[$tag] > $version) {
|
||||
$tagVersions[$tag] = $version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$now = microtime(true);
|
||||
$tags = [];
|
||||
foreach ($tagVersions as $tag => $version) {
|
||||
$tags[$tag.static::TAGS_PREFIX] = $tag;
|
||||
if ($fetchTagVersions || !isset($this->knownTagVersions[$tag]) || !\is_int($version)) {
|
||||
$fetchTagVersions = true;
|
||||
continue;
|
||||
}
|
||||
$version -= $this->knownTagVersions[$tag][1];
|
||||
if ((0 !== $version && 1 !== $version) || $now - $this->knownTagVersions[$tag][0] >= $this->knownTagVersionsTtl) {
|
||||
// reuse previously fetched tag versions up to the ttl, unless we are storing items or a potential miss arises
|
||||
$fetchTagVersions = true;
|
||||
} else {
|
||||
$this->knownTagVersions[$tag][1] += $version;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$fetchTagVersions) {
|
||||
return $tagVersions;
|
||||
}
|
||||
|
||||
foreach ($this->tags->getItems(array_keys($tags)) as $tag => $version) {
|
||||
$tagVersions[$tag = $tags[$tag]] = $version->get() ?: 0;
|
||||
if (isset($invalidatedTags[$tag])) {
|
||||
$invalidatedTags[$tag] = $version->set(++$tagVersions[$tag]);
|
||||
}
|
||||
if (!\is_int($tagVersions[$tag])) {
|
||||
unset($this->knownTagVersions[$tag]);
|
||||
continue;
|
||||
}
|
||||
$this->knownTagVersions[$tag] = [$now, $tagVersions[$tag]];
|
||||
}
|
||||
|
||||
return $tagVersions;
|
||||
}
|
||||
}
|
||||
33
vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php
vendored
Normal file
33
vendor/symfony/cache/Adapter/TagAwareAdapterInterface.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use 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 True on success
|
||||
*
|
||||
* @throws InvalidArgumentException When $tags is not valid
|
||||
*/
|
||||
public function invalidateTags(array $tags);
|
||||
}
|
||||
298
vendor/symfony/cache/Adapter/TraceableAdapter.php
vendored
Normal file
298
vendor/symfony/cache/Adapter/TraceableAdapter.php
vendored
Normal file
@@ -0,0 +1,298 @@
|
||||
<?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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use 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_class($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] = \is_object($value) ? \get_class($value) : \gettype($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}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/)
|
||||
{
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
$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($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;
|
||||
}
|
||||
38
vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.php
vendored
Normal file
38
vendor/symfony/cache/Adapter/TraceableTagAwareAdapter.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 Symfony\Component\Cache\Adapter;
|
||||
|
||||
use 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
vendor/symfony/cache/CHANGELOG.md
vendored
Normal file
73
vendor/symfony/cache/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* added support for connecting to Redis Sentinel clusters
|
||||
* added argument `$prefix` to `AdapterInterface::clear()`
|
||||
* improved `RedisTagAwareAdapter` to support Redis server >= 2.8 and up to 4B items per tag
|
||||
* added `TagAwareMarshaller` for optimized data storage when using `AbstractTagAwareAdapter`
|
||||
* added `DeflateMarshaller` to compress serialized values
|
||||
* removed support for phpredis 4 `compression`
|
||||
* [BC BREAK] `RedisTagAwareAdapter` is not compatible with `RedisCluster` from `Predis` anymore, use `phpredis` instead
|
||||
* Marked the `CacheDataCollector` class as `@final`.
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* removed `psr/simple-cache` dependency, run `composer require psr/simple-cache` if you need it
|
||||
* deprecated all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead
|
||||
* deprecated `SimpleCacheAdapter`, use `Psr16Adapter` instead
|
||||
|
||||
4.2.0
|
||||
-----
|
||||
|
||||
* added support for connecting to Redis clusters via DSN
|
||||
* added support for configuring multiple Memcached servers via DSN
|
||||
* added `MarshallerInterface` and `DefaultMarshaller` to allow changing the serializer and provide one that automatically uses igbinary when available
|
||||
* implemented `CacheInterface`, which provides stampede protection via probabilistic early expiration and should become the preferred way to use a cache
|
||||
* added sub-second expiry accuracy for backends that support it
|
||||
* added support for phpredis 4 `compression` and `tcp_keepalive` options
|
||||
* added automatic table creation when using Doctrine DBAL with PDO-based backends
|
||||
* throw `LogicException` when `CacheItem::tag()` is called on an item coming from a non tag-aware pool
|
||||
* deprecated `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead
|
||||
* deprecated the `AbstractAdapter::unserialize()` and `AbstractCache::unserialize()` methods
|
||||
* added `CacheCollectorPass` (originally in `FrameworkBundle`)
|
||||
* added `CachePoolClearerPass` (originally in `FrameworkBundle`)
|
||||
* added `CachePoolPass` (originally in `FrameworkBundle`)
|
||||
* added `CachePoolPrunerPass` (originally in `FrameworkBundle`)
|
||||
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* added using options from Memcached DSN
|
||||
* added PruneableInterface so PSR-6 or PSR-16 cache implementations can declare support for manual stale cache pruning
|
||||
* added prune logic to FilesystemTrait, PhpFilesTrait, PdoTrait, TagAwareAdapter and ChainTrait
|
||||
* now FilesystemAdapter, PhpFilesAdapter, FilesystemCache, PhpFilesCache, PdoAdapter, PdoCache, ChainAdapter, and
|
||||
ChainCache implement PruneableInterface and support manual stale cache pruning
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* added CacheItem::getPreviousTags() to get bound tags coming from the pool storage if any
|
||||
* added PSR-16 "Simple Cache" implementations for all existing PSR-6 adapters
|
||||
* added Psr6Cache and SimpleCacheAdapter for bidirectional interoperability between PSR-6 and PSR-16
|
||||
* added MemcachedAdapter (PSR-6) and MemcachedCache (PSR-16)
|
||||
* added TraceableAdapter (PSR-6) and TraceableCache (PSR-16)
|
||||
|
||||
3.2.0
|
||||
-----
|
||||
|
||||
* added TagAwareAdapter for tags-based invalidation
|
||||
* added PdoAdapter with PDO and Doctrine DBAL support
|
||||
* added PhpArrayAdapter and PhpFilesAdapter for OPcache-backed shared memory storage (PHP 7+ only)
|
||||
* added NullAdapter
|
||||
|
||||
3.1.0
|
||||
-----
|
||||
|
||||
* added the component with strict PSR-6 implementations
|
||||
* added ApcuAdapter, ArrayAdapter, FilesystemAdapter and RedisAdapter
|
||||
* added AbstractAdapter, ChainAdapter and ProxyAdapter
|
||||
* added DoctrineAdapter and DoctrineProvider for bidirectional interoperability with Doctrine Cache
|
||||
204
vendor/symfony/cache/CacheItem.php
vendored
Normal file
204
vendor/symfony/cache/CacheItem.php
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
<?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 Symfony\Component\Cache;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Exception\LogicException;
|
||||
use 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.', \is_object($expiration) ? \get_class($expiration) : \gettype($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.', \is_object($time) ? \get_class($time) : \gettype($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of tags bound to the value coming from the pool storage if any.
|
||||
*
|
||||
* @deprecated since Symfony 4.2, use the "getMetadata()" method instead.
|
||||
*/
|
||||
public function getPreviousTags(): array
|
||||
{
|
||||
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.2, use the "getMetadata()" method instead.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
return $this->metadata[self::METADATA_TAGS] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.', \is_object($key) ? \get_class($key) : \gettype($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);
|
||||
}
|
||||
}
|
||||
}
|
||||
194
vendor/symfony/cache/DataCollector/CacheDataCollector.php
vendored
Normal file
194
vendor/symfony/cache/DataCollector/CacheDataCollector.php
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
<?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 Symfony\Component\Cache\DataCollector;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TraceableAdapterEvent;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||
|
||||
/**
|
||||
* @author Aaron Scherer <aequasi@gmail.com>
|
||||
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
|
||||
*
|
||||
* @final since Symfony 4.4
|
||||
*/
|
||||
class CacheDataCollector extends DataCollector implements LateDataCollectorInterface
|
||||
{
|
||||
/**
|
||||
* @var TraceableAdapter[]
|
||||
*/
|
||||
private $instances = [];
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function addInstance($name, TraceableAdapter $instance)
|
||||
{
|
||||
$this->instances[$name] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param \Throwable|null $exception
|
||||
*/
|
||||
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()
|
||||
{
|
||||
return 'cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns amount of logged Cache reads: "get" calls.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStatistics()
|
||||
{
|
||||
return $this->data['instances']['statistics'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Method returns the statistic totals.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTotals()
|
||||
{
|
||||
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 - $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'];
|
||||
if (false === $call->result) {
|
||||
++$statistics[$name]['misses'];
|
||||
} else {
|
||||
++$statistics[$name]['hits'];
|
||||
}
|
||||
} 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
vendor/symfony/cache/DependencyInjection/CacheCollectorPass.php
vendored
Normal file
81
vendor/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 Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
|
||||
use Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||
use Symfony\Component\Cache\Adapter\TraceableTagAwareAdapter;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use 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')
|
||||
{
|
||||
$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)]);
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
48
vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.php
vendored
Normal file
48
vendor/symfony/cache/DependencyInjection/CachePoolClearerPass.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 Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class CachePoolClearerPass implements CompilerPassInterface
|
||||
{
|
||||
private $cachePoolClearerTag;
|
||||
|
||||
public function __construct(string $cachePoolClearerTag = 'cache.pool.clearer')
|
||||
{
|
||||
$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);
|
||||
}
|
||||
}
|
||||
}
|
||||
228
vendor/symfony/cache/DependencyInjection/CachePoolPass.php
vendored
Normal file
228
vendor/symfony/cache/DependencyInjection/CachePoolPass.php
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
<?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 Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use Symfony\Component\Cache\Adapter\NullAdapter;
|
||||
use Symfony\Component\DependencyInjection\ChildDefinition;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use 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;
|
||||
|
||||
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')
|
||||
{
|
||||
$this->cachePoolTag = $cachePoolTag;
|
||||
$this->kernelResetTag = $kernelResetTag;
|
||||
$this->cacheClearerId = $cacheClearerId;
|
||||
$this->cachePoolClearerTag = $cachePoolClearerTag;
|
||||
$this->cacheSystemClearerId = $cacheSystemClearerId;
|
||||
$this->cacheSystemClearerTag = $cacheSystemClearerTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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');
|
||||
|
||||
$allPools = [];
|
||||
$clearers = [];
|
||||
$attributes = [
|
||||
'provider',
|
||||
'name',
|
||||
'namespace',
|
||||
'default_lifetime',
|
||||
'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 ('namespace' !== $attr || !\in_array($class, [ArrayAdapter::class, NullAdapter::class], true)) {
|
||||
$pool->replaceArgument($i++, $tags[0][$attr]);
|
||||
}
|
||||
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" 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);
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
if ($container->hasDefinition('console.command.cache_pool_list')) {
|
||||
$container->getDefinition('console.command.cache_pool_list')->replaceArgument(0, array_keys($allPools));
|
||||
}
|
||||
}
|
||||
|
||||
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, $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;
|
||||
}
|
||||
}
|
||||
60
vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php
vendored
Normal file
60
vendor/symfony/cache/DependencyInjection/CachePoolPrunerPass.php
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
<?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 Symfony\Component\Cache\DependencyInjection;
|
||||
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use 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')
|
||||
{
|
||||
$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));
|
||||
}
|
||||
}
|
||||
120
vendor/symfony/cache/DoctrineProvider.php
vendored
Normal file
120
vendor/symfony/cache/DoctrineProvider.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 Symfony\Component\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
if (!class_exists(CacheProvider::class)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class DoctrineProvider extends CacheProvider implements PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
|
||||
public function __construct(CacheItemPoolInterface $pool)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
}
|
||||
25
vendor/symfony/cache/Exception/CacheException.php
vendored
Normal file
25
vendor/symfony/cache/Exception/CacheException.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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 Symfony\Component\Cache\Exception;
|
||||
|
||||
use Psr\Cache\CacheException as Psr6CacheInterface;
|
||||
use Psr\SimpleCache\CacheException as SimpleCacheInterface;
|
||||
|
||||
if (interface_exists(SimpleCacheInterface::class)) {
|
||||
class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class CacheException extends \Exception implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
||||
25
vendor/symfony/cache/Exception/InvalidArgumentException.php
vendored
Normal file
25
vendor/symfony/cache/Exception/InvalidArgumentException.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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 Symfony\Component\Cache\Exception;
|
||||
|
||||
use Psr\Cache\InvalidArgumentException as Psr6CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface;
|
||||
|
||||
if (interface_exists(SimpleCacheInterface::class)) {
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
||||
25
vendor/symfony/cache/Exception/LogicException.php
vendored
Normal file
25
vendor/symfony/cache/Exception/LogicException.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?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 Symfony\Component\Cache\Exception;
|
||||
|
||||
use Psr\Cache\CacheException as Psr6CacheInterface;
|
||||
use Psr\SimpleCache\CacheException as SimpleCacheInterface;
|
||||
|
||||
if (interface_exists(SimpleCacheInterface::class)) {
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/cache/LICENSE
vendored
Normal file
19
vendor/symfony/cache/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2016-2022 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
161
vendor/symfony/cache/LockRegistry.php
vendored
Normal file
161
vendor/symfony/cache/LockRegistry.php
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
<?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 Symfony\Component\Cache;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use 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.'DoctrineAdapter.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.'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.'SimpleCacheAdapter.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:\"\0Exception\0trace\";a:0:{}}");
|
||||
self::$signalingCallback ?? self::$signalingCallback = function () { throw self::$signalingException; };
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
99
vendor/symfony/cache/Marshaller/DefaultMarshaller.php
vendored
Normal file
99
vendor/symfony/cache/Marshaller/DefaultMarshaller.php
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
<?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 Symfony\Component\Cache\Marshaller;
|
||||
|
||||
use 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;
|
||||
|
||||
public function __construct(bool $useIgbinarySerialize = null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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) {
|
||||
$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($class)
|
||||
{
|
||||
throw new \DomainException('Class not found: '.$class);
|
||||
}
|
||||
}
|
||||
53
vendor/symfony/cache/Marshaller/DeflateMarshaller.php
vendored
Normal file
53
vendor/symfony/cache/Marshaller/DeflateMarshaller.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 Symfony\Component\Cache\Marshaller;
|
||||
|
||||
use 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);
|
||||
}
|
||||
}
|
||||
40
vendor/symfony/cache/Marshaller/MarshallerInterface.php
vendored
Normal file
40
vendor/symfony/cache/Marshaller/MarshallerInterface.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?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 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);
|
||||
}
|
||||
89
vendor/symfony/cache/Marshaller/TagAwareMarshaller.php
vendored
Normal file
89
vendor/symfony/cache/Marshaller/TagAwareMarshaller.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 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'] ?? "\0\0\0\0\0\0\0\0").pack('N', \strlen($v['tags'])).$v['tags'].$v['value'];
|
||||
$serialized[$id][9] = "\x5F";
|
||||
}
|
||||
} else {
|
||||
// other arbitratry 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] || "\0" !== $value[5] || "\x5F" !== $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] = "\0";
|
||||
$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' => "\0\0\0\0\0\0\0\0" === $meta ? null : $meta,
|
||||
];
|
||||
}
|
||||
}
|
||||
23
vendor/symfony/cache/PruneableInterface.php
vendored
Normal file
23
vendor/symfony/cache/PruneableInterface.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 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();
|
||||
}
|
||||
284
vendor/symfony/cache/Psr16Cache.php
vendored
Normal file
284
vendor/symfony/cache/Psr16Cache.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 Symfony\Component\Cache;
|
||||
|
||||
use Psr\Cache\CacheException as Psr6CacheException;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\SimpleCache\CacheException as SimpleCacheException;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use 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;
|
||||
$item->key = $allowInt && \is_int($key) ? (string) $key : CacheItem::validateKey($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.', \is_object($keys) ? \get_class($keys) : \gettype($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])."\x5F" => $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.', \is_object($values) ? \get_class($values) : \gettype($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.', \is_object($keys) ? \get_class($keys) : \gettype($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
vendor/symfony/cache/README.md
vendored
Normal file
19
vendor/symfony/cache/README.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Symfony PSR-6 implementation for caching
|
||||
========================================
|
||||
|
||||
The Cache component provides an extended
|
||||
[PSR-6](http://www.php-fig.org/psr/psr-6/) implementation for adding cache to
|
||||
your applications. It is designed to have a low overhead so that caching is
|
||||
fastest. It ships with a few caching adapters for the most widespread and
|
||||
suited to caching backends. It also provides a `doctrine/cache` proxy adapter
|
||||
to cover more advanced caching needs and a proxy adapter for greater
|
||||
interoperability between PSR-6 implementations.
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/cache.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
in the [main Symfony repository](https://github.com/symfony/symfony)
|
||||
21
vendor/symfony/cache/ResettableInterface.php
vendored
Normal file
21
vendor/symfony/cache/ResettableInterface.php
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<?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 Symfony\Component\Cache;
|
||||
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
/**
|
||||
* Resets a pool's local state.
|
||||
*/
|
||||
interface ResettableInterface extends ResetInterface
|
||||
{
|
||||
}
|
||||
199
vendor/symfony/cache/Simple/AbstractCache.php
vendored
Normal file
199
vendor/symfony/cache/Simple/AbstractCache.php
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\AbstractTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', AbstractCache::class, AbstractAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use AbstractAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
abstract class AbstractCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use AbstractTrait {
|
||||
deleteItems as private;
|
||||
AbstractTrait::deleteItem as delete;
|
||||
AbstractTrait::hasItem as has;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected const NS_SEPARATOR = ':';
|
||||
|
||||
private $defaultLifetime;
|
||||
|
||||
protected function __construct(string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
$this->defaultLifetime = max(0, $defaultLifetime);
|
||||
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$id = $this->getId($key);
|
||||
|
||||
try {
|
||||
foreach ($this->doFetch([$id]) as $value) {
|
||||
return $value;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
CacheItem::validateKey($key);
|
||||
|
||||
return $this->setMultiple([$key => $value], $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
|
||||
}
|
||||
$ids = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$ids[] = $this->getId($key);
|
||||
}
|
||||
try {
|
||||
$values = $this->doFetch($ids);
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch values: '.$e->getMessage(), ['keys' => $keys, 'exception' => $e]);
|
||||
$values = [];
|
||||
}
|
||||
$ids = array_combine($ids, $keys);
|
||||
|
||||
return $this->generateValues($values, $ids, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if (!\is_array($values) && !$values instanceof \Traversable) {
|
||||
throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', \is_object($values) ? \get_class($values) : \gettype($values)));
|
||||
}
|
||||
$valuesById = [];
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (\is_int($key)) {
|
||||
$key = (string) $key;
|
||||
}
|
||||
$valuesById[$this->getId($key)] = $value;
|
||||
}
|
||||
if (false === $ttl = $this->normalizeTtl($ttl)) {
|
||||
return $this->doDelete(array_keys($valuesById));
|
||||
}
|
||||
|
||||
try {
|
||||
$e = $this->doSave($valuesById, $ttl);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
if (true === $e || [] === $e) {
|
||||
return true;
|
||||
}
|
||||
$keys = [];
|
||||
foreach (\is_array($e) ? $e : array_keys($valuesById) as $id) {
|
||||
$keys[] = substr($id, \strlen($this->namespace));
|
||||
}
|
||||
$message = 'Failed to save values'.($e instanceof \Exception ? ': '.$e->getMessage() : '.');
|
||||
CacheItem::log($this->logger, $message, ['keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
|
||||
}
|
||||
|
||||
return $this->deleteItems($keys);
|
||||
}
|
||||
|
||||
private function normalizeTtl($ttl)
|
||||
{
|
||||
if (null === $ttl) {
|
||||
return $this->defaultLifetime;
|
||||
}
|
||||
if ($ttl instanceof \DateInterval) {
|
||||
$ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U');
|
||||
}
|
||||
if (\is_int($ttl)) {
|
||||
return 0 < $ttl ? $ttl : false;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl)));
|
||||
}
|
||||
|
||||
private function generateValues(iterable $values, array &$keys, $default): iterable
|
||||
{
|
||||
try {
|
||||
foreach ($values 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 => $value;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch values: '.$e->getMessage(), ['keys' => array_values($keys), 'exception' => $e]);
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
vendor/symfony/cache/Simple/ApcuCache.php
vendored
Normal file
31
vendor/symfony/cache/Simple/ApcuCache.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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\ApcuAdapter;
|
||||
use Symfony\Component\Cache\Traits\ApcuTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ApcuCache::class, ApcuAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use ApcuAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class ApcuCache extends AbstractCache
|
||||
{
|
||||
use ApcuTrait;
|
||||
|
||||
public function __construct(string $namespace = '', int $defaultLifetime = 0, string $version = null)
|
||||
{
|
||||
$this->init($namespace, $defaultLifetime, $version);
|
||||
}
|
||||
}
|
||||
167
vendor/symfony/cache/Simple/ArrayCache.php
vendored
Normal file
167
vendor/symfony/cache/Simple/ArrayCache.php
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\ArrayAdapter;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ArrayTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ArrayCache::class, ArrayAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use ArrayAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class ArrayCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use ArrayTrait {
|
||||
ArrayTrait::deleteItem as delete;
|
||||
ArrayTrait::hasItem as has;
|
||||
}
|
||||
|
||||
private $defaultLifetime;
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$this->defaultLifetime = $defaultLifetime;
|
||||
$this->storeSerialized = $storeSerialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (!\is_string($key) || !isset($this->expiries[$key])) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > microtime(true) || !$this->delete($key))) {
|
||||
$this->values[$key] = null;
|
||||
|
||||
return $default;
|
||||
}
|
||||
if (!$this->storeSerialized) {
|
||||
return $this->values[$key];
|
||||
}
|
||||
$value = $this->unfreeze($key, $isHit);
|
||||
|
||||
return $isHit ? $value : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key) || !isset($this->expiries[$key])) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->generateItems($keys, microtime(true), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; });
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
if (!\is_array($keys) && !$keys instanceof \Traversable) {
|
||||
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
$this->delete($key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
|
||||
return $this->setMultiple([$key => $value], $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if (!\is_array($values) && !$values instanceof \Traversable) {
|
||||
throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', \is_object($values) ? \get_class($values) : \gettype($values)));
|
||||
}
|
||||
$valuesArray = [];
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (!\is_int($key) && !(\is_string($key) && isset($this->expiries[$key]))) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
$valuesArray[$key] = $value;
|
||||
}
|
||||
if (false === $ttl = $this->normalizeTtl($ttl)) {
|
||||
return $this->deleteMultiple(array_keys($valuesArray));
|
||||
}
|
||||
$expiry = 0 < $ttl ? microtime(true) + $ttl : \PHP_INT_MAX;
|
||||
|
||||
foreach ($valuesArray as $key => $value) {
|
||||
if ($this->storeSerialized && null === $value = $this->freeze($value, $key)) {
|
||||
return false;
|
||||
}
|
||||
$this->values[$key] = $value;
|
||||
$this->expiries[$key] = $expiry;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function normalizeTtl($ttl)
|
||||
{
|
||||
if (null === $ttl) {
|
||||
return $this->defaultLifetime;
|
||||
}
|
||||
if ($ttl instanceof \DateInterval) {
|
||||
$ttl = (int) \DateTime::createFromFormat('U', 0)->add($ttl)->format('U');
|
||||
}
|
||||
if (\is_int($ttl)) {
|
||||
return 0 < $ttl ? $ttl : false;
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException(sprintf('Expiration date must be an integer, a DateInterval or null, "%s" given.', \is_object($ttl) ? \get_class($ttl) : \gettype($ttl)));
|
||||
}
|
||||
}
|
||||
271
vendor/symfony/cache/Simple/ChainCache.php
vendored
Normal file
271
vendor/symfony/cache/Simple/ChainCache.php
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\ChainAdapter;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', ChainCache::class, ChainAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Chains several caches together.
|
||||
*
|
||||
* Cached items are fetched from the first cache having them in its data store.
|
||||
* They are saved and deleted in all caches at once.
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use ChainAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class ChainCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $miss;
|
||||
private $caches = [];
|
||||
private $defaultLifetime;
|
||||
private $cacheCount;
|
||||
|
||||
/**
|
||||
* @param Psr16CacheInterface[] $caches The ordered list of caches used to fetch cached items
|
||||
* @param int $defaultLifetime The lifetime of items propagated from lower caches to upper ones
|
||||
*/
|
||||
public function __construct(array $caches, int $defaultLifetime = 0)
|
||||
{
|
||||
if (!$caches) {
|
||||
throw new InvalidArgumentException('At least one cache must be specified.');
|
||||
}
|
||||
|
||||
foreach ($caches as $cache) {
|
||||
if (!$cache instanceof Psr16CacheInterface) {
|
||||
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), Psr16CacheInterface::class));
|
||||
}
|
||||
}
|
||||
|
||||
$this->miss = new \stdClass();
|
||||
$this->caches = array_values($caches);
|
||||
$this->cacheCount = \count($this->caches);
|
||||
$this->defaultLifetime = 0 < $defaultLifetime ? $defaultLifetime : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$miss = null !== $default && \is_object($default) ? $default : $this->miss;
|
||||
|
||||
foreach ($this->caches as $i => $cache) {
|
||||
$value = $cache->get($key, $miss);
|
||||
|
||||
if ($miss !== $value) {
|
||||
while (0 <= --$i) {
|
||||
$this->caches[$i]->set($key, $value, $this->defaultLifetime);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
$miss = null !== $default && \is_object($default) ? $default : $this->miss;
|
||||
|
||||
return $this->generateItems($this->caches[0]->getMultiple($keys, $miss), 0, $miss, $default);
|
||||
}
|
||||
|
||||
private function generateItems(iterable $values, int $cacheIndex, $miss, $default): iterable
|
||||
{
|
||||
$missing = [];
|
||||
$nextCacheIndex = $cacheIndex + 1;
|
||||
$nextCache = $this->caches[$nextCacheIndex] ?? null;
|
||||
|
||||
foreach ($values as $k => $value) {
|
||||
if ($miss !== $value) {
|
||||
yield $k => $value;
|
||||
} elseif (!$nextCache) {
|
||||
yield $k => $default;
|
||||
} else {
|
||||
$missing[] = $k;
|
||||
}
|
||||
}
|
||||
|
||||
if ($missing) {
|
||||
$cache = $this->caches[$cacheIndex];
|
||||
$values = $this->generateItems($nextCache->getMultiple($missing, $miss), $nextCacheIndex, $miss, $default);
|
||||
|
||||
foreach ($values as $k => $value) {
|
||||
if ($miss !== $value) {
|
||||
$cache->set($k, $value, $this->defaultLifetime);
|
||||
yield $k => $value;
|
||||
} else {
|
||||
yield $k => $default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
foreach ($this->caches as $cache) {
|
||||
if ($cache->has($key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$cleared = true;
|
||||
$i = $this->cacheCount;
|
||||
|
||||
while ($i--) {
|
||||
$cleared = $this->caches[$i]->clear() && $cleared;
|
||||
}
|
||||
|
||||
return $cleared;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
$deleted = true;
|
||||
$i = $this->cacheCount;
|
||||
|
||||
while ($i--) {
|
||||
$deleted = $this->caches[$i]->delete($key) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
if ($keys instanceof \Traversable) {
|
||||
$keys = iterator_to_array($keys, false);
|
||||
}
|
||||
$deleted = true;
|
||||
$i = $this->cacheCount;
|
||||
|
||||
while ($i--) {
|
||||
$deleted = $this->caches[$i]->deleteMultiple($keys) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
$saved = true;
|
||||
$i = $this->cacheCount;
|
||||
|
||||
while ($i--) {
|
||||
$saved = $this->caches[$i]->set($key, $value, $ttl) && $saved;
|
||||
}
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if ($values instanceof \Traversable) {
|
||||
$valuesIterator = $values;
|
||||
$values = function () use ($valuesIterator, &$values) {
|
||||
$generatedValues = [];
|
||||
|
||||
foreach ($valuesIterator as $key => $value) {
|
||||
yield $key => $value;
|
||||
$generatedValues[$key] = $value;
|
||||
}
|
||||
|
||||
$values = $generatedValues;
|
||||
};
|
||||
$values = $values();
|
||||
}
|
||||
$saved = true;
|
||||
$i = $this->cacheCount;
|
||||
|
||||
while ($i--) {
|
||||
$saved = $this->caches[$i]->setMultiple($values, $ttl) && $saved;
|
||||
}
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
$pruned = true;
|
||||
|
||||
foreach ($this->caches as $cache) {
|
||||
if ($cache instanceof PruneableInterface) {
|
||||
$pruned = $cache->prune() && $pruned;
|
||||
}
|
||||
}
|
||||
|
||||
return $pruned;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
foreach ($this->caches as $cache) {
|
||||
if ($cache instanceof ResetInterface) {
|
||||
$cache->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
vendor/symfony/cache/Simple/DoctrineCache.php
vendored
Normal file
34
vendor/symfony/cache/Simple/DoctrineCache.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Symfony\Component\Cache\Adapter\DoctrineAdapter;
|
||||
use Symfony\Component\Cache\Traits\DoctrineTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', DoctrineCache::class, DoctrineAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use DoctrineAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class DoctrineCache extends AbstractCache
|
||||
{
|
||||
use DoctrineTrait;
|
||||
|
||||
public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
|
||||
{
|
||||
parent::__construct('', $defaultLifetime);
|
||||
$this->provider = $provider;
|
||||
$provider->setNamespace($namespace);
|
||||
}
|
||||
}
|
||||
36
vendor/symfony/cache/Simple/FilesystemCache.php
vendored
Normal file
36
vendor/symfony/cache/Simple/FilesystemCache.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\Traits\FilesystemTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', FilesystemCache::class, FilesystemAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use FilesystemAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class FilesystemCache extends AbstractCache 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);
|
||||
}
|
||||
}
|
||||
34
vendor/symfony/cache/Simple/MemcachedCache.php
vendored
Normal file
34
vendor/symfony/cache/Simple/MemcachedCache.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\MemcachedAdapter;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Traits\MemcachedTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', MemcachedCache::class, MemcachedAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use MemcachedAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class MemcachedCache extends AbstractCache
|
||||
{
|
||||
use MemcachedTrait;
|
||||
|
||||
protected $maxIdLength = 250;
|
||||
|
||||
public function __construct(\Memcached $client, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
$this->init($client, $namespace, $defaultLifetime, $marshaller);
|
||||
}
|
||||
}
|
||||
104
vendor/symfony/cache/Simple/NullCache.php
vendored
Normal file
104
vendor/symfony/cache/Simple/NullCache.php
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\NullAdapter;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', NullCache::class, NullAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use NullAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class NullCache implements Psr16CacheInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
59
vendor/symfony/cache/Simple/PdoCache.php
vendored
Normal file
59
vendor/symfony/cache/Simple/PdoCache.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\PdoAdapter;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\Traits\PdoTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PdoCache::class, PdoAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use PdoAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class PdoCache extends AbstractCache implements PruneableInterface
|
||||
{
|
||||
use PdoTrait;
|
||||
|
||||
protected $maxIdLength = 255;
|
||||
|
||||
/**
|
||||
* You can either pass an existing database connection as PDO instance or
|
||||
* a Doctrine DBAL Connection or a DSN string that will be used to
|
||||
* lazy-connect to the database when the cache is actually used.
|
||||
*
|
||||
* When a Doctrine DBAL Connection is passed, 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]
|
||||
* * 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|Connection|string $connOrDsn a \PDO or Connection instance or DSN string or null
|
||||
*
|
||||
* @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)
|
||||
{
|
||||
$this->init($connOrDsn, $namespace, $defaultLifetime, $options, $marshaller);
|
||||
}
|
||||
}
|
||||
256
vendor/symfony/cache/Simple/PhpArrayCache.php
vendored
Normal file
256
vendor/symfony/cache/Simple/PhpArrayCache.php
vendored
Normal file
@@ -0,0 +1,256 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\PhpArrayAdapter;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\PhpArrayTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PhpArrayCache::class, PhpArrayAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use PhpArrayAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class PhpArrayCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use PhpArrayTrait;
|
||||
|
||||
/**
|
||||
* @param string $file The PHP file were values are cached
|
||||
* @param Psr16CacheInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||
*/
|
||||
public function __construct(string $file, Psr16CacheInterface $fallbackPool)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->pool = $fallbackPool;
|
||||
}
|
||||
|
||||
/**
|
||||
* This adapter takes advantage of how PHP stores arrays in its latest versions.
|
||||
*
|
||||
* @param string $file The PHP file were values are cached
|
||||
* @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||
*
|
||||
* @return Psr16CacheInterface
|
||||
*/
|
||||
public static function create($file, Psr16CacheInterface $fallbackPool)
|
||||
{
|
||||
return new static($file, $fallbackPool);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
if (!isset($this->keys[$key])) {
|
||||
return $this->pool->get($key, $default);
|
||||
}
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
|
||||
if ('N;' === $value) {
|
||||
return null;
|
||||
}
|
||||
if ($value instanceof \Closure) {
|
||||
try {
|
||||
return $value();
|
||||
} catch (\Throwable $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
|
||||
}
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return $this->generateItems($keys, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return isset($this->keys[$key]) || $this->pool->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->keys[$key]) && $this->pool->delete($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
if (!\is_array($keys) && !$keys instanceof \Traversable) {
|
||||
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given.', \is_object($keys) ? \get_class($keys) : \gettype($keys)));
|
||||
}
|
||||
|
||||
$deleted = true;
|
||||
$fallbackKeys = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
|
||||
if (isset($this->keys[$key])) {
|
||||
$deleted = false;
|
||||
} else {
|
||||
$fallbackKeys[] = $key;
|
||||
}
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
if ($fallbackKeys) {
|
||||
$deleted = $this->pool->deleteMultiple($fallbackKeys) && $deleted;
|
||||
}
|
||||
|
||||
return $deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
if (!\is_string($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
if (null === $this->values) {
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
return !isset($this->keys[$key]) && $this->pool->set($key, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if (!\is_array($values) && !$values instanceof \Traversable) {
|
||||
throw new InvalidArgumentException(sprintf('Cache values must be array or Traversable, "%s" given.', \is_object($values) ? \get_class($values) : \gettype($values)));
|
||||
}
|
||||
|
||||
$saved = true;
|
||||
$fallbackValues = [];
|
||||
|
||||
foreach ($values as $key => $value) {
|
||||
if (!\is_string($key) && !\is_int($key)) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given.', \is_object($key) ? \get_class($key) : \gettype($key)));
|
||||
}
|
||||
|
||||
if (isset($this->keys[$key])) {
|
||||
$saved = false;
|
||||
} else {
|
||||
$fallbackValues[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fallbackValues) {
|
||||
$saved = $this->pool->setMultiple($fallbackValues, $ttl) && $saved;
|
||||
}
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
private function generateItems(array $keys, $default): iterable
|
||||
{
|
||||
$fallbackKeys = [];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (isset($this->keys[$key])) {
|
||||
$value = $this->values[$this->keys[$key]];
|
||||
|
||||
if ('N;' === $value) {
|
||||
yield $key => null;
|
||||
} elseif ($value instanceof \Closure) {
|
||||
try {
|
||||
yield $key => $value();
|
||||
} catch (\Throwable $e) {
|
||||
yield $key => $default;
|
||||
}
|
||||
} else {
|
||||
yield $key => $value;
|
||||
}
|
||||
} else {
|
||||
$fallbackKeys[] = $key;
|
||||
}
|
||||
}
|
||||
|
||||
if ($fallbackKeys) {
|
||||
yield from $this->pool->getMultiple($fallbackKeys, $default);
|
||||
}
|
||||
}
|
||||
}
|
||||
45
vendor/symfony/cache/Simple/PhpFilesCache.php
vendored
Normal file
45
vendor/symfony/cache/Simple/PhpFilesCache.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\Traits\PhpFilesTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', PhpFilesCache::class, PhpFilesAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use PhpFilesAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class PhpFilesCache extends AbstractCache implements PruneableInterface
|
||||
{
|
||||
use PhpFilesTrait;
|
||||
|
||||
/**
|
||||
* @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);
|
||||
};
|
||||
}
|
||||
}
|
||||
23
vendor/symfony/cache/Simple/Psr6Cache.php
vendored
Normal file
23
vendor/symfony/cache/Simple/Psr6Cache.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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" instead.', Psr6Cache::class, Psr16Cache::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use Psr16Cache instead.
|
||||
*/
|
||||
class Psr6Cache extends Psr16Cache
|
||||
{
|
||||
}
|
||||
37
vendor/symfony/cache/Simple/RedisCache.php
vendored
Normal file
37
vendor/symfony/cache/Simple/RedisCache.php
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
use Symfony\Component\Cache\Traits\RedisClusterProxy;
|
||||
use Symfony\Component\Cache\Traits\RedisProxy;
|
||||
use Symfony\Component\Cache\Traits\RedisTrait;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', RedisCache::class, RedisAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use RedisAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class RedisCache extends AbstractCache
|
||||
{
|
||||
use RedisTrait;
|
||||
|
||||
/**
|
||||
* @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis
|
||||
*/
|
||||
public function __construct($redis, string $namespace = '', int $defaultLifetime = 0, MarshallerInterface $marshaller = null)
|
||||
{
|
||||
$this->init($redis, $namespace, $defaultLifetime, $marshaller);
|
||||
}
|
||||
}
|
||||
258
vendor/symfony/cache/Simple/TraceableCache.php
vendored
Normal file
258
vendor/symfony/cache/Simple/TraceableCache.php
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
<?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 Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Component\Cache\Adapter\TraceableAdapter;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
@trigger_error(sprintf('The "%s" class is deprecated since Symfony 4.3, use "%s" and type-hint for "%s" instead.', TraceableCache::class, TraceableAdapter::class, CacheInterface::class), \E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use TraceableAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class TraceableCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
private $miss;
|
||||
private $calls = [];
|
||||
|
||||
public function __construct(Psr16CacheInterface $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
$this->miss = new \stdClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function get($key, $default = null)
|
||||
{
|
||||
$miss = null !== $default && \is_object($default) ? $default : $this->miss;
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$value = $this->pool->get($key, $miss);
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
if ($event->result[$key] = $miss !== $value) {
|
||||
++$event->hits;
|
||||
} else {
|
||||
++$event->misses;
|
||||
$value = $default;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has($key)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$key] = $this->pool->has($key);
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete($key)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$key] = $this->pool->delete($key);
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result[$key] = $this->pool->set($key, $value, $ttl);
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
$event->result['keys'] = [];
|
||||
|
||||
if ($values instanceof \Traversable) {
|
||||
$values = function () use ($values, $event) {
|
||||
foreach ($values as $k => $v) {
|
||||
$event->result['keys'][] = $k;
|
||||
yield $k => $v;
|
||||
}
|
||||
};
|
||||
$values = $values();
|
||||
} elseif (\is_array($values)) {
|
||||
$event->result['keys'] = array_keys($values);
|
||||
}
|
||||
|
||||
try {
|
||||
return $event->result['result'] = $this->pool->setMultiple($values, $ttl);
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return iterable
|
||||
*/
|
||||
public function getMultiple($keys, $default = null)
|
||||
{
|
||||
$miss = null !== $default && \is_object($default) ? $default : $this->miss;
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$result = $this->pool->getMultiple($keys, $miss);
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
$f = function () use ($result, $event, $miss, $default) {
|
||||
$event->result = [];
|
||||
foreach ($result as $key => $value) {
|
||||
if ($event->result[$key] = $miss !== $value) {
|
||||
++$event->hits;
|
||||
} else {
|
||||
++$event->misses;
|
||||
$value = $default;
|
||||
}
|
||||
yield $key => $value;
|
||||
}
|
||||
};
|
||||
|
||||
return $f();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
return $event->result = $this->pool->clear();
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteMultiple($keys)
|
||||
{
|
||||
$event = $this->start(__FUNCTION__);
|
||||
if ($keys instanceof \Traversable) {
|
||||
$keys = $event->result['keys'] = iterator_to_array($keys, false);
|
||||
} else {
|
||||
$event->result['keys'] = $keys;
|
||||
}
|
||||
try {
|
||||
return $event->result['result'] = $this->pool->deleteMultiple($keys);
|
||||
} 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) {
|
||||
return;
|
||||
}
|
||||
$event = $this->start(__FUNCTION__);
|
||||
try {
|
||||
$this->pool->reset();
|
||||
} finally {
|
||||
$event->end = microtime(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCalls()
|
||||
{
|
||||
try {
|
||||
return $this->calls;
|
||||
} finally {
|
||||
$this->calls = [];
|
||||
}
|
||||
}
|
||||
|
||||
private function start(string $name): TraceableCacheEvent
|
||||
{
|
||||
$this->calls[] = $event = new TraceableCacheEvent();
|
||||
$event->name = $name;
|
||||
$event->start = microtime(true);
|
||||
|
||||
return $event;
|
||||
}
|
||||
}
|
||||
|
||||
class TraceableCacheEvent
|
||||
{
|
||||
public $name;
|
||||
public $start;
|
||||
public $end;
|
||||
public $result;
|
||||
public $hits = 0;
|
||||
public $misses = 0;
|
||||
}
|
||||
164
vendor/symfony/cache/Traits/AbstractAdapterTrait.php
vendored
Normal file
164
vendor/symfony/cache/Traits/AbstractAdapterTrait.php
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Psr\Cache\CacheItemInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait AbstractAdapterTrait
|
||||
{
|
||||
use AbstractTrait;
|
||||
|
||||
/**
|
||||
* @var \Closure needs to be set by class, signature is function(string <key>, mixed <value>, bool <isHit>)
|
||||
*/
|
||||
private $createCacheItem;
|
||||
|
||||
/**
|
||||
* @var \Closure needs to be set by class, signature is function(array <deferred>, string <namespace>, array <&expiredIds>)
|
||||
*/
|
||||
private $mergeByLifetime;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getItem($key)
|
||||
{
|
||||
$id = $this->getId($key);
|
||||
|
||||
if (isset($this->deferred[$key])) {
|
||||
$this->commit();
|
||||
}
|
||||
|
||||
$f = $this->createCacheItem;
|
||||
$isHit = false;
|
||||
$value = null;
|
||||
|
||||
try {
|
||||
foreach ($this->doFetch([$id]) as $value) {
|
||||
$isHit = true;
|
||||
}
|
||||
|
||||
return $f($key, $value, $isHit);
|
||||
} catch (\Exception $e) {
|
||||
CacheItem::log($this->logger, 'Failed to fetch key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
|
||||
}
|
||||
|
||||
return $f($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]);
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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): iterable
|
||||
{
|
||||
$f = $this->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]);
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
yield $key => $f($key, null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
320
vendor/symfony/cache/Traits/AbstractTrait.php
vendored
Normal file
320
vendor/symfony/cache/Traits/AbstractTrait.php
vendored
Normal file
@@ -0,0 +1,320 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait AbstractTrait
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
private $namespace;
|
||||
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 The corresponding values found in the cache
|
||||
*/
|
||||
abstract protected function doFetch(array $ids);
|
||||
|
||||
/**
|
||||
* Confirms if the cache contains specified cache item.
|
||||
*
|
||||
* @param string $id The identifier for which to check existence
|
||||
*
|
||||
* @return bool True if item exists in the cache, false otherwise
|
||||
*/
|
||||
abstract protected function doHave($id);
|
||||
|
||||
/**
|
||||
* Deletes all items in the pool.
|
||||
*
|
||||
* @param string $namespace The prefix used for all identifiers managed by this pool
|
||||
*
|
||||
* @return bool True if the pool was successfully cleared, false otherwise
|
||||
*/
|
||||
abstract protected function doClear($namespace);
|
||||
|
||||
/**
|
||||
* Removes multiple items from the pool.
|
||||
*
|
||||
* @param array $ids An array of identifiers that should be removed from the pool
|
||||
*
|
||||
* @return bool True if the items were successfully removed, false otherwise
|
||||
*/
|
||||
abstract protected 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
|
||||
*/
|
||||
abstract protected 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]);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @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]);
|
||||
} else {
|
||||
$this->namespaceVersion = $namespaceVersion;
|
||||
$this->ids = [];
|
||||
}
|
||||
} else {
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
$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]);
|
||||
|
||||
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]);
|
||||
$ok = false;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 resynchonization of it.
|
||||
*
|
||||
* @param bool $enable
|
||||
*
|
||||
* @return bool the previous state of versioning
|
||||
*/
|
||||
public function enableVersioning($enable = true)
|
||||
{
|
||||
$wasEnabled = $this->versioningIsEnabled;
|
||||
$this->versioningIsEnabled = (bool) $enable;
|
||||
$this->namespaceVersion = '';
|
||||
$this->ids = [];
|
||||
|
||||
return $wasEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
if ($this->deferred) {
|
||||
$this->commit();
|
||||
}
|
||||
$this->namespaceVersion = '';
|
||||
$this->ids = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Like the native unserialize() function but throws an exception if anything goes wrong.
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @deprecated since Symfony 4.2, use DefaultMarshaller instead.
|
||||
*/
|
||||
protected static function unserialize($value)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s::unserialize()" method is deprecated since Symfony 4.2, use DefaultMarshaller instead.', __CLASS__), \E_USER_DEPRECATED);
|
||||
|
||||
if ('b:0;' === $value) {
|
||||
return false;
|
||||
}
|
||||
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
|
||||
try {
|
||||
if (false !== $value = unserialize($value)) {
|
||||
return $value;
|
||||
}
|
||||
throw new \DomainException('Failed to unserialize cached value.');
|
||||
} catch (\Error $e) {
|
||||
throw new \ErrorException($e->getMessage(), $e->getCode(), \E_ERROR, $e->getFile(), $e->getLine());
|
||||
} finally {
|
||||
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
|
||||
}
|
||||
}
|
||||
|
||||
private function getId($key): string
|
||||
{
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_string($key) && isset($this->ids[$key])) {
|
||||
return $this->namespace.$this->namespaceVersion.$this->ids[$key];
|
||||
}
|
||||
CacheItem::validateKey($key);
|
||||
$this->ids[$key] = $key;
|
||||
|
||||
if (\count($this->ids) > 1000) {
|
||||
array_splice($this->ids, 0, 500); // 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($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), '/', '_');
|
||||
}
|
||||
}
|
||||
128
vendor/symfony/cache/Traits/ApcuTrait.php
vendored
Normal file
128
vendor/symfony/cache/Traits/ApcuTrait.php
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait ApcuTrait
|
||||
{
|
||||
public static function isSupported()
|
||||
{
|
||||
return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), \FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
private function init(string $namespace, int $defaultLifetime, ?string $version)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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] = $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($id)
|
||||
{
|
||||
return apcu_exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
184
vendor/symfony/cache/Traits/ArrayTrait.php
vendored
Normal file
184
vendor/symfony/cache/Traits/ArrayTrait.php
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Psr\Log\LoggerAwareTrait;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait ArrayTrait
|
||||
{
|
||||
use LoggerAwareTrait;
|
||||
|
||||
private $storeSerialized;
|
||||
private $values = [];
|
||||
private $expiries = [];
|
||||
|
||||
/**
|
||||
* 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}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasItem($key)
|
||||
{
|
||||
if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
|
||||
return true;
|
||||
}
|
||||
CacheItem::validateKey($key);
|
||||
|
||||
return isset($this->expiries[$key]) && !$this->deleteItem($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/)
|
||||
{
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
|
||||
if ('' !== $prefix) {
|
||||
foreach ($this->values as $key => $value) {
|
||||
if (str_starts_with($key, $prefix)) {
|
||||
unset($this->values[$key], $this->expiries[$key]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->values = $this->expiries = [];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteItem($key)
|
||||
{
|
||||
if (!\is_string($key) || !isset($this->expiries[$key])) {
|
||||
CacheItem::validateKey($key);
|
||||
}
|
||||
unset($this->values[$key], $this->expiries[$key]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->clear();
|
||||
}
|
||||
|
||||
private function generateItems(array $keys, float $now, callable $f): iterable
|
||||
{
|
||||
foreach ($keys as $i => $key) {
|
||||
if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] > $now || !$this->deleteItem($key))) {
|
||||
$this->values[$key] = $value = null;
|
||||
} else {
|
||||
$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, $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 = \is_object($value) ? \get_class($value) : \gettype($value);
|
||||
$message = sprintf('Failed to save key "{key}" of type %s: ', $type).$e->getMessage();
|
||||
CacheItem::log($this->logger, $message, ['key' => $key, 'exception' => $e]);
|
||||
|
||||
return null;
|
||||
}
|
||||
// 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]);
|
||||
$value = false;
|
||||
}
|
||||
if (false === $value) {
|
||||
$this->values[$key] = $value = null;
|
||||
$isHit = false;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
109
vendor/symfony/cache/Traits/ContractsTrait.php
vendored
Normal file
109
vendor/symfony/cache/Traits/ContractsTrait.php
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\LockRegistry;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Cache\CacheTrait;
|
||||
use 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);
|
||||
}
|
||||
}
|
||||
98
vendor/symfony/cache/Traits/DoctrineTrait.php
vendored
Normal file
98
vendor/symfony/cache/Traits/DoctrineTrait.php
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait DoctrineTrait
|
||||
{
|
||||
private $provider;
|
||||
|
||||
/**
|
||||
* {@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($id)
|
||||
{
|
||||
return $this->provider->contains($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($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);
|
||||
}
|
||||
}
|
||||
196
vendor/symfony/cache/Traits/FilesystemCommonTrait.php
vendored
Normal file
196
vendor/symfony/cache/Traits/FilesystemCommonTrait.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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use 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 (!file_exists($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($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 = (!file_exists($file) || $this->doUnlink($file) || !file_exists($file)) && $ok;
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
protected function doUnlink($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 && !file_exists($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 (!file_exists($directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$chars = '+-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
|
||||
for ($i = 0; $i < 38; ++$i) {
|
||||
if (!file_exists($directory.$chars[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for ($j = 0; $j < 38; ++$j) {
|
||||
if (!file_exists($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($type, $message, $file, $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 && file_exists($this->tmp)) {
|
||||
unlink($this->tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
124
vendor/symfony/cache/Traits/FilesystemTrait.php
vendored
Normal file
124
vendor/symfony/cache/Traits/FilesystemTrait.php
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use 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 (!file_exists($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($id)
|
||||
{
|
||||
$file = $this->getFile($id);
|
||||
|
||||
return file_exists($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));
|
||||
}
|
||||
}
|
||||
343
vendor/symfony/cache/Traits/MemcachedTrait.php
vendored
Normal file
343
vendor/symfony/cache/Traits/MemcachedTrait.php
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
|
||||
/**
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait MemcachedTrait
|
||||
{
|
||||
private static $defaultClientOptions = [
|
||||
'persistent_id' => null,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
\Memcached::OPT_SERIALIZER => \Memcached::SERIALIZER_PHP,
|
||||
];
|
||||
|
||||
/**
|
||||
* 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 static $RESERVED_MEMCACHED = " \n\r\t\v\f\0";
|
||||
private static $RESERVED_PSR6 = '@()\{}/';
|
||||
|
||||
private $marshaller;
|
||||
private $client;
|
||||
private $lazyClient;
|
||||
|
||||
public static function isSupported()
|
||||
{
|
||||
return \extension_loaded('memcached') && version_compare(phpversion('memcached'), \PHP_VERSION_ID >= 80100 ? '3.1.6' : '2.2.0', '>=');
|
||||
}
|
||||
|
||||
private function init(\Memcached $client, string $namespace, int $defaultLifetime, ?MarshallerInterface $marshaller)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.', \gettype($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 {
|
||||
$options += static::$defaultClientOptions;
|
||||
$client = new \Memcached($options['persistent_id']);
|
||||
$username = $options['username'];
|
||||
$password = $options['password'];
|
||||
|
||||
// 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));
|
||||
}
|
||||
$opt = \constant('Memcached::OPT_'.$name);
|
||||
|
||||
unset($options[$name]);
|
||||
$options[$opt] = $value;
|
||||
}
|
||||
$client->setOptions($options);
|
||||
|
||||
// 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($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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($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);
|
||||
}
|
||||
}
|
||||
505
vendor/symfony/cache/Traits/PdoTrait.php
vendored
Normal file
505
vendor/symfony/cache/Traits/PdoTrait.php
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||
use Doctrine\DBAL\DriverManager;
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Exception\TableNotFoundException;
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Statement;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
trait PdoTrait
|
||||
{
|
||||
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 function init($connOrDsn, string $namespace, int $defaultLifetime, array $options, ?MarshallerInterface $marshaller)
|
||||
{
|
||||
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 ($connOrDsn instanceof Connection) {
|
||||
$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__, \is_object($connOrDsn) ? \get_class($connOrDsn) : \gettype($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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 DBALException When the table already exists
|
||||
* @throws Exception When the table already exists
|
||||
* @throws \DomainException When an unsupported PDO driver is used
|
||||
*/
|
||||
public function createTable()
|
||||
{
|
||||
// connect if we are not yet
|
||||
$conn = $this->getConnection();
|
||||
|
||||
if ($conn instanceof Connection) {
|
||||
$types = [
|
||||
'mysql' => 'binary',
|
||||
'sqlite' => 'text',
|
||||
'pgsql' => 'string',
|
||||
'oci' => 'string',
|
||||
'sqlsrv' => 'string',
|
||||
];
|
||||
if (!isset($types[$this->driver])) {
|
||||
throw new \DomainException(sprintf('Creating the cache table is currently not implemented for PDO driver "%s".', $this->driver));
|
||||
}
|
||||
|
||||
$schema = new Schema();
|
||||
$table = $schema->createTable($this->table);
|
||||
$table->addColumn($this->idCol, $types[$this->driver], ['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]);
|
||||
|
||||
foreach ($schema->toSql($conn->getDatabasePlatform()) as $sql) {
|
||||
if (method_exists($conn, 'executeStatement')) {
|
||||
$conn->executeStatement($sql);
|
||||
} else {
|
||||
$conn->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
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 utf8_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));
|
||||
}
|
||||
|
||||
if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) {
|
||||
$conn->executeStatement($sql);
|
||||
} else {
|
||||
$conn->exec($sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function prune()
|
||||
{
|
||||
$deleteSql = "DELETE FROM $this->table WHERE $this->lifetimeCol + $this->timeCol <= :time";
|
||||
|
||||
if ('' !== $this->namespace) {
|
||||
$deleteSql .= " AND $this->idCol LIKE :namespace";
|
||||
}
|
||||
|
||||
$connection = $this->getConnection();
|
||||
$useDbalConstants = $connection instanceof Connection;
|
||||
|
||||
try {
|
||||
$delete = $connection->prepare($deleteSql);
|
||||
} catch (TableNotFoundException $e) {
|
||||
return true;
|
||||
} catch (\PDOException $e) {
|
||||
return true;
|
||||
}
|
||||
$delete->bindValue(':time', time(), $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
|
||||
if ('' !== $this->namespace) {
|
||||
$delete->bindValue(':namespace', sprintf('%s%%', $this->namespace), $useDbalConstants ? ParameterType::STRING : \PDO::PARAM_STR);
|
||||
}
|
||||
try {
|
||||
// Doctrine DBAL ^2.13 || >= 3.1
|
||||
if ($delete instanceof Statement && method_exists($delete, 'executeStatement')) {
|
||||
$delete->executeStatement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return $delete->execute();
|
||||
} catch (TableNotFoundException $e) {
|
||||
return true;
|
||||
} catch (\PDOException $e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doFetch(array $ids)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$useDbalConstants = $connection instanceof Connection;
|
||||
|
||||
$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, $useDbalConstants ? ParameterType::INTEGER : \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, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
foreach ($expired as $id) {
|
||||
$stmt->bindValue(++$i, $id);
|
||||
}
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doHave($id)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$useDbalConstants = $connection instanceof Connection;
|
||||
|
||||
$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(), $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
$result = $stmt->execute();
|
||||
|
||||
return (bool) (\is_object($result) ? $result->fetchOne() : $stmt->fetchColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($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 {
|
||||
if ($conn instanceof Connection && method_exists($conn, 'executeStatement')) {
|
||||
$conn->executeStatement($sql);
|
||||
} else {
|
||||
$conn->exec($sql);
|
||||
}
|
||||
} catch (TableNotFoundException $e) {
|
||||
} 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 (TableNotFoundException $e) {
|
||||
} 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();
|
||||
$useDbalConstants = $conn instanceof Connection;
|
||||
|
||||
$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 (TableNotFoundException $e) {
|
||||
if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$stmt = $conn->prepare($sql);
|
||||
} catch (\PDOException $e) {
|
||||
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$stmt = $conn->prepare($sql);
|
||||
}
|
||||
|
||||
if ('sqlsrv' === $driver || 'oci' === $driver) {
|
||||
$stmt->bindParam(1, $id);
|
||||
$stmt->bindParam(2, $id);
|
||||
$stmt->bindParam(3, $data, $useDbalConstants ? ParameterType::LARGE_OBJECT : \PDO::PARAM_LOB);
|
||||
$stmt->bindValue(4, $lifetime, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
$stmt->bindValue(5, $now, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
$stmt->bindParam(6, $data, $useDbalConstants ? ParameterType::LARGE_OBJECT : \PDO::PARAM_LOB);
|
||||
$stmt->bindValue(7, $lifetime, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
$stmt->bindValue(8, $now, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
} else {
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->bindParam(':data', $data, $useDbalConstants ? ParameterType::LARGE_OBJECT : \PDO::PARAM_LOB);
|
||||
$stmt->bindValue(':lifetime', $lifetime, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
$stmt->bindValue(':time', $now, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
}
|
||||
if (null === $driver) {
|
||||
$insertStmt = $conn->prepare($insertSql);
|
||||
|
||||
$insertStmt->bindParam(':id', $id);
|
||||
$insertStmt->bindParam(':data', $data, $useDbalConstants ? ParameterType::LARGE_OBJECT : \PDO::PARAM_LOB);
|
||||
$insertStmt->bindValue(':lifetime', $lifetime, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
$insertStmt->bindValue(':time', $now, $useDbalConstants ? ParameterType::INTEGER : \PDO::PARAM_INT);
|
||||
}
|
||||
|
||||
foreach ($values as $id => $data) {
|
||||
try {
|
||||
$result = $stmt->execute();
|
||||
} catch (TableNotFoundException $e) {
|
||||
if (!$conn->isTransactionActive() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$result = $stmt->execute();
|
||||
} catch (\PDOException $e) {
|
||||
if (!$conn->inTransaction() || \in_array($this->driver, ['pgsql', 'sqlite', 'sqlsrv'], true)) {
|
||||
$this->createTable();
|
||||
}
|
||||
$result = $stmt->execute();
|
||||
}
|
||||
if (null === $driver && !(\is_object($result) ? $result->rowCount() : $stmt->rowCount())) {
|
||||
try {
|
||||
$insertStmt->execute();
|
||||
} catch (DBALException|Exception $e) {
|
||||
} catch (\PDOException $e) {
|
||||
// A concurrent write won, let it be
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \PDO|Connection
|
||||
*/
|
||||
private function getConnection()
|
||||
{
|
||||
if (null === $this->conn) {
|
||||
if (strpos($this->dsn, '://')) {
|
||||
if (!class_exists(DriverManager::class)) {
|
||||
throw new InvalidArgumentException(sprintf('Failed to parse the DSN "%s". Try running "composer require doctrine/dbal".', $this->dsn));
|
||||
}
|
||||
$this->conn = DriverManager::getConnection(['url' => $this->dsn]);
|
||||
} else {
|
||||
$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) {
|
||||
if ($this->conn instanceof \PDO) {
|
||||
$this->driver = $this->conn->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
} else {
|
||||
$driver = $this->conn->getDriver();
|
||||
|
||||
switch (true) {
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\Mysqli\Driver:
|
||||
throw new \LogicException(sprintf('The adapter "%s" does not support the mysqli driver, use pdo_mysql instead.', static::class));
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\AbstractMySQLDriver:
|
||||
$this->driver = 'mysql';
|
||||
break;
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDOSqlite\Driver:
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDO\SQLite\Driver:
|
||||
$this->driver = 'sqlite';
|
||||
break;
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDOPgSql\Driver:
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDO\PgSQL\Driver:
|
||||
$this->driver = 'pgsql';
|
||||
break;
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\OCI8\Driver:
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDOOracle\Driver:
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDO\OCI\Driver:
|
||||
$this->driver = 'oci';
|
||||
break;
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\SQLSrv\Driver:
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDOSqlsrv\Driver:
|
||||
case $driver instanceof \Doctrine\DBAL\Driver\PDO\SQLSrv\Driver:
|
||||
$this->driver = 'sqlsrv';
|
||||
break;
|
||||
case $driver instanceof \Doctrine\DBAL\Driver:
|
||||
$this->driver = [
|
||||
'mssql' => 'sqlsrv',
|
||||
'oracle' => 'oci',
|
||||
'postgresql' => 'pgsql',
|
||||
'sqlite' => 'sqlite',
|
||||
'mysql' => 'mysql',
|
||||
][$driver->getDatabasePlatform()->getName()] ?? \get_class($driver);
|
||||
break;
|
||||
default:
|
||||
$this->driver = \get_class($driver);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
private function getServerVersion(): string
|
||||
{
|
||||
if (null === $this->serverVersion) {
|
||||
$conn = $this->conn instanceof \PDO ? $this->conn : $this->conn->getWrappedConnection();
|
||||
if ($conn instanceof \PDO) {
|
||||
$this->serverVersion = $conn->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||
} elseif ($conn instanceof ServerInfoAwareConnection) {
|
||||
$this->serverVersion = $conn->getServerVersion();
|
||||
} else {
|
||||
$this->serverVersion = '0';
|
||||
}
|
||||
}
|
||||
|
||||
return $this->serverVersion;
|
||||
}
|
||||
}
|
||||
169
vendor/symfony/cache/Traits/PhpArrayTrait.php
vendored
Normal file
169
vendor/symfony/cache/Traits/PhpArrayTrait.php
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\AdapterInterface;
|
||||
use Symfony\Component\Cache\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
|
||||
/**
|
||||
* @author Titouan Galopin <galopintitouan@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait PhpArrayTrait
|
||||
{
|
||||
use ProxyTrait;
|
||||
|
||||
private $file;
|
||||
private $keys;
|
||||
private $values;
|
||||
|
||||
private static $valuesCache = [];
|
||||
|
||||
/**
|
||||
* Store an array of cached values.
|
||||
*
|
||||
* @param array $values The cached values
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
$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);
|
||||
} catch (\Exception $e) {
|
||||
throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable "%s" value.', $key, \is_object($value) ? \get_class($value) : 'array'), 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, \gettype($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();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string $prefix
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear(/*string $prefix = ''*/)
|
||||
{
|
||||
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the cache file.
|
||||
*/
|
||||
private function initialize()
|
||||
{
|
||||
if (isset(self::$valuesCache[$this->file])) {
|
||||
$values = self::$valuesCache[$this->file];
|
||||
} elseif (!file_exists($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;
|
||||
}
|
||||
}
|
||||
}
|
||||
313
vendor/symfony/cache/Traits/PhpFilesTrait.php
vendored
Normal file
313
vendor/symfony/cache/Traits/PhpFilesTrait.php
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\VarExporter\VarExporter;
|
||||
|
||||
/**
|
||||
* @author Piotr Stankowski <git@trakos.pl>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @author Rob Frawley 2nd <rmf@src.run>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
trait PhpFilesTrait
|
||||
{
|
||||
use FilesystemCommonTrait {
|
||||
doClear as private doCommonClear;
|
||||
doDelete as private doCommonDelete;
|
||||
}
|
||||
|
||||
private $includeHandler;
|
||||
private $appendOnly;
|
||||
private $values = [];
|
||||
private $files = [];
|
||||
|
||||
private static $startTime;
|
||||
private static $valuesCache = [];
|
||||
|
||||
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($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, \is_object($value) ? \get_class($value) : 'array'), 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, \gettype($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($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($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;
|
||||
}
|
||||
}
|
||||
43
vendor/symfony/cache/Traits/ProxyTrait.php
vendored
Normal file
43
vendor/symfony/cache/Traits/ProxyTrait.php
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
53
vendor/symfony/cache/Traits/RedisClusterNodeProxy.php
vendored
Normal file
53
vendor/symfony/cache/Traits/RedisClusterNodeProxy.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 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);
|
||||
}
|
||||
}
|
||||
63
vendor/symfony/cache/Traits/RedisClusterProxy.php
vendored
Normal file
63
vendor/symfony/cache/Traits/RedisClusterProxy.php
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
<?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 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($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);
|
||||
}
|
||||
}
|
||||
65
vendor/symfony/cache/Traits/RedisProxy.php
vendored
Normal file
65
vendor/symfony/cache/Traits/RedisProxy.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?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 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($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);
|
||||
}
|
||||
}
|
||||
561
vendor/symfony/cache/Traits/RedisTrait.php
vendored
Normal file
561
vendor/symfony/cache/Traits/RedisTrait.php
vendored
Normal file
@@ -0,0 +1,561 @@
|
||||
<?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 Symfony\Component\Cache\Traits;
|
||||
|
||||
use Predis\Connection\Aggregate\ClusterInterface;
|
||||
use Predis\Connection\Aggregate\RedisCluster;
|
||||
use Predis\Connection\Aggregate\ReplicationInterface;
|
||||
use Predis\Response\ErrorInterface;
|
||||
use Predis\Response\Status;
|
||||
use Symfony\Component\Cache\Exception\CacheException;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
|
||||
use 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, // see https://php.net/context.ssl
|
||||
];
|
||||
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 \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__, \is_object($redis) ? \get_class($redis) : \gettype($redis)));
|
||||
}
|
||||
|
||||
if ($redis instanceof \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 string $dsn
|
||||
* @param array $options See self::$defaultConnectionOptions
|
||||
*
|
||||
* @throws InvalidArgumentException when the DSN is invalid
|
||||
*
|
||||
* @return \Redis|\RedisCluster|RedisClusterProxy|RedisProxy|\Predis\ClientInterface According to the "class" option
|
||||
*/
|
||||
public static function createConnection($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(\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];
|
||||
$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(\Predis\Client::class)) {
|
||||
throw new CacheException(sprintf('Redis Sentinel support requires the "predis/predis" package: "%s".', $dsn));
|
||||
}
|
||||
|
||||
if (null === $params['class'] && !isset($params['redis_sentinel']) && \extension_loaded('redis')) {
|
||||
$class = $params['redis_cluster'] ? \RedisCluster::class : (1 < \count($hosts) ? \RedisArray::class : \Redis::class);
|
||||
} else {
|
||||
$class = $params['class'] ?? \Predis\Client::class;
|
||||
}
|
||||
|
||||
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) {
|
||||
$host = $hosts[0]['host'] ?? $hosts[0]['path'];
|
||||
$port = $hosts[0]['port'] ?? 0;
|
||||
|
||||
if (isset($hosts[0]['host']) && $tls) {
|
||||
$host = 'tls://'.$host;
|
||||
}
|
||||
|
||||
try {
|
||||
@$redis->{$connect}($host, $port, $params['timeout'], (string) $params['persistent_id'], $params['retry_interval'], $params['read_timeout'], ...\defined('Redis::SCAN_PREFIX') ? [['stream' => $params['ssl'] ?? null]] : []);
|
||||
|
||||
set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
|
||||
$isConnected = $redis->isConnected();
|
||||
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, \Predis\ClientInterface::class, true)) {
|
||||
if ($params['redis_cluster']) {
|
||||
$params['cluster'] = 'redis';
|
||||
if (isset($params['redis_sentinel'])) {
|
||||
throw new InvalidArgumentException(sprintf('Cannot use both "redis_cluster" and "redis_sentinel" at the same time: "%s".', $dsn));
|
||||
}
|
||||
} 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 (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, array_diff_key(self::$defaultConnectionOptions, ['ssl' => null])));
|
||||
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 \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($id)
|
||||
{
|
||||
return (bool) $this->redis->exists($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function doClear($namespace)
|
||||
{
|
||||
if ($this->redis instanceof \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 \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 \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.
|
||||
$args = $this->redis instanceof \Predis\ClientInterface ? [0, $pattern] : [[$pattern], 0];
|
||||
$cleared = $host->eval("local keys=redis.call('KEYS',ARGV[1]) for i=1,#keys,5000 do redis.call('DEL',unpack(keys,i,math.min(i+4999,#keys))) end return 1", $args[0], $args[1]) && $cleared;
|
||||
continue;
|
||||
}
|
||||
|
||||
$cursor = null;
|
||||
do {
|
||||
$keys = $host instanceof \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 \Predis\ClientInterface && $this->redis->getConnection() instanceof ClusterInterface) {
|
||||
$this->pipeline(function () use ($ids) {
|
||||
foreach ($ids as $id) {
|
||||
yield 'del' => [$id];
|
||||
}
|
||||
})->rewind();
|
||||
} else {
|
||||
$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, $redis = null): \Generator
|
||||
{
|
||||
$ids = [];
|
||||
$redis = $redis ?? $this->redis;
|
||||
|
||||
if ($redis instanceof RedisClusterProxy || $redis instanceof \RedisCluster || ($redis instanceof \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 \Predis\ClientInterface ? $args[2] : $args[1][0]) : $args[0];
|
||||
}
|
||||
} elseif ($redis instanceof \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 \Predis\ClientInterface && 'eval' === $command && $redis->getLastError()) {
|
||||
$e = new \RedisException($redis->getLastError());
|
||||
$results = array_map(function ($v) use ($e) { return false === $v ? $e : $v; }, $results);
|
||||
}
|
||||
|
||||
foreach ($ids as $k => $id) {
|
||||
yield $id => $results[$k];
|
||||
}
|
||||
}
|
||||
|
||||
private function getHosts(): array
|
||||
{
|
||||
$hosts = [$this->redis];
|
||||
if ($this->redis instanceof \Predis\ClientInterface) {
|
||||
$connection = $this->redis->getConnection();
|
||||
if ($connection instanceof ClusterInterface && $connection instanceof \Traversable) {
|
||||
$hosts = [];
|
||||
foreach ($connection as $c) {
|
||||
$hosts[] = new \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;
|
||||
}
|
||||
}
|
||||
58
vendor/symfony/cache/composer.json
vendored
Normal file
58
vendor/symfony/cache/composer.json
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
{
|
||||
"name": "symfony/cache",
|
||||
"type": "library",
|
||||
"description": "Provides an extended PSR-6, PSR-16 (and tags) implementation",
|
||||
"keywords": ["caching", "psr6"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"provide": {
|
||||
"psr/cache-implementation": "1.0|2.0",
|
||||
"psr/simple-cache-implementation": "1.0|2.0",
|
||||
"symfony/cache-implementation": "1.0|2.0"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1.3",
|
||||
"psr/cache": "^1.0|^2.0",
|
||||
"psr/log": "^1|^2|^3",
|
||||
"symfony/cache-contracts": "^1.1.7|^2",
|
||||
"symfony/polyfill-php73": "^1.9",
|
||||
"symfony/polyfill-php80": "^1.16",
|
||||
"symfony/service-contracts": "^1.1|^2",
|
||||
"symfony/var-exporter": "^4.2|^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cache/integration-tests": "dev-master",
|
||||
"doctrine/cache": "^1.6|^2.0",
|
||||
"doctrine/dbal": "^2.7|^3.0",
|
||||
"predis/predis": "^1.1",
|
||||
"psr/simple-cache": "^1.0|^2.0",
|
||||
"symfony/config": "^4.2|^5.0",
|
||||
"symfony/dependency-injection": "^3.4|^4.1|^5.0",
|
||||
"symfony/filesystem": "^4.4|^5.0",
|
||||
"symfony/http-kernel": "^4.4",
|
||||
"symfony/var-dumper": "^4.4|^5.0"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/dbal": "<2.7",
|
||||
"symfony/dependency-injection": "<3.4",
|
||||
"symfony/http-kernel": "<4.4|>=5.0",
|
||||
"symfony/var-dumper": "<4.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Component\\Cache\\": "" },
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev"
|
||||
}
|
||||
3
vendor/symfony/event-dispatcher-contracts/.gitignore
vendored
Normal file
3
vendor/symfony/event-dispatcher-contracts/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
vendor/
|
||||
composer.lock
|
||||
phpunit.xml
|
||||
96
vendor/symfony/event-dispatcher-contracts/Event.php
vendored
Normal file
96
vendor/symfony/event-dispatcher-contracts/Event.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?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 Symfony\Contracts\EventDispatcher;
|
||||
|
||||
use Psr\EventDispatcher\StoppableEventInterface;
|
||||
|
||||
if (interface_exists(StoppableEventInterface::class)) {
|
||||
/**
|
||||
* Event is the base class for classes containing event data.
|
||||
*
|
||||
* This class contains no event data. It is used by events that do not pass
|
||||
* state information to an event handler when an event is raised.
|
||||
*
|
||||
* You can call the method stopPropagation() to abort the execution of
|
||||
* further listeners in your event listener.
|
||||
*
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class Event implements StoppableEventInterface
|
||||
{
|
||||
private $propagationStopped = false;
|
||||
|
||||
/**
|
||||
* Returns whether further event listeners should be triggered.
|
||||
*/
|
||||
public function isPropagationStopped(): bool
|
||||
{
|
||||
return $this->propagationStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the propagation of the event to further event listeners.
|
||||
*
|
||||
* If multiple event listeners are connected to the same event, no
|
||||
* further event listener will be triggered once any trigger calls
|
||||
* stopPropagation().
|
||||
*/
|
||||
public function stopPropagation(): void
|
||||
{
|
||||
$this->propagationStopped = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Event is the base class for classes containing event data.
|
||||
*
|
||||
* This class contains no event data. It is used by events that do not pass
|
||||
* state information to an event handler when an event is raised.
|
||||
*
|
||||
* You can call the method stopPropagation() to abort the execution of
|
||||
* further listeners in your event listener.
|
||||
*
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
private $propagationStopped = false;
|
||||
|
||||
/**
|
||||
* Returns whether further event listeners should be triggered.
|
||||
*/
|
||||
public function isPropagationStopped(): bool
|
||||
{
|
||||
return $this->propagationStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the propagation of the event to further event listeners.
|
||||
*
|
||||
* If multiple event listeners are connected to the same event, no
|
||||
* further event listener will be triggered once any trigger calls
|
||||
* stopPropagation().
|
||||
*/
|
||||
public function stopPropagation(): void
|
||||
{
|
||||
$this->propagationStopped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php
vendored
Normal file
58
vendor/symfony/event-dispatcher-contracts/EventDispatcherInterface.php
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
<?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 Symfony\Contracts\EventDispatcher;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface as PsrEventDispatcherInterface;
|
||||
|
||||
if (interface_exists(PsrEventDispatcherInterface::class)) {
|
||||
/**
|
||||
* Allows providing hooks on domain-specific lifecycles by dispatching events.
|
||||
*/
|
||||
interface EventDispatcherInterface extends PsrEventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Dispatches an event to all registered listeners.
|
||||
*
|
||||
* For BC with Symfony 4, the $eventName argument is not declared explicitly on the
|
||||
* signature of the method. Implementations that are not bound by this BC constraint
|
||||
* MUST declare it explicitly, as allowed by PHP.
|
||||
*
|
||||
* @param object $event The event to pass to the event handlers/listeners
|
||||
* @param string|null $eventName The name of the event to dispatch. If not supplied,
|
||||
* the class of $event should be used instead.
|
||||
*
|
||||
* @return object The passed $event MUST be returned
|
||||
*/
|
||||
public function dispatch($event/*, string $eventName = null*/);
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* Allows providing hooks on domain-specific lifecycles by dispatching events.
|
||||
*/
|
||||
interface EventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Dispatches an event to all registered listeners.
|
||||
*
|
||||
* For BC with Symfony 4, the $eventName argument is not declared explicitly on the
|
||||
* signature of the method. Implementations that are not bound by this BC constraint
|
||||
* MUST declare it explicitly, as allowed by PHP.
|
||||
*
|
||||
* @param object $event The event to pass to the event handlers/listeners
|
||||
* @param string|null $eventName The name of the event to dispatch. If not supplied,
|
||||
* the class of $event should be used instead.
|
||||
*
|
||||
* @return object The passed $event MUST be returned
|
||||
*/
|
||||
public function dispatch($event/*, string $eventName = null*/);
|
||||
}
|
||||
}
|
||||
19
vendor/symfony/event-dispatcher-contracts/LICENSE
vendored
Normal file
19
vendor/symfony/event-dispatcher-contracts/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2018-2022 Fabien Potencier
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
9
vendor/symfony/event-dispatcher-contracts/README.md
vendored
Normal file
9
vendor/symfony/event-dispatcher-contracts/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
Symfony EventDispatcher Contracts
|
||||
=================================
|
||||
|
||||
A set of abstractions extracted out of the Symfony components.
|
||||
|
||||
Can be used to build on semantics that the Symfony components proved useful - and
|
||||
that already have battle tested implementations.
|
||||
|
||||
See https://github.com/symfony/contracts/blob/main/README.md for more information.
|
||||
38
vendor/symfony/event-dispatcher-contracts/composer.json
vendored
Normal file
38
vendor/symfony/event-dispatcher-contracts/composer.json
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "symfony/event-dispatcher-contracts",
|
||||
"type": "library",
|
||||
"description": "Generic abstractions related to dispatching event",
|
||||
"keywords": ["abstractions", "contracts", "decoupling", "interfaces", "interoperability", "standards"],
|
||||
"homepage": "https://symfony.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1.3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/event-dispatcher": "",
|
||||
"symfony/event-dispatcher-implementation": ""
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Symfony\\Contracts\\EventDispatcher\\": "" }
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.1-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
}
|
||||
}
|
||||
67
vendor/symfony/event-dispatcher/CHANGELOG.md
vendored
Normal file
67
vendor/symfony/event-dispatcher/CHANGELOG.md
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
4.4.0
|
||||
-----
|
||||
|
||||
* `AddEventAliasesPass` has been added, allowing applications and bundles to extend the event alias mapping used by `RegisterListenersPass`.
|
||||
* Made the `event` attribute of the `kernel.event_listener` tag optional for FQCN events.
|
||||
|
||||
4.3.0
|
||||
-----
|
||||
|
||||
* The signature of the `EventDispatcherInterface::dispatch()` method should be updated to `dispatch($event, string $eventName = null)`, not doing so is deprecated
|
||||
* deprecated the `Event` class, use `Symfony\Contracts\EventDispatcher\Event` instead
|
||||
|
||||
4.1.0
|
||||
-----
|
||||
|
||||
* added support for invokable event listeners tagged with `kernel.event_listener` by default
|
||||
* The `TraceableEventDispatcher::getOrphanedEvents()` method has been added.
|
||||
* The `TraceableEventDispatcherInterface` has been deprecated.
|
||||
|
||||
4.0.0
|
||||
-----
|
||||
|
||||
* removed the `ContainerAwareEventDispatcher` class
|
||||
* added the `reset()` method to the `TraceableEventDispatcherInterface`
|
||||
|
||||
3.4.0
|
||||
-----
|
||||
|
||||
* Implementing `TraceableEventDispatcherInterface` without the `reset()` method has been deprecated.
|
||||
|
||||
3.3.0
|
||||
-----
|
||||
|
||||
* The ContainerAwareEventDispatcher class has been deprecated. Use EventDispatcher with closure factories instead.
|
||||
|
||||
3.0.0
|
||||
-----
|
||||
|
||||
* The method `getListenerPriority($eventName, $listener)` has been added to the
|
||||
`EventDispatcherInterface`.
|
||||
* The methods `Event::setDispatcher()`, `Event::getDispatcher()`, `Event::setName()`
|
||||
and `Event::getName()` have been removed.
|
||||
The event dispatcher and the event name are passed to the listener call.
|
||||
|
||||
2.5.0
|
||||
-----
|
||||
|
||||
* added Debug\TraceableEventDispatcher (originally in HttpKernel)
|
||||
* changed Debug\TraceableEventDispatcherInterface to extend EventDispatcherInterface
|
||||
* added RegisterListenersPass (originally in HttpKernel)
|
||||
|
||||
2.1.0
|
||||
-----
|
||||
|
||||
* added TraceableEventDispatcherInterface
|
||||
* added ContainerAwareEventDispatcher
|
||||
* added a reference to the EventDispatcher on the Event
|
||||
* added a reference to the Event name on the event
|
||||
* added fluid interface to the dispatch() method which now returns the Event
|
||||
object
|
||||
* added GenericEvent event class
|
||||
* added the possibility for subscribers to subscribe several times for the
|
||||
same event
|
||||
* added ImmutableEventDispatcher
|
||||
407
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
vendored
Normal file
407
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
<?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 Symfony\Component\EventDispatcher\Debug;
|
||||
|
||||
use Psr\EventDispatcher\StoppableEventInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
|
||||
use Symfony\Component\EventDispatcher\LegacyEventProxy;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
|
||||
|
||||
/**
|
||||
* Collects some data about event listeners.
|
||||
*
|
||||
* This event dispatcher delegates the dispatching to another one.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
{
|
||||
protected $logger;
|
||||
protected $stopwatch;
|
||||
|
||||
private $callStack;
|
||||
private $dispatcher;
|
||||
private $wrappedListeners;
|
||||
private $orphanedEvents;
|
||||
private $requestStack;
|
||||
private $currentRequestHash = '';
|
||||
|
||||
public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null, RequestStack $requestStack = null)
|
||||
{
|
||||
$this->dispatcher = LegacyEventDispatcherProxy::decorate($dispatcher);
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->logger = $logger;
|
||||
$this->wrappedListeners = [];
|
||||
$this->orphanedEvents = [];
|
||||
$this->requestStack = $requestStack;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addListener($eventName, $listener, $priority = 0)
|
||||
{
|
||||
$this->dispatcher->addListener($eventName, $listener, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSubscriber(EventSubscriberInterface $subscriber)
|
||||
{
|
||||
$this->dispatcher->addSubscriber($subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeListener($eventName, $listener)
|
||||
{
|
||||
if (isset($this->wrappedListeners[$eventName])) {
|
||||
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
|
||||
if ($wrappedListener->getWrappedListener() === $listener) {
|
||||
$listener = $wrappedListener;
|
||||
unset($this->wrappedListeners[$eventName][$index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dispatcher->removeListener($eventName, $listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeSubscriber(EventSubscriberInterface $subscriber)
|
||||
{
|
||||
return $this->dispatcher->removeSubscriber($subscriber);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListeners($eventName = null)
|
||||
{
|
||||
return $this->dispatcher->getListeners($eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListenerPriority($eventName, $listener)
|
||||
{
|
||||
// we might have wrapped listeners for the event (if called while dispatching)
|
||||
// in that case get the priority by wrapper
|
||||
if (isset($this->wrappedListeners[$eventName])) {
|
||||
foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
|
||||
if ($wrappedListener->getWrappedListener() === $listener) {
|
||||
return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dispatcher->getListenerPriority($eventName, $listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasListeners($eventName = null)
|
||||
{
|
||||
return $this->dispatcher->hasListeners($eventName);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string|null $eventName
|
||||
*/
|
||||
public function dispatch($event/*, string $eventName = null*/)
|
||||
{
|
||||
if (null === $this->callStack) {
|
||||
$this->callStack = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
$currentRequestHash = $this->currentRequestHash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
|
||||
$eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
|
||||
|
||||
if (\is_object($event)) {
|
||||
$eventName = $eventName ?? \get_class($event);
|
||||
} else {
|
||||
@trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as first argument is deprecated since Symfony 4.3, pass it second and provide the event object first instead.', EventDispatcherInterface::class), \E_USER_DEPRECATED);
|
||||
$swap = $event;
|
||||
$event = $eventName ?? new Event();
|
||||
$eventName = $swap;
|
||||
|
||||
if (!$event instanceof Event) {
|
||||
throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an instance of "%s", "%s" given.', EventDispatcherInterface::class, Event::class, \is_object($event) ? \get_class($event) : \gettype($event)));
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $this->logger && ($event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface) && $event->isPropagationStopped()) {
|
||||
$this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
|
||||
}
|
||||
|
||||
$this->preProcess($eventName);
|
||||
try {
|
||||
$this->beforeDispatch($eventName, $event);
|
||||
try {
|
||||
$e = $this->stopwatch->start($eventName, 'section');
|
||||
try {
|
||||
$this->dispatcher->dispatch($event, $eventName);
|
||||
} finally {
|
||||
if ($e->isStarted()) {
|
||||
$e->stop();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
$this->afterDispatch($eventName, $event);
|
||||
}
|
||||
} finally {
|
||||
$this->currentRequestHash = $currentRequestHash;
|
||||
$this->postProcess($eventName);
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param Request|null $request The request to get listeners for
|
||||
*/
|
||||
public function getCalledListeners(/* Request $request = null */)
|
||||
{
|
||||
if (null === $this->callStack) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$hash = 1 <= \func_num_args() && null !== ($request = func_get_arg(0)) ? spl_object_hash($request) : null;
|
||||
$called = [];
|
||||
foreach ($this->callStack as $listener) {
|
||||
[$eventName, $requestHash] = $this->callStack->getInfo();
|
||||
if (null === $hash || $hash === $requestHash) {
|
||||
$called[] = $listener->getInfo($eventName);
|
||||
}
|
||||
}
|
||||
|
||||
return $called;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param Request|null $request The request to get listeners for
|
||||
*/
|
||||
public function getNotCalledListeners(/* Request $request = null */)
|
||||
{
|
||||
try {
|
||||
$allListeners = $this->getListeners();
|
||||
} catch (\Exception $e) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->info('An exception was thrown while getting the uncalled listeners.', ['exception' => $e]);
|
||||
}
|
||||
|
||||
// unable to retrieve the uncalled listeners
|
||||
return [];
|
||||
}
|
||||
|
||||
$hash = 1 <= \func_num_args() && null !== ($request = func_get_arg(0)) ? spl_object_hash($request) : null;
|
||||
$calledListeners = [];
|
||||
|
||||
if (null !== $this->callStack) {
|
||||
foreach ($this->callStack as $calledListener) {
|
||||
[, $requestHash] = $this->callStack->getInfo();
|
||||
|
||||
if (null === $hash || $hash === $requestHash) {
|
||||
$calledListeners[] = $calledListener->getWrappedListener();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$notCalled = [];
|
||||
foreach ($allListeners as $eventName => $listeners) {
|
||||
foreach ($listeners as $listener) {
|
||||
if (!\in_array($listener, $calledListeners, true)) {
|
||||
if (!$listener instanceof WrappedListener) {
|
||||
$listener = new WrappedListener($listener, null, $this->stopwatch, $this);
|
||||
}
|
||||
$notCalled[] = $listener->getInfo($eventName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uasort($notCalled, [$this, 'sortNotCalledListeners']);
|
||||
|
||||
return $notCalled;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request|null $request The request to get orphaned events for
|
||||
*/
|
||||
public function getOrphanedEvents(/* Request $request = null */): array
|
||||
{
|
||||
if (1 <= \func_num_args() && null !== $request = func_get_arg(0)) {
|
||||
return $this->orphanedEvents[spl_object_hash($request)] ?? [];
|
||||
}
|
||||
|
||||
if (!$this->orphanedEvents) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_merge(...array_values($this->orphanedEvents));
|
||||
}
|
||||
|
||||
public function reset()
|
||||
{
|
||||
$this->callStack = null;
|
||||
$this->orphanedEvents = [];
|
||||
$this->currentRequestHash = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxies all method calls to the original event dispatcher.
|
||||
*
|
||||
* @param string $method The method name
|
||||
* @param array $arguments The method arguments
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $arguments)
|
||||
{
|
||||
return $this->dispatcher->{$method}(...$arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before dispatching the event.
|
||||
*
|
||||
* @param object $event
|
||||
*/
|
||||
protected function beforeDispatch(string $eventName, $event)
|
||||
{
|
||||
$this->preDispatch($eventName, $event instanceof Event ? $event : new LegacyEventProxy($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after dispatching the event.
|
||||
*
|
||||
* @param object $event
|
||||
*/
|
||||
protected function afterDispatch(string $eventName, $event)
|
||||
{
|
||||
$this->postDispatch($eventName, $event instanceof Event ? $event : new LegacyEventProxy($event));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, will be removed in 5.0, use beforeDispatch instead
|
||||
*/
|
||||
protected function preDispatch($eventName, Event $event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, will be removed in 5.0, use afterDispatch instead
|
||||
*/
|
||||
protected function postDispatch($eventName, Event $event)
|
||||
{
|
||||
}
|
||||
|
||||
private function preProcess(string $eventName)
|
||||
{
|
||||
if (!$this->dispatcher->hasListeners($eventName)) {
|
||||
$this->orphanedEvents[$this->currentRequestHash][] = $eventName;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
|
||||
$priority = $this->getListenerPriority($eventName, $listener);
|
||||
$wrappedListener = new WrappedListener($listener instanceof WrappedListener ? $listener->getWrappedListener() : $listener, null, $this->stopwatch, $this);
|
||||
$this->wrappedListeners[$eventName][] = $wrappedListener;
|
||||
$this->dispatcher->removeListener($eventName, $listener);
|
||||
$this->dispatcher->addListener($eventName, $wrappedListener, $priority);
|
||||
$this->callStack->attach($wrappedListener, [$eventName, $this->currentRequestHash]);
|
||||
}
|
||||
}
|
||||
|
||||
private function postProcess(string $eventName)
|
||||
{
|
||||
unset($this->wrappedListeners[$eventName]);
|
||||
$skipped = false;
|
||||
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
|
||||
if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
|
||||
continue;
|
||||
}
|
||||
// Unwrap listener
|
||||
$priority = $this->getListenerPriority($eventName, $listener);
|
||||
$this->dispatcher->removeListener($eventName, $listener);
|
||||
$this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);
|
||||
|
||||
if (null !== $this->logger) {
|
||||
$context = ['event' => $eventName, 'listener' => $listener->getPretty()];
|
||||
}
|
||||
|
||||
if ($listener->wasCalled()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Notified event "{event}" to listener "{listener}".', $context);
|
||||
}
|
||||
} else {
|
||||
$this->callStack->detach($listener);
|
||||
}
|
||||
|
||||
if (null !== $this->logger && $skipped) {
|
||||
$this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
|
||||
}
|
||||
|
||||
if ($listener->stoppedPropagation()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context);
|
||||
}
|
||||
|
||||
$skipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function sortNotCalledListeners(array $a, array $b)
|
||||
{
|
||||
if (0 !== $cmp = strcmp($a['event'], $b['event'])) {
|
||||
return $cmp;
|
||||
}
|
||||
|
||||
if (\is_int($a['priority']) && !\is_int($b['priority'])) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!\is_int($a['priority']) && \is_int($b['priority'])) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($a['priority'] === $b['priority']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($a['priority'] > $b['priority']) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
42
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.php
vendored
Normal file
42
vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcherInterface.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 Symfony\Component\EventDispatcher\Debug;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.1
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface TraceableEventDispatcherInterface extends EventDispatcherInterface, ResetInterface
|
||||
{
|
||||
/**
|
||||
* Gets the called listeners.
|
||||
*
|
||||
* @param Request|null $request The request to get listeners for
|
||||
*
|
||||
* @return array An array of called listeners
|
||||
*/
|
||||
public function getCalledListeners(/* Request $request = null */);
|
||||
|
||||
/**
|
||||
* Gets the not called listeners.
|
||||
*
|
||||
* @param Request|null $request The request to get listeners for
|
||||
*
|
||||
* @return array An array of not called listeners
|
||||
*/
|
||||
public function getNotCalledListeners(/* Request $request = null */);
|
||||
}
|
||||
136
vendor/symfony/event-dispatcher/Debug/WrappedListener.php
vendored
Normal file
136
vendor/symfony/event-dispatcher/Debug/WrappedListener.php
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
<?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 Symfony\Component\EventDispatcher\Debug;
|
||||
|
||||
use Psr\EventDispatcher\StoppableEventInterface;
|
||||
use Symfony\Component\EventDispatcher\Event;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\LegacyEventProxy;
|
||||
use Symfony\Component\Stopwatch\Stopwatch;
|
||||
use Symfony\Component\VarDumper\Caster\ClassStub;
|
||||
use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final since Symfony 4.3: the "Event" type-hint on __invoke() will be replaced by "object" in 5.0
|
||||
*/
|
||||
class WrappedListener
|
||||
{
|
||||
private $listener;
|
||||
private $optimizedListener;
|
||||
private $name;
|
||||
private $called;
|
||||
private $stoppedPropagation;
|
||||
private $stopwatch;
|
||||
private $dispatcher;
|
||||
private $pretty;
|
||||
private $stub;
|
||||
private $priority;
|
||||
private static $hasClassStub;
|
||||
|
||||
public function __construct($listener, ?string $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null)
|
||||
{
|
||||
$this->listener = $listener;
|
||||
$this->optimizedListener = $listener instanceof \Closure ? $listener : (\is_callable($listener) ? \Closure::fromCallable($listener) : null);
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->called = false;
|
||||
$this->stoppedPropagation = false;
|
||||
|
||||
if (\is_array($listener)) {
|
||||
$this->name = \is_object($listener[0]) ? \get_class($listener[0]) : $listener[0];
|
||||
$this->pretty = $this->name.'::'.$listener[1];
|
||||
} elseif ($listener instanceof \Closure) {
|
||||
$r = new \ReflectionFunction($listener);
|
||||
if (str_contains($r->name, '{closure}')) {
|
||||
$this->pretty = $this->name = 'closure';
|
||||
} elseif ($class = $r->getClosureScopeClass()) {
|
||||
$this->name = $class->name;
|
||||
$this->pretty = $this->name.'::'.$r->name;
|
||||
} else {
|
||||
$this->pretty = $this->name = $r->name;
|
||||
}
|
||||
} elseif (\is_string($listener)) {
|
||||
$this->pretty = $this->name = $listener;
|
||||
} else {
|
||||
$this->name = \get_class($listener);
|
||||
$this->pretty = $this->name.'::__invoke';
|
||||
}
|
||||
|
||||
if (null !== $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
if (null === self::$hasClassStub) {
|
||||
self::$hasClassStub = class_exists(ClassStub::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function getWrappedListener()
|
||||
{
|
||||
return $this->listener;
|
||||
}
|
||||
|
||||
public function wasCalled()
|
||||
{
|
||||
return $this->called;
|
||||
}
|
||||
|
||||
public function stoppedPropagation()
|
||||
{
|
||||
return $this->stoppedPropagation;
|
||||
}
|
||||
|
||||
public function getPretty()
|
||||
{
|
||||
return $this->pretty;
|
||||
}
|
||||
|
||||
public function getInfo($eventName)
|
||||
{
|
||||
if (null === $this->stub) {
|
||||
$this->stub = self::$hasClassStub ? new ClassStub($this->pretty.'()', $this->listener) : $this->pretty.'()';
|
||||
}
|
||||
|
||||
return [
|
||||
'event' => $eventName,
|
||||
'priority' => null !== $this->priority ? $this->priority : (null !== $this->dispatcher ? $this->dispatcher->getListenerPriority($eventName, $this->listener) : null),
|
||||
'pretty' => $this->pretty,
|
||||
'stub' => $this->stub,
|
||||
];
|
||||
}
|
||||
|
||||
public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
|
||||
{
|
||||
if ($event instanceof LegacyEventProxy) {
|
||||
$event = $event->getEvent();
|
||||
}
|
||||
|
||||
$dispatcher = $this->dispatcher ?: $dispatcher;
|
||||
|
||||
$this->called = true;
|
||||
$this->priority = $dispatcher->getListenerPriority($eventName, $this->listener);
|
||||
|
||||
$e = $this->stopwatch->start($this->name, 'event_listener');
|
||||
|
||||
($this->optimizedListener ?? $this->listener)($event, $eventName, $dispatcher);
|
||||
|
||||
if ($e->isStarted()) {
|
||||
$e->stop();
|
||||
}
|
||||
|
||||
if (($event instanceof Event || $event instanceof ContractsEvent || $event instanceof StoppableEventInterface) && $event->isPropagationStopped()) {
|
||||
$this->stoppedPropagation = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
vendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.php
vendored
Normal file
42
vendor/symfony/event-dispatcher/DependencyInjection/AddEventAliasesPass.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 Symfony\Component\EventDispatcher\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
|
||||
/**
|
||||
* This pass allows bundles to extend the list of event aliases.
|
||||
*
|
||||
* @author Alexander M. Turek <me@derrabus.de>
|
||||
*/
|
||||
class AddEventAliasesPass implements CompilerPassInterface
|
||||
{
|
||||
private $eventAliases;
|
||||
private $eventAliasesParameter;
|
||||
|
||||
public function __construct(array $eventAliases, string $eventAliasesParameter = 'event_dispatcher.event_aliases')
|
||||
{
|
||||
$this->eventAliases = $eventAliases;
|
||||
$this->eventAliasesParameter = $eventAliasesParameter;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container): void
|
||||
{
|
||||
$eventAliases = $container->hasParameter($this->eventAliasesParameter) ? $container->getParameter($this->eventAliasesParameter) : [];
|
||||
|
||||
$container->setParameter(
|
||||
$this->eventAliasesParameter,
|
||||
array_merge($eventAliases, $this->eventAliases)
|
||||
);
|
||||
}
|
||||
}
|
||||
178
vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
vendored
Normal file
178
vendor/symfony/event-dispatcher/DependencyInjection/RegisterListenersPass.php
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
<?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 Symfony\Component\EventDispatcher\DependencyInjection;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
use Symfony\Component\EventDispatcher\Event as LegacyEvent;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
/**
|
||||
* Compiler pass to register tagged services for an event dispatcher.
|
||||
*/
|
||||
class RegisterListenersPass implements CompilerPassInterface
|
||||
{
|
||||
protected $dispatcherService;
|
||||
protected $listenerTag;
|
||||
protected $subscriberTag;
|
||||
protected $eventAliasesParameter;
|
||||
|
||||
private $hotPathEvents = [];
|
||||
private $hotPathTagName;
|
||||
|
||||
public function __construct(string $dispatcherService = 'event_dispatcher', string $listenerTag = 'kernel.event_listener', string $subscriberTag = 'kernel.event_subscriber', string $eventAliasesParameter = 'event_dispatcher.event_aliases')
|
||||
{
|
||||
$this->dispatcherService = $dispatcherService;
|
||||
$this->listenerTag = $listenerTag;
|
||||
$this->subscriberTag = $subscriberTag;
|
||||
$this->eventAliasesParameter = $eventAliasesParameter;
|
||||
}
|
||||
|
||||
public function setHotPathEvents(array $hotPathEvents, $tagName = 'container.hot_path')
|
||||
{
|
||||
$this->hotPathEvents = array_flip($hotPathEvents);
|
||||
$this->hotPathTagName = $tagName;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function process(ContainerBuilder $container)
|
||||
{
|
||||
if (!$container->hasDefinition($this->dispatcherService) && !$container->hasAlias($this->dispatcherService)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aliases = [];
|
||||
|
||||
if ($container->hasParameter($this->eventAliasesParameter)) {
|
||||
$aliases = $container->getParameter($this->eventAliasesParameter);
|
||||
}
|
||||
|
||||
$definition = $container->findDefinition($this->dispatcherService);
|
||||
|
||||
foreach ($container->findTaggedServiceIds($this->listenerTag, true) as $id => $events) {
|
||||
foreach ($events as $event) {
|
||||
$priority = $event['priority'] ?? 0;
|
||||
|
||||
if (!isset($event['event'])) {
|
||||
if ($container->getDefinition($id)->hasTag($this->subscriberTag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$event['method'] = $event['method'] ?? '__invoke';
|
||||
$event['event'] = $this->getEventFromTypeDeclaration($container, $id, $event['method']);
|
||||
}
|
||||
|
||||
$event['event'] = $aliases[$event['event']] ?? $event['event'];
|
||||
|
||||
if (!isset($event['method'])) {
|
||||
$event['method'] = 'on'.preg_replace_callback([
|
||||
'/(?<=\b|_)[a-z]/i',
|
||||
'/[^a-z0-9]/i',
|
||||
], function ($matches) { return strtoupper($matches[0]); }, $event['event']);
|
||||
$event['method'] = preg_replace('/[^a-z0-9]/i', '', $event['method']);
|
||||
|
||||
if (null !== ($class = $container->getDefinition($id)->getClass()) && ($r = $container->getReflectionClass($class, false)) && !$r->hasMethod($event['method']) && $r->hasMethod('__invoke')) {
|
||||
$event['method'] = '__invoke';
|
||||
}
|
||||
}
|
||||
|
||||
$definition->addMethodCall('addListener', [$event['event'], [new ServiceClosureArgument(new Reference($id)), $event['method']], $priority]);
|
||||
|
||||
if (isset($this->hotPathEvents[$event['event']])) {
|
||||
$container->getDefinition($id)->addTag($this->hotPathTagName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$extractingDispatcher = new ExtractingEventDispatcher();
|
||||
|
||||
foreach ($container->findTaggedServiceIds($this->subscriberTag, true) as $id => $attributes) {
|
||||
$def = $container->getDefinition($id);
|
||||
|
||||
// We must assume that the class value has been correctly filled, even if the service is created by a factory
|
||||
$class = $def->getClass();
|
||||
|
||||
if (!$r = $container->getReflectionClass($class)) {
|
||||
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id));
|
||||
}
|
||||
if (!$r->isSubclassOf(EventSubscriberInterface::class)) {
|
||||
throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, EventSubscriberInterface::class));
|
||||
}
|
||||
$class = $r->name;
|
||||
|
||||
ExtractingEventDispatcher::$aliases = $aliases;
|
||||
ExtractingEventDispatcher::$subscriber = $class;
|
||||
$extractingDispatcher->addSubscriber($extractingDispatcher);
|
||||
foreach ($extractingDispatcher->listeners as $args) {
|
||||
$args[1] = [new ServiceClosureArgument(new Reference($id)), $args[1]];
|
||||
$definition->addMethodCall('addListener', $args);
|
||||
|
||||
if (isset($this->hotPathEvents[$args[0]])) {
|
||||
$container->getDefinition($id)->addTag($this->hotPathTagName);
|
||||
}
|
||||
}
|
||||
$extractingDispatcher->listeners = [];
|
||||
ExtractingEventDispatcher::$aliases = [];
|
||||
}
|
||||
}
|
||||
|
||||
private function getEventFromTypeDeclaration(ContainerBuilder $container, string $id, string $method): string
|
||||
{
|
||||
if (
|
||||
null === ($class = $container->getDefinition($id)->getClass())
|
||||
|| !($r = $container->getReflectionClass($class, false))
|
||||
|| !$r->hasMethod($method)
|
||||
|| 1 > ($m = $r->getMethod($method))->getNumberOfParameters()
|
||||
|| !($type = $m->getParameters()[0]->getType()) instanceof \ReflectionNamedType
|
||||
|| $type->isBuiltin()
|
||||
|| Event::class === ($name = $type->getName())
|
||||
|| LegacyEvent::class === $name
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf('Service "%s" must define the "event" attribute on "%s" tags.', $id, $this->listenerTag));
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ExtractingEventDispatcher extends EventDispatcher implements EventSubscriberInterface
|
||||
{
|
||||
public $listeners = [];
|
||||
|
||||
public static $aliases = [];
|
||||
public static $subscriber;
|
||||
|
||||
public function addListener($eventName, $listener, $priority = 0)
|
||||
{
|
||||
$this->listeners[] = [$eventName, $listener[1], $priority];
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
$events = [];
|
||||
|
||||
foreach ([self::$subscriber, 'getSubscribedEvents']() as $eventName => $params) {
|
||||
$events[self::$aliases[$eventName] ?? $eventName] = $params;
|
||||
}
|
||||
|
||||
return $events;
|
||||
}
|
||||
}
|
||||
38
vendor/symfony/event-dispatcher/Event.php
vendored
Normal file
38
vendor/symfony/event-dispatcher/Event.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 Symfony\Component\EventDispatcher;
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
private $propagationStopped = false;
|
||||
|
||||
/**
|
||||
* @return bool Whether propagation was already stopped for this event
|
||||
*
|
||||
* @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
|
||||
*/
|
||||
public function isPropagationStopped()
|
||||
{
|
||||
return $this->propagationStopped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead
|
||||
*/
|
||||
public function stopPropagation()
|
||||
{
|
||||
$this->propagationStopped = true;
|
||||
}
|
||||
}
|
||||
314
vendor/symfony/event-dispatcher/EventDispatcher.php
vendored
Normal file
314
vendor/symfony/event-dispatcher/EventDispatcher.php
vendored
Normal file
@@ -0,0 +1,314 @@
|
||||
<?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 Symfony\Component\EventDispatcher;
|
||||
|
||||
use Psr\EventDispatcher\StoppableEventInterface;
|
||||
use Symfony\Component\EventDispatcher\Debug\WrappedListener;
|
||||
use Symfony\Contracts\EventDispatcher\Event as ContractsEvent;
|
||||
|
||||
/**
|
||||
* The EventDispatcherInterface is the central point of Symfony's event listener system.
|
||||
*
|
||||
* Listeners are registered on the manager and events are dispatched through the
|
||||
* manager.
|
||||
*
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Jordan Alliot <jordan.alliot@gmail.com>
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class EventDispatcher implements EventDispatcherInterface
|
||||
{
|
||||
private $listeners = [];
|
||||
private $sorted = [];
|
||||
private $optimized;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
if (__CLASS__ === static::class) {
|
||||
$this->optimized = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param string|null $eventName
|
||||
*/
|
||||
public function dispatch($event/*, string $eventName = null*/)
|
||||
{
|
||||
$eventName = 1 < \func_num_args() ? func_get_arg(1) : null;
|
||||
|
||||
if (\is_object($event)) {
|
||||
$eventName = $eventName ?? \get_class($event);
|
||||
} elseif (\is_string($event) && (null === $eventName || $eventName instanceof ContractsEvent || $eventName instanceof Event)) {
|
||||
@trigger_error(sprintf('Calling the "%s::dispatch()" method with the event name as the first argument is deprecated since Symfony 4.3, pass it as the second argument and provide the event object as the first argument instead.', EventDispatcherInterface::class), \E_USER_DEPRECATED);
|
||||
$swap = $event;
|
||||
$event = $eventName ?? new Event();
|
||||
$eventName = $swap;
|
||||
} else {
|
||||
throw new \TypeError(sprintf('Argument 1 passed to "%s::dispatch()" must be an object, "%s" given.', EventDispatcherInterface::class, \is_object($event) ? \get_class($event) : \gettype($event)));
|
||||
}
|
||||
|
||||
if (null !== $this->optimized && null !== $eventName) {
|
||||
$listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
|
||||
} else {
|
||||
$listeners = $this->getListeners($eventName);
|
||||
}
|
||||
|
||||
if ($listeners) {
|
||||
$this->callListeners($listeners, $eventName, $event);
|
||||
}
|
||||
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListeners($eventName = null)
|
||||
{
|
||||
if (null !== $eventName) {
|
||||
if (empty($this->listeners[$eventName])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!isset($this->sorted[$eventName])) {
|
||||
$this->sortListeners($eventName);
|
||||
}
|
||||
|
||||
return $this->sorted[$eventName];
|
||||
}
|
||||
|
||||
foreach ($this->listeners as $eventName => $eventListeners) {
|
||||
if (!isset($this->sorted[$eventName])) {
|
||||
$this->sortListeners($eventName);
|
||||
}
|
||||
}
|
||||
|
||||
return array_filter($this->sorted);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getListenerPriority($eventName, $listener)
|
||||
{
|
||||
if (empty($this->listeners[$eventName])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
|
||||
$listener[0] = $listener[0]();
|
||||
$listener[1] = $listener[1] ?? '__invoke';
|
||||
}
|
||||
|
||||
foreach ($this->listeners[$eventName] as $priority => &$listeners) {
|
||||
foreach ($listeners as &$v) {
|
||||
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
|
||||
$v[0] = $v[0]();
|
||||
$v[1] = $v[1] ?? '__invoke';
|
||||
}
|
||||
if ($v === $listener) {
|
||||
return $priority;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hasListeners($eventName = null)
|
||||
{
|
||||
if (null !== $eventName) {
|
||||
return !empty($this->listeners[$eventName]);
|
||||
}
|
||||
|
||||
foreach ($this->listeners as $eventListeners) {
|
||||
if ($eventListeners) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addListener($eventName, $listener, $priority = 0)
|
||||
{
|
||||
$this->listeners[$eventName][$priority][] = $listener;
|
||||
unset($this->sorted[$eventName], $this->optimized[$eventName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeListener($eventName, $listener)
|
||||
{
|
||||
if (empty($this->listeners[$eventName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
|
||||
$listener[0] = $listener[0]();
|
||||
$listener[1] = $listener[1] ?? '__invoke';
|
||||
}
|
||||
|
||||
foreach ($this->listeners[$eventName] as $priority => &$listeners) {
|
||||
foreach ($listeners as $k => &$v) {
|
||||
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
|
||||
$v[0] = $v[0]();
|
||||
$v[1] = $v[1] ?? '__invoke';
|
||||
}
|
||||
if ($v === $listener) {
|
||||
unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$listeners) {
|
||||
unset($this->listeners[$eventName][$priority]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function addSubscriber(EventSubscriberInterface $subscriber)
|
||||
{
|
||||
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
|
||||
if (\is_string($params)) {
|
||||
$this->addListener($eventName, [$subscriber, $params]);
|
||||
} elseif (\is_string($params[0])) {
|
||||
$this->addListener($eventName, [$subscriber, $params[0]], $params[1] ?? 0);
|
||||
} else {
|
||||
foreach ($params as $listener) {
|
||||
$this->addListener($eventName, [$subscriber, $listener[0]], $listener[1] ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function removeSubscriber(EventSubscriberInterface $subscriber)
|
||||
{
|
||||
foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
|
||||
if (\is_array($params) && \is_array($params[0])) {
|
||||
foreach ($params as $listener) {
|
||||
$this->removeListener($eventName, [$subscriber, $listener[0]]);
|
||||
}
|
||||
} else {
|
||||
$this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers the listeners of an event.
|
||||
*
|
||||
* This method can be overridden to add functionality that is executed
|
||||
* for each listener.
|
||||
*
|
||||
* @param callable[] $listeners The event listeners
|
||||
* @param string $eventName The name of the event to dispatch
|
||||
* @param object $event The event object to pass to the event handlers/listeners
|
||||
*/
|
||||
protected function callListeners(iterable $listeners, string $eventName, $event)
|
||||
{
|
||||
if ($event instanceof Event) {
|
||||
$this->doDispatch($listeners, $eventName, $event);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$stoppable = $event instanceof ContractsEvent || $event instanceof StoppableEventInterface;
|
||||
|
||||
foreach ($listeners as $listener) {
|
||||
if ($stoppable && $event->isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
// @deprecated: the ternary operator is part of a BC layer and should be removed in 5.0
|
||||
$listener($listener instanceof WrappedListener ? new LegacyEventProxy($event) : $event, $eventName, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Symfony 4.3, use callListeners() instead
|
||||
*/
|
||||
protected function doDispatch($listeners, $eventName, Event $event)
|
||||
{
|
||||
foreach ($listeners as $listener) {
|
||||
if ($event->isPropagationStopped()) {
|
||||
break;
|
||||
}
|
||||
$listener($event, $eventName, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the internal list of listeners for the given event by priority.
|
||||
*/
|
||||
private function sortListeners(string $eventName)
|
||||
{
|
||||
krsort($this->listeners[$eventName]);
|
||||
$this->sorted[$eventName] = [];
|
||||
|
||||
foreach ($this->listeners[$eventName] as &$listeners) {
|
||||
foreach ($listeners as $k => &$listener) {
|
||||
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
|
||||
$listener[0] = $listener[0]();
|
||||
$listener[1] = $listener[1] ?? '__invoke';
|
||||
}
|
||||
$this->sorted[$eventName][] = $listener;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimizes the internal list of listeners for the given event by priority.
|
||||
*/
|
||||
private function optimizeListeners(string $eventName): array
|
||||
{
|
||||
krsort($this->listeners[$eventName]);
|
||||
$this->optimized[$eventName] = [];
|
||||
|
||||
foreach ($this->listeners[$eventName] as &$listeners) {
|
||||
foreach ($listeners as &$listener) {
|
||||
$closure = &$this->optimized[$eventName][];
|
||||
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
|
||||
$closure = static function (...$args) use (&$listener, &$closure) {
|
||||
if ($listener[0] instanceof \Closure) {
|
||||
$listener[0] = $listener[0]();
|
||||
$listener[1] = $listener[1] ?? '__invoke';
|
||||
}
|
||||
($closure = \Closure::fromCallable($listener))(...$args);
|
||||
};
|
||||
} else {
|
||||
$closure = $listener instanceof \Closure || $listener instanceof WrappedListener ? $listener : \Closure::fromCallable($listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->optimized[$eventName];
|
||||
}
|
||||
}
|
||||
82
vendor/symfony/event-dispatcher/EventDispatcherInterface.php
vendored
Normal file
82
vendor/symfony/event-dispatcher/EventDispatcherInterface.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 Symfony\Component\EventDispatcher;
|
||||
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface as ContractsEventDispatcherInterface;
|
||||
|
||||
/**
|
||||
* The EventDispatcherInterface is the central point of Symfony's event listener system.
|
||||
* Listeners are registered on the manager and events are dispatched through the
|
||||
* manager.
|
||||
*
|
||||
* @author Bernhard Schussek <bschussek@gmail.com>
|
||||
*/
|
||||
interface EventDispatcherInterface extends ContractsEventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Adds an event listener that listens on the specified events.
|
||||
*
|
||||
* @param string $eventName The event to listen on
|
||||
* @param callable $listener The listener
|
||||
* @param int $priority The higher this value, the earlier an event
|
||||
* listener will be triggered in the chain (defaults to 0)
|
||||
*/
|
||||
public function addListener($eventName, $listener, $priority = 0);
|
||||
|
||||
/**
|
||||
* Adds an event subscriber.
|
||||
*
|
||||
* The subscriber is asked for all the events it is
|
||||
* interested in and added as a listener for these events.
|
||||
*/
|
||||
public function addSubscriber(EventSubscriberInterface $subscriber);
|
||||
|
||||
/**
|
||||
* Removes an event listener from the specified events.
|
||||
*
|
||||
* @param string $eventName The event to remove a listener from
|
||||
* @param callable $listener The listener to remove
|
||||
*/
|
||||
public function removeListener($eventName, $listener);
|
||||
|
||||
public function removeSubscriber(EventSubscriberInterface $subscriber);
|
||||
|
||||
/**
|
||||
* Gets the listeners of a specific event or all listeners sorted by descending priority.
|
||||
*
|
||||
* @param string|null $eventName The name of the event
|
||||
*
|
||||
* @return array The event listeners for the specified event, or all event listeners by event name
|
||||
*/
|
||||
public function getListeners($eventName = null);
|
||||
|
||||
/**
|
||||
* Gets the listener priority for a specific event.
|
||||
*
|
||||
* Returns null if the event or the listener does not exist.
|
||||
*
|
||||
* @param string $eventName The name of the event
|
||||
* @param callable $listener The listener
|
||||
*
|
||||
* @return int|null The event listener priority
|
||||
*/
|
||||
public function getListenerPriority($eventName, $listener);
|
||||
|
||||
/**
|
||||
* Checks whether an event has any registered listeners.
|
||||
*
|
||||
* @param string|null $eventName The name of the event
|
||||
*
|
||||
* @return bool true if the specified event has any listeners, false otherwise
|
||||
*/
|
||||
public function hasListeners($eventName = null);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user