Skip to content

Commit

Permalink
[Cache] deprecate all PSR-16 adapters, provide Psr16Cache
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Nov 16, 2018
1 parent dbf053b commit f65edc3
Show file tree
Hide file tree
Showing 37 changed files with 1,243 additions and 1,539 deletions.
98 changes: 95 additions & 3 deletions src/Symfony/Component/Cache/Adapter/ApcuAdapter.php
Expand Up @@ -11,17 +11,109 @@

namespace Symfony\Component\Cache\Adapter;

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

class ApcuAdapter extends AbstractAdapter
{
use ApcuTrait;
public static function isSupported()
{
return \function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN);
}

/**
* @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);
}
}
}

/**
* {@inheritdoc}
*/
protected function doFetch(array $ids)
{
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
try {
$values = array();
foreach (apcu_fetch($ids, $ok) ?: array() 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;
}
}
}
141 changes: 140 additions & 1 deletion src/Symfony/Component/Cache/Adapter/ArrayAdapter.php
Expand Up @@ -13,6 +13,7 @@

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;
Expand All @@ -23,9 +24,12 @@
*/
class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInterface, ResettableInterface
{
use ArrayTrait;
use LoggerAwareTrait;

private $storeSerialized;
private $createCacheItem;
private $values = array();
private $expiries = array();

/**
* @param int $defaultLifetime
Expand Down Expand Up @@ -64,6 +68,19 @@ public function get(string $key, callable $callback, float $beta = null)
return $item->get();
}

/**
* {@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 @@ -159,4 +176,126 @@ public function delete(string $key): bool
{
return $this->deleteItem($key);
}

/**
* {@inheritdoc}
*/
public function clear()
{
$this->values = $this->expiries = array();

return true;
}

/**
* {@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}
*/
public function reset()
{
$this->clear();
}

/**
* 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;
}

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)
{
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);
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, '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}"', array('key' => $key, 'exception' => $e));
$value = false;
}
if (false === $value) {
$this->values[$key] = $value = null;
$isHit = false;
}
}

return $value;
}
}

0 comments on commit f65edc3

Please sign in to comment.