Skip to content

[Cache] Implement PSR-16 SimpleCache v1.0 #20694

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 3 commits into from
Jan 23, 2017
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
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"twig/twig": "~1.28|~2.0",
"psr/cache": "~1.0",
"psr/log": "~1.0",
"psr/simple-cache": "^1.0",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-mbstring": "~1.0",
"symfony/polyfill-php56": "~1.0",
Expand Down Expand Up @@ -79,7 +80,7 @@
"symfony/yaml": "self.version"
},
"require-dev": {
"cache/integration-tests": "dev-master",
"cache/integration-tests": "^0.15.0",
"doctrine/cache": "~1.6",
"doctrine/data-fixtures": "1.0.*",
"doctrine/dbal": "~2.4",
Expand All @@ -99,7 +100,8 @@
"phpdocumentor/type-resolver": "<0.2.0"
},
"provide": {
"psr/cache-implementation": "1.0"
"psr/cache-implementation": "1.0",
"psr/simple-cache-implementation": "1.0"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 2 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
<array>
<element><string>Cache\IntegrationTests</string></element>
<element><string>Doctrine\Common\Cache</string></element>
<element><string>Symfony\Component\Cache</string></element>
<element><string>Symfony\Component\Cache\Traits</string></element>
<element><string>Symfony\Component\Console</string></element>
<element><string>Symfony\Component\HttpFoundation</string></element>
</array>
Expand Down
187 changes: 2 additions & 185 deletions src/Symfony/Component/Cache/Adapter/AbstractAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,24 @@

use Psr\Cache\CacheItemInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Traits\AbstractTrait;

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
{
use LoggerAwareTrait;
use AbstractTrait;

private static $apcuSupported;
private static $phpFilesSupported;

private $namespace;
private $deferred = array();
private $createCacheItem;
private $mergeByLifetime;

/**
* @var int|null The maximum length to enforce for identifiers or null when no limit applies
*/
protected $maxIdLength;

protected function __construct($namespace = '', $defaultLifetime = 0)
{
$this->namespace = '' === $namespace ? '' : $this->getId($namespace).':';
Expand Down Expand Up @@ -130,52 +123,6 @@ public static function createConnection($dsn, array $options = array())
throw new InvalidArgumentException(sprintf('Unsupported DSN: %s.', $dsn));
}

/**
* 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 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, $lifetime);

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -225,87 +172,6 @@ public function getItems(array $keys = array())
return $this->generateItems($items, $ids);
}

/**
* {@inheritdoc}
*/
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', array('key' => $key, 'exception' => $e));

return false;
}
}

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

try {
return $this->doClear($this->namespace);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to clear the cache', array('exception' => $e));

return false;
}
}

/**
* {@inheritdoc}
*/
public function deleteItem($key)
{
return $this->deleteItems(array($key));
}

/**
* {@inheritdoc}
*/
public function deleteItems(array $keys)
{
$ids = array();

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(array($id))) {
continue;
}
} catch (\Exception $e) {
}
CacheItem::log($this->logger, 'Failed to delete key "{key}"', array('key' => $key, 'exception' => $e));
$ok = false;
}

return $ok;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -394,47 +260,6 @@ public function __destruct()
}
}

/**
* Like the native unserialize() function but throws an exception if anything goes wrong.
*
* @param string $value
*
* @return mixed
*
* @throws \Exception
*/
protected static function unserialize($value)
{
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)
{
CacheItem::validateKey($key);

if (null === $this->maxIdLength) {
return $this->namespace.$key;
}
if (strlen($id = $this->namespace.$key) > $this->maxIdLength) {
$id = $this->namespace.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -22);
}

return $id;
}

private function generateItems($items, &$keys)
{
$f = $this->createCacheItem;
Expand All @@ -453,12 +278,4 @@ private function generateItems($items, &$keys)
yield $key => $f($key, null, false);
}
}

/**
* @internal
*/
public static function handleUnserializeCallback($class)
{
throw new \DomainException('Class not found: '.$class);
}
}
89 changes: 3 additions & 86 deletions src/Symfony/Component/Cache/Adapter/ApcuAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,97 +11,14 @@

namespace Symfony\Component\Cache\Adapter;

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

/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class ApcuAdapter extends AbstractAdapter
{
public static function isSupported()
{
return function_exists('apcu_fetch') && ini_get('apc.enabled') && !('cli' === PHP_SAPI && !ini_get('apc.enable_cli'));
}
use ApcuTrait;

public function __construct($namespace = '', $defaultLifetime = 0, $version = null)
{
if (!static::isSupported()) {
throw new CacheException('APCu is not enabled');
}
if ('cli' === PHP_SAPI) {
ini_set('apc.use_request_time', 0);
}
parent::__construct($namespace, $defaultLifetime);

if (null !== $version) {
CacheItem::validateKey($version);

if (!apcu_exists($version.'@'.$namespace)) {
$this->clear($namespace);
apcu_add($version.'@'.$namespace, null);
}
}
}

/**
* {@inheritdoc}
*/
protected function doFetch(array $ids)
{
try {
return apcu_fetch($ids);
} catch (\Error $e) {
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
}
}

/**
* {@inheritdoc}
*/
protected function doHave($id)
{
return apcu_exists($id);
}

/**
* {@inheritdoc}
*/
protected function doClear($namespace)
{
return isset($namespace[0]) && class_exists('APCuIterator', false)
? 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 {
return array_keys(apcu_store($values, null, $lifetime));
} catch (\Error $e) {
} catch (\Exception $e) {
}

if (1 === count($values)) {
// Workaround https://github.com/krakjoe/apcu/issues/170
apcu_delete(key($values));
}

throw $e;
$this->init($namespace, $defaultLifetime, $version);
}
}
Loading