8000 [Cache] remove deprecated PSR-16 implementations et al. by nicolas-grekas · Pull Request #31760 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Cache] remove deprecated PSR-16 implementations et al. #31760

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 99 additions & 4 deletions src/Symfony/Component/Cache/Adapter/ApcuAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,112 @@

namespace Symfony\Component\Cache\Adapter;

use Symfony\Component\Cache\Traits\ApcuTrait;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\CacheException;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
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);
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);
}
}
}

public static function isSupported()
{
return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN);
}

/**
* {@inheritdoc}
*/
protected function doFetch(array $ids)
{
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
try {
$values = [];
foreach (apcu_fetch($ids, $ok) ?: [] as $k => $v) {
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', 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, $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(key($values));
}

throw $e;
}
}
}
147 changes: 143 additions & 4 deletions src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,21 @@

use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
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;
use LoggerAwareTrait;

private $storeSerialized;
private $values = [];
private $expiries = [];
private $createCacheItem;

/**
Expand Down Expand Up @@ -65,6 +68,27 @@ public function get(string $key, callable $callback, float $beta = null, array &
return $item->get();
}

/**
* {@inheritdoc}
*/
public function delete(string $key): bool
{
return $this->deleteItem($key);
}

/**
* {@inheritdoc}
*/
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}
*/
Expand Down Expand Up @@ -94,6 +118,19 @@ public function getItems(array $keys = [])
return $this->generateItems($keys, microtime(true), $this->createCacheItem);
}

/**
* {@inheritdoc}
*/
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}
*/
Expand Down Expand Up @@ -156,8 +193,110 @@ public function commit()
/**
* {@inheritdoc}
*/
public function delete(string $key): bool
public function clear()
{
return $this->deleteItem($key);
$this->values = $this->expiries = [];

return true;
}

/**
* Returns all cached values, with cache miss as null.
*
* @return array
*/
public function getValues()
{
if (!$this->storeSerialized) {
return $this->values;
}

$values = $this->values;
foreach ($values as $k => $v) {
if (null === $v || 'N;' === $v) {
continue;
}
if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
$values[$k] = serialize($v);
}
}

return $values;
}

/**
* {@inheritdoc}
*/
public function reset()
{
$this->clear();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isn't just doing the same thing as clear without the return better ?

Copy link
Member Author
@nicolas-grekas nicolas-grekas May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must implement both: clear() for psr-6, reset() for service-contracts.
This is copy/paste from current implementations that exist in traits.

}

private function generateItems(array $keys, $now, $f)
{
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) {
$type = \is_object($value) ? \get_class($value) : \gettype($value);
$message = sprintf('Failed to save key "{key}" of type %s: %s', $type, $e->getMessage());
CacheItem::log($this->logger, $message, ['key' => substr($id, \strlen($this->namespace)), 'exception' => $e]);

return;
}
// Keep value serialized if it contains any objects or any internal references
if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
return $serialized;
}
}

return $value;
}

private function unfreeze(string $key, bool &$isHit)
{
if ('N;' === $value = $this->values[$key]) {
return null;
}
if (\is_string($value) && isset($value[2]) && ':' === $value[1]) {
try {
$value = unserialize($value);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to unserialize key "{key}": '.$e->getMessage(), ['key' => $key, 'exception' => $e]);
$value = false;
}
if (false === $value) {
$this->values[$key] = $value = null;
$isHit = false;
}
}

return $value;
}
}
82 changes: 80 additions & 2 deletions src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,94 @@
namespace Symfony\Component\Cache\Adapter;

use Doctrine\Common\Cache\CacheProvider;
use Symfony\Component\Cache\Traits\DoctrineTrait;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class DoctrineAdapter extends AbstractAdapter
{
use DoctrineTrait;
private $provider;

public function __construct(CacheProvider $provider, string $namespace = '', int $defaultLifetime = 0)
{
parent::__construct('', $defaultLifetime);
$this->provider = $provider;
$provider->setNamespace($namespace);
}

/**
* {@inheritdoc}
*/
public function reset()
{
parent::reset();
$this->provider->setNamespace($this->provider->getNamespace());
}

/**
* {@inheritdoc}
*/
protected function doFetch(array $ids)
{
$unserializeCallbackHandler = ini_set('unserialize_callback_func', parent::class.'::handleUnserializeCallback');
try {
return $this->provider->fetchMultiple($ids);
} catch (\Error $e) {
$trace = $e->getTrace();

if (isset($trace[0]['function']) && !isset($trace[0]['class'])) {
switch ($trace[0]['function']) {
case 'unserialize':
case 'apcu_fetch':
case 'apc_fetch':
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
}
}

throw $e;
} finally {
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
}
}

/**
* {@inheritdoc}
*/
protected function doHave($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, $lifetime)
{
return $this->provider->saveMultiple($values, $lifetime);
}
}
Loading
0