[Cache] deprecate all PSR-16 adapters, provide Psr16Cache instead
This commit is contained in:
parent
cafbdb73e8
commit
5006be63c1
@ -8,6 +8,13 @@ BrowserKit
|
||||
* Deprecated `Response::buildHeader()`
|
||||
* Deprecated `Response::getStatus()`, use `Response::getStatusCode()` instead
|
||||
|
||||
Cache
|
||||
-----
|
||||
|
||||
* The `psr/simple-cache` dependency has been removed - 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.
|
||||
|
||||
Config
|
||||
------
|
||||
|
||||
@ -18,6 +25,7 @@ FrameworkBundle
|
||||
|
||||
* Not passing the project directory to the constructor of the `AssetsInstallCommand` is deprecated. This argument will
|
||||
be mandatory in 5.0.
|
||||
* Deprecated the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead.
|
||||
|
||||
HttpFoundation
|
||||
--------------
|
||||
|
@ -13,6 +13,8 @@ Cache
|
||||
-----
|
||||
|
||||
* Removed `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead.
|
||||
* Removed all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead.
|
||||
* Removed `SimpleCacheAdapter`, use `Psr16Adapter instead.
|
||||
|
||||
Config
|
||||
------
|
||||
@ -163,6 +165,7 @@ FrameworkBundle
|
||||
* The `Templating\Helper\TranslatorHelper::transChoice()` method has been removed, use the `trans()` one instead with a `%count%` parameter.
|
||||
* Removed support for legacy translations directories `src/Resources/translations/` and `src/Resources/<BundleName>/translations/`, use `translations/` instead.
|
||||
* Support for the legacy directory structure in `translation:update` and `debug:translation` commands has been removed.
|
||||
* Removed the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead.
|
||||
|
||||
HttpFoundation
|
||||
--------------
|
||||
|
@ -8,6 +8,7 @@ CHANGELOG
|
||||
be mandatory in 5.0.
|
||||
* Added `ControllerTrait::isFormValid()`
|
||||
* Added an `help_html` form option to display the `help` text as HTML
|
||||
* Deprecated the "Psr\SimpleCache\CacheInterface" / "cache.app.simple" service, use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead
|
||||
|
||||
* [BC Break] When using Messenger, the default transport changed from
|
||||
using Symfony's serializer service to use `PhpSerializer`, which uses
|
||||
|
@ -11,7 +11,8 @@
|
||||
<tag name="cache.pool" clearer="cache.app_clearer" reset="reset" />
|
||||
</service>
|
||||
|
||||
<service id="cache.app.simple" class="Symfony\Component\Cache\Simple\Psr6Cache">
|
||||
<service id="cache.app.simple" class="Symfony\Component\Cache\Psr16Cache">
|
||||
<deprecated>The "Psr\SimpleCache\CacheInterface" / "%service_id%" service is deprecated since Symfony 4.3. Use "Symfony\Contracts\Cache\CacheInterface" / "cache.app" instead.</deprecated>
|
||||
<argument type="service" id="cache.app" />
|
||||
</service>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
"require": {
|
||||
"php": "^7.1.3",
|
||||
"ext-xml": "*",
|
||||
"symfony/cache": "~4.2",
|
||||
"symfony/cache": "~4.3",
|
||||
"symfony/config": "~4.2",
|
||||
"symfony/contracts": "^1.0.2",
|
||||
"symfony/dependency-injection": "^4.2",
|
||||
|
81
src/Symfony/Component/Cache/Adapter/Psr16Adapter.php
Normal file
81
src/Symfony/Component/Cache/Adapter/Psr16Adapter.php
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\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;
|
||||
|
||||
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, $lifetime)
|
||||
{
|
||||
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
|
||||
}
|
||||
}
|
@ -11,69 +11,11 @@
|
||||
|
||||
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;
|
||||
@trigger_error(sprintf('The "%s" class is @deprecated since Symfony 4.3, use "Psr16Adapter" instead.', SimpleCacheAdapter::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use Psr16Adapter instead.
|
||||
*/
|
||||
class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface, ResettableInterface
|
||||
class SimpleCacheAdapter extends Psr16Adapter
|
||||
{
|
||||
use ProxyTrait;
|
||||
|
||||
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, $lifetime)
|
||||
{
|
||||
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,13 @@
|
||||
CHANGELOG
|
||||
=========
|
||||
|
||||
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
|
||||
-----
|
||||
|
||||
|
@ -14,6 +14,12 @@ namespace Symfony\Component\Cache\Exception;
|
||||
use Psr\Cache\CacheException as Psr6CacheInterface;
|
||||
use Psr\SimpleCache\CacheException as SimpleCacheInterface;
|
||||
|
||||
class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
if (interface_exists(SimpleCacheInterface::class)) {
|
||||
class CacheException extends \Exception implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class CacheException extends \Exception implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,12 @@ namespace Symfony\Component\Cache\Exception;
|
||||
use Psr\Cache\InvalidArgumentException as Psr6CacheInterface;
|
||||
use Psr\SimpleCache\InvalidArgumentException as SimpleCacheInterface;
|
||||
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
if (interface_exists(SimpleCacheInterface::class)) {
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class InvalidArgumentException extends \InvalidArgumentException implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,12 @@ namespace Symfony\Component\Cache\Exception;
|
||||
use Psr\Cache\CacheException as Psr6CacheInterface;
|
||||
use Psr\SimpleCache\CacheException as SimpleCacheInterface;
|
||||
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
if (interface_exists(SimpleCacheInterface::class)) {
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface, SimpleCacheInterface
|
||||
{
|
||||
}
|
||||
} else {
|
||||
class LogicException extends \LogicException implements Psr6CacheInterface
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ class LockRegistry
|
||||
__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.'SimpleCacheAdapter.php',
|
||||
__DIR__.\DIRECTORY_SEPARATOR.'Adapter'.\DIRECTORY_SEPARATOR.'TagAwareAdapter.php',
|
||||
|
263
src/Symfony/Component/Cache/Psr16Cache.php
Normal file
263
src/Symfony/Component/Cache/Psr16Cache.php
Normal file
@ -0,0 +1,263 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* 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(
|
||||
function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) {
|
||||
$item = clone $cacheItemPrototype;
|
||||
$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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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) $metadata[CacheItem::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET, $metadata[CacheItem::METADATA_CTIME])."\x5F" => $values[$key]];
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -12,16 +12,20 @@
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
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);
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use AbstractAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
abstract class AbstractCache implements CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
abstract class AbstractCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use AbstractTrait {
|
||||
deleteItems as private;
|
||||
|
@ -13,6 +13,11 @@ namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Traits\ApcuTrait;
|
||||
|
||||
@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;
|
||||
|
@ -12,16 +12,20 @@
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
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);
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use ArrayAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
class ArrayCache implements Psr16CacheInterface, LoggerAwareInterface, ResettableInterface
|
||||
{
|
||||
use ArrayTrait {
|
||||
ArrayTrait::deleteItem as delete;
|
||||
|
@ -11,21 +11,25 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
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.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use ChainAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class ChainCache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
class ChainCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $miss;
|
||||
private $caches = [];
|
||||
@ -33,8 +37,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
|
||||
private $cacheCount;
|
||||
|
||||
/**
|
||||
* @param CacheInterface[] $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
|
||||
* @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)
|
||||
{
|
||||
@ -43,8 +47,8 @@ class ChainCache implements CacheInterface, PruneableInterface, ResettableInterf
|
||||
}
|
||||
|
||||
foreach ($caches as $cache) {
|
||||
if (!$cache instanceof CacheInterface) {
|
||||
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), CacheInterface::class));
|
||||
if (!$cache instanceof Psr16CacheInterface) {
|
||||
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', \get_class($cache), Psr16CacheInterface::class));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,15 @@
|
||||
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;
|
||||
|
@ -11,11 +11,18 @@
|
||||
|
||||
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;
|
||||
|
@ -11,9 +11,16 @@
|
||||
|
||||
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;
|
||||
|
@ -11,12 +11,16 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
use Symfony\Components\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);
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use NullAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class NullCache implements CacheInterface
|
||||
class NullCache implements Psr16CacheInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
|
@ -11,10 +11,17 @@
|
||||
|
||||
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;
|
||||
|
@ -11,28 +11,28 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @deprecated since Symfony 4.3, use PhpArrayAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
class PhpArrayCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
use PhpArrayTrait;
|
||||
|
||||
/**
|
||||
* @param string $file The PHP file were values are cached
|
||||
* @param CacheInterface $fallbackPool A pool to fallback on when an item is not hit
|
||||
* @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, CacheInterface $fallbackPool)
|
||||
public function __construct(string $file, Psr16CacheInterface $fallbackPool)
|
||||
{
|
||||
$this->file = $file;
|
||||
$this->pool = $fallbackPool;
|
||||
@ -43,9 +43,9 @@ class PhpArrayCache implements CacheInterface, PruneableInterface, ResettableInt
|
||||
*
|
||||
* @param string $file The PHP file were values are cached
|
||||
*
|
||||
* @return CacheInterface
|
||||
* @return Psr16CacheInterface
|
||||
*/
|
||||
public static function create($file, CacheInterface $fallbackPool)
|
||||
public static function create($file, Psr16CacheInterface $fallbackPool)
|
||||
{
|
||||
// Shared memory is available in PHP 7.0+ with OPCache enabled
|
||||
if (filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
|
@ -11,10 +11,17 @@
|
||||
|
||||
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;
|
||||
|
@ -11,254 +11,13 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
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\CacheItem;
|
||||
use Symfony\Component\Cache\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\ResettableInterface;
|
||||
use Symfony\Component\Cache\Traits\ProxyTrait;
|
||||
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);
|
||||
|
||||
/**
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use Psr16Cache instead.
|
||||
*/
|
||||
class Psr6Cache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
class Psr6Cache extends Psr16Cache
|
||||
{
|
||||
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(
|
||||
function ($key, $value, $allowInt = false) use (&$cacheItemPrototype) {
|
||||
$item = clone $cacheItemPrototype;
|
||||
$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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
return $this->pool->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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) $metadata[CacheItem::METADATA_EXPIRY] - self::METADATA_EXPIRY_OFFSET, $metadata[CacheItem::METADATA_CTIME])."\x5F" => $values[$key]];
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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}
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,16 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\RedisAdapter;
|
||||
use Symfony\Component\Cache\Marshaller\MarshallerInterface;
|
||||
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;
|
||||
|
@ -11,23 +11,24 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Simple;
|
||||
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use Psr\SimpleCache\CacheInterface as Psr16CacheInterface;
|
||||
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);
|
||||
|
||||
/**
|
||||
* An adapter that collects data about all cache calls.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
* @deprecated since Symfony 4.3, use TraceableAdapter and type-hint for CacheInterface instead.
|
||||
*/
|
||||
class TraceableCache implements CacheInterface, PruneableInterface, ResettableInterface
|
||||
class TraceableCache implements Psr16CacheInterface, PruneableInterface, ResettableInterface
|
||||
{
|
||||
private $pool;
|
||||
private $miss;
|
||||
private $calls = [];
|
||||
|
||||
public function __construct(CacheInterface $pool)
|
||||
public function __construct(Psr16CacheInterface $pool)
|
||||
{
|
||||
$this->pool = $pool;
|
||||
$this->miss = new \stdClass();
|
||||
|
@ -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\Tests\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\Psr16Adapter;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class Psr16AdapterTest extends AdapterTestCase
|
||||
{
|
||||
protected $skippedTests = [
|
||||
'testPrune' => 'Psr16adapter just proxies',
|
||||
];
|
||||
|
||||
public function createCachePool($defaultLifetime = 0)
|
||||
{
|
||||
return new Psr16Adapter(new Psr16Cache(new FilesystemAdapter()), '', $defaultLifetime);
|
||||
}
|
||||
}
|
@ -11,12 +11,12 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
|
||||
use Symfony\Component\Cache\Simple\Psr6Cache;
|
||||
use Symfony\Component\Cache\Simple\FilesystemCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class SimpleCacheAdapterTest extends AdapterTestCase
|
||||
{
|
||||
@ -26,6 +26,6 @@ class SimpleCacheAdapterTest extends AdapterTestCase
|
||||
|
||||
public function createCachePool($defaultLifetime = 0)
|
||||
{
|
||||
return new SimpleCacheAdapter(new Psr6Cache(new FilesystemAdapter()), '', $defaultLifetime);
|
||||
return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
|
||||
}
|
||||
}
|
||||
|
173
src/Symfony/Component/Cache/Tests/Psr16CacheTest.php
Normal file
173
src/Symfony/Component/Cache/Tests/Psr16CacheTest.php
Normal file
@ -0,0 +1,173 @@
|
||||
<?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\Tests;
|
||||
|
||||
use Cache\IntegrationTests\SimpleCacheTest;
|
||||
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
use Symfony\Component\Cache\PruneableInterface;
|
||||
use Symfony\Component\Cache\Psr16Cache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
*/
|
||||
class Psr16CacheTest extends SimpleCacheTest
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if (array_key_exists('testPrune', $this->skippedTests)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$pool = $this->createSimpleCache();
|
||||
if ($pool instanceof Psr16Cache) {
|
||||
$pool = ((array) $pool)[sprintf("\0%s\0pool", Psr16Cache::class)];
|
||||
}
|
||||
|
||||
if (!$pool instanceof PruneableInterface) {
|
||||
$this->skippedTests['testPrune'] = 'Not a pruneable cache pool.';
|
||||
}
|
||||
}
|
||||
|
||||
public function createSimpleCache($defaultLifetime = 0)
|
||||
{
|
||||
return new Psr16Cache(new FilesystemAdapter('', $defaultLifetime));
|
||||
}
|
||||
|
||||
public static function validKeys()
|
||||
{
|
||||
return array_merge(parent::validKeys(), [["a\0b"]]);
|
||||
}
|
||||
|
||||
public function testDefaultLifeTime()
|
||||
{
|
||||
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||
}
|
||||
|
||||
$cache = $this->createSimpleCache(2);
|
||||
$cache->clear();
|
||||
|
||||
$cache->set('key.dlt', 'value');
|
||||
sleep(1);
|
||||
|
||||
$this->assertSame('value', $cache->get('key.dlt'));
|
||||
|
||||
sleep(2);
|
||||
$this->assertNull($cache->get('key.dlt'));
|
||||
|
||||
$cache->clear();
|
||||
}
|
||||
|
||||
public function testNotUnserializable()
|
||||
{
|
||||
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||
}
|
||||
|
||||
$cache = $this->createSimpleCache();
|
||||
$cache->clear();
|
||||
|
||||
$cache->set('foo', new NotUnserializable());
|
||||
|
||||
$this->assertNull($cache->get('foo'));
|
||||
|
||||
$cache->setMultiple(['foo' => new NotUnserializable()]);
|
||||
|
||||
foreach ($cache->getMultiple(['foo']) as $value) {
|
||||
}
|
||||
$this->assertNull($value);
|
||||
|
||||
$cache->clear();
|
||||
}
|
||||
|
||||
public function testPrune()
|
||||
{
|
||||
if (isset($this->skippedTests[__FUNCTION__])) {
|
||||
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
|
||||
}
|
||||
|
||||
/** @var PruneableInterface|CacheInterface $cache */
|
||||
$cache = $this->createSimpleCache();
|
||||
$cache->clear();
|
||||
|
||||
$cache->set('foo', 'foo-val', new \DateInterval('PT05S'));
|
||||
$cache->set('bar', 'bar-val', new \DateInterval('PT10S'));
|
||||
$cache->set('baz', 'baz-val', new \DateInterval('PT15S'));
|
||||
$cache->set('qux', 'qux-val', new \DateInterval('PT20S'));
|
||||
|
||||
sleep(30);
|
||||
$cache->prune();
|
||||
$this->assertTrue($this->isPruned($cache, 'foo'));
|
||||
$this->assertTrue($this->isPruned($cache, 'bar'));
|
||||
$this->assertTrue($this->isPruned($cache, 'baz'));
|
||||
$this->assertTrue($this->isPruned($cache, 'qux'));
|
||||
|
||||
$cache->set('foo', 'foo-val');
|
||||
$cache->set('bar', 'bar-val', new \DateInterval('PT20S'));
|
||||
$cache->set('baz', 'baz-val', new \DateInterval('PT40S'));
|
||||
$cache->set('qux', 'qux-val', new \DateInterval('PT80S'));
|
||||
|
||||
$cache->prune();
|
||||
$this->assertFalse($this->isPruned($cache, 'foo'));
|
||||
$this->assertFalse($this->isPruned($cache, 'bar'));
|
||||
$this->assertFalse($this->isPruned($cache, 'baz'));
|
||||
$this->assertFalse($this->isPruned($cache, 'qux'));
|
||||
|
||||
sleep(30);
|
||||
$cache->prune();
|
||||
$this->assertFalse($this->isPruned($cache, 'foo'));
|
||||
$this->assertTrue($this->isPruned($cache, 'bar'));
|
||||
$this->assertFalse($this->isPruned($cache, 'baz'));
|
||||
$this->assertFalse($this->isPruned($cache, 'qux'));
|
||||
|
||||
sleep(30);
|
||||
$cache->prune();
|
||||
$this->assertFalse($this->isPruned($cache, 'foo'));
|
||||
$this->assertTrue($this->isPruned($cache, 'baz'));
|
||||
$this->assertFalse($this->isPruned($cache, 'qux'));
|
||||
|
||||
sleep(30);
|
||||
$cache->prune();
|
||||
$this->assertFalse($this->isPruned($cache, 'foo'));
|
||||
$this->assertTrue($this->isPruned($cache, 'qux'));
|
||||
|
||||
$cache->clear();
|
||||
}
|
||||
|
||||
protected function isPruned($cache, $name)
|
||||
{
|
||||
if (Psr16Cache::class !== \get_class($cache)) {
|
||||
$this->fail('Test classes for pruneable caches must implement `isPruned($cache, $name)` method.');
|
||||
}
|
||||
|
||||
$pool = ((array) $cache)[sprintf("\0%s\0pool", Psr16Cache::class)];
|
||||
$getFileMethod = (new \ReflectionObject($pool))->getMethod('getFile');
|
||||
$getFileMethod->setAccessible(true);
|
||||
|
||||
return !file_exists($getFileMethod->invoke($pool, $name));
|
||||
}
|
||||
}
|
||||
|
||||
class NotUnserializable implements \Serializable
|
||||
{
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(123);
|
||||
}
|
||||
|
||||
public function unserialize($ser)
|
||||
{
|
||||
throw new \Exception(__CLASS__);
|
||||
}
|
||||
}
|
@ -13,6 +13,9 @@ namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Simple\RedisCache;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
abstract class AbstractRedisCacheTest extends CacheTestCase
|
||||
{
|
||||
protected $skippedTests = [
|
||||
|
@ -13,6 +13,9 @@ namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Simple\ApcuCache;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class ApcuCacheTest extends CacheTestCase
|
||||
{
|
||||
protected $skippedTests = [
|
||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Cache\Simple\ArrayCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class ArrayCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -19,6 +19,7 @@ use Symfony\Component\Cache\Simple\FilesystemCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class ChainCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Tests\Fixtures\ArrayCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class DoctrineCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Simple\FilesystemCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class FilesystemCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Tests\Simple;
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Simple\MemcachedCache;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class MemcachedCacheTest extends CacheTestCase
|
||||
{
|
||||
protected $skippedTests = [
|
||||
|
@ -14,6 +14,9 @@ namespace Symfony\Component\Cache\Tests\Simple;
|
||||
use Symfony\Component\Cache\Adapter\AbstractAdapter;
|
||||
use Symfony\Component\Cache\Simple\MemcachedCache;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class MemcachedCacheTextModeTest extends MemcachedCacheTest
|
||||
{
|
||||
public function createSimpleCache($defaultLifetime = 0)
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Simple\NullCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class NullCacheTest extends TestCase
|
||||
{
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class PdoCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Cache\Tests\Traits\PdoPruneableTrait;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class PdoDbalCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class PhpArrayCacheTest extends CacheTestCase
|
||||
{
|
||||
@ -127,35 +128,3 @@ class PhpArrayCacheTest extends CacheTestCase
|
||||
$this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory');
|
||||
}
|
||||
}
|
||||
|
||||
class PhpArrayCacheWrapper extends PhpArrayCache
|
||||
{
|
||||
protected $data = [];
|
||||
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
(\Closure::bind(function () use ($key, $value) {
|
||||
$this->data[$key] = $value;
|
||||
$this->warmUp($this->data);
|
||||
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||
}, $this, PhpArrayCache::class))();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if (!\is_array($values) && !$values instanceof \Traversable) {
|
||||
return parent::setMultiple($values, $ttl);
|
||||
}
|
||||
(\Closure::bind(function () use ($values) {
|
||||
foreach ($values as $key => $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
$this->warmUp($this->data);
|
||||
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||
}, $this, PhpArrayCache::class))();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class PhpArrayCacheWithFallbackTest extends CacheTestCase
|
||||
{
|
||||
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Simple\PhpArrayCache;
|
||||
|
||||
class PhpArrayCacheWrapper extends PhpArrayCache
|
||||
{
|
||||
protected $data = [];
|
||||
|
||||
public function set($key, $value, $ttl = null)
|
||||
{
|
||||
(\Closure::bind(function () use ($key, $value) {
|
||||
$this->data[$key] = $value;
|
||||
$this->warmUp($this->data);
|
||||
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||
}, $this, PhpArrayCache::class))();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setMultiple($values, $ttl = null)
|
||||
{
|
||||
if (!\is_array($values) && !$values instanceof \Traversable) {
|
||||
return parent::setMultiple($values, $ttl);
|
||||
}
|
||||
(\Closure::bind(function () use ($values) {
|
||||
foreach ($values as $key => $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
$this->warmUp($this->data);
|
||||
list($this->keys, $this->values) = eval(substr(file_get_contents($this->file), 6));
|
||||
}, $this, PhpArrayCache::class))();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Simple\PhpFilesCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class PhpFilesCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -13,6 +13,9 @@ namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Simple\Psr6Cache;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
abstract class Psr6CacheTest extends CacheTestCase
|
||||
{
|
||||
protected $skippedTests = [
|
||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Cache\Adapter\FilesystemAdapter;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class Psr6CacheWithAdapterTest extends Psr6CacheTest
|
||||
{
|
||||
|
@ -15,6 +15,7 @@ use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class Psr6CacheWithoutAdapterTest extends Psr6CacheTest
|
||||
{
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class RedisArrayCacheTest extends AbstractRedisCacheTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
|
@ -13,6 +13,9 @@ namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
use Symfony\Component\Cache\Simple\RedisCache;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class RedisCacheTest extends AbstractRedisCacheTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
|
@ -11,6 +11,9 @@
|
||||
|
||||
namespace Symfony\Component\Cache\Tests\Simple;
|
||||
|
||||
/**
|
||||
* @group legacy
|
||||
*/
|
||||
class RedisClusterCacheTest extends AbstractRedisCacheTest
|
||||
{
|
||||
public static function setupBeforeClass()
|
||||
|
@ -16,6 +16,7 @@ use Symfony\Component\Cache\Simple\TraceableCache;
|
||||
|
||||
/**
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class TraceableCacheTest extends CacheTestCase
|
||||
{
|
||||
|
@ -24,7 +24,6 @@
|
||||
"php": "^7.1.3",
|
||||
"psr/cache": "~1.0",
|
||||
"psr/log": "~1.0",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"symfony/contracts": "^1.0",
|
||||
"symfony/var-exporter": "^4.2"
|
||||
},
|
||||
@ -33,6 +32,7 @@
|
||||
"doctrine/cache": "~1.6",
|
||||
"doctrine/dbal": "~2.5",
|
||||
"predis/predis": "~1.1",
|
||||
"psr/simple-cache": "^1.0",
|
||||
"symfony/config": "~4.2",
|
||||
"symfony/dependency-injection": "~3.4|~4.1",
|
||||
"symfony/var-dumper": "^4.1.1"
|
||||
|
Reference in New Issue
Block a user