[Cache] Create PSR-16 variants of all PSR-6 adapters

This commit is contained in:
Nicolas Grekas 2017-01-03 19:51:00 +01:00
parent 99ae9d6a35
commit 6219dd6b62
52 changed files with 2707 additions and 99 deletions

View File

@ -0,0 +1,24 @@
<?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;
public function __construct($namespace = '', $defaultLifetime = 0, $version = null)
{
$this->init($namespace, $defaultLifetime, $version);
}
}

View 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, $namespace = '', $defaultLifetime = 0)
{
parent::__construct('', $defaultLifetime);
$this->provider = $provider;
$provider->setNamespace($namespace);
}
}

View 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\Adapter;
use Symfony\Component\Cache\Traits\FilesystemTrait;
class FilesystemAdapter extends AbstractAdapter
{
use FilesystemTrait;
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
}
}

View File

@ -0,0 +1,26 @@
<?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\MemcachedTrait;
class MemcachedAdapter extends AbstractAdapter
{
use MemcachedTrait;
protected $maxIdLength = 250;
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
{
$this->init($client, $namespace, $defaultLifetime);
}
}

View File

@ -0,0 +1,52 @@
<?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\PdoTrait;
class PdoAdapter extends AbstractAdapter
{
use PdoTrait;
protected $maxIdLength = 255;
/**
* Constructor.
*
* 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.
*
* 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: array()]
*
* @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null
* @param string $namespace
* @param int $defaultLifetime
* @param array $options An associative array of options
*
* @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, $namespace = '', $defaultLifetime = 0, array $options = array())
{
$this->init($connOrDsn, $namespace, $defaultLifetime, $options);
}
}

View 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\Exception\CacheException;
use Symfony\Component\Cache\Traits\PhpFilesTrait;
class PhpFilesAdapter extends AbstractAdapter
{
use PhpFilesTrait;
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{
if (!static::isSupported()) {
throw new CacheException('OPcache is not enabled');
}
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
$e = new \Exception();
$this->includeHandler = function () use ($e) { throw $e; };
}
}

View 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\RedisTrait;
class RedisAdapter extends AbstractAdapter
{
use RedisTrait;
/**
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient
*/
public function __construct($redisClient, $namespace = '', $defaultLifetime = 0)
{
$this->init($redisClient, $namespace, $defaultLifetime);
}
}

View File

@ -1,6 +1,14 @@
CHANGELOG
=========
3.3.0
-----
* 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
-----

View File

@ -0,0 +1,177 @@
<?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;
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 AbstractCache implements CacheInterface, LoggerAwareInterface
{
use AbstractTrait {
deleteItems as private;
AbstractTrait::deleteItem as delete;
AbstractTrait::hasItem as has;
}
private $defaultLifetime;
protected function __construct($namespace = '', $defaultLifetime = 0)
{
$this->defaultLifetime = max(0, (int) $defaultLifetime);
$this->namespace = '' === $namespace ? '' : $this->getId($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(array($id)) as $value) {
return $value;
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch key "{key}"', array('key' => $key, 'exception' => $e));
}
return $default;
}
/**
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
CacheItem::validateKey($key);
return $this->setMultiple(array($key => $value), $ttl);
}
/**
* {@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)));
}
$ids = array();
foreach ($keys as $key) {
$ids[] = $this->getId($key);
}
try {
$values = $this->doFetch($ids);
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch requested values', array('keys' => $keys, 'exception' => $e));
$values = array();
}
$ids = array_combine($ids, $keys);
return $this->generateValues($values, $ids, $default);
}
/**
* {@inheritdoc}
*/
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 = array();
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 || array() === $e) {
return true;
}
$keys = array();
foreach (is_array($e) ? $e : array_keys($valuesById) as $id) {
$keys[] = substr($id, strlen($this->namespace));
}
CacheItem::log($this->logger, 'Failed to save values', array('keys' => $keys, 'exception' => $e instanceof \Exception ? $e : null));
return false;
}
/**
* {@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)));
}
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($values, &$keys, $default)
{
try {
foreach ($values as $id => $value) {
$key = $keys[$id];
unset($keys[$id]);
yield $key => $value;
}
} catch (\Exception $e) {
CacheItem::log($this->logger, 'Failed to fetch requested values', array('keys' => array_values($keys), 'exception' => $e));
}
foreach ($keys as $key) {
yield $key => $default;
}
}
}

View File

@ -0,0 +1,24 @@
<?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\Traits\ApcuTrait;
class ApcuCache extends AbstractCache
{
use ApcuTrait;
public function __construct($namespace = '', $defaultLifetime = 0, $version = null)
{
$this->init($namespace, $defaultLifetime, $version);
}
}

View File

@ -0,0 +1,147 @@
<?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;
use Symfony\Component\Cache\CacheItem;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Traits\ArrayTrait;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class ArrayCache implements CacheInterface, LoggerAwareInterface
{
use ArrayTrait {
ArrayTrait::deleteItem as delete;
ArrayTrait::hasItem as has;
}
private $defaultLifetime;
/**
* @param int $defaultLifetime
* @param bool $storeSerialized Disabling serialization can lead to cache corruptions when storing mutable values but increases performance otherwise
*/
public function __construct($defaultLifetime = 0, $storeSerialized = true)
{
$this->defaultLifetime = (int) $defaultLifetime;
$this->storeSerialized = $storeSerialized;
}
/**
* {@inheritdoc}
*/
public function get($key, $default = null)
{
foreach ($this->getMultiple(array($key), $default) as $v) {
return $v;
}
}
/**
* {@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)));
}
foreach ($keys as $key) {
CacheItem::validateKey($key);
}
return $this->generateItems($keys, time(), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; });
}
/**
* {@inheritdoc}
*/
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}
*/
public function set($key, $value, $ttl = null)
{
CacheItem::validateKey($key);
return $this->setMultiple(array($key => $value), $ttl);
}
/**
* {@inheritdoc}
*/
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 = array();
foreach ($values as $key => $value) {
is_int($key) || CacheItem::validateKey($key);
$valuesArray[$key] = $value;
}
if (false === $ttl = $this->normalizeTtl($ttl)) {
return $this->deleteMultiple(array_keys($valuesArray));
}
if ($this->storeSerialized) {
foreach ($valuesArray as $key => $value) {
try {
$valuesArray[$key] = 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 false;
}
}
}
$expiry = 0 < $ttl ? time() + $ttl : PHP_INT_MAX;
foreach ($valuesArray as $key => $value) {
$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)));
}
}

View File

@ -0,0 +1,222 @@
<?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;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
/**
* 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>
*/
class ChainCache implements CacheInterface
{
private $miss;
private $caches = array();
private $defaultLifetime;
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
*/
public function __construct(array $caches, $defaultLifetime = 0)
{
if (!$caches) {
throw new InvalidArgumentException('At least one cache must be specified.');
}
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));
}
}
$this->miss = new \stdClass();
$this->caches = array_values($caches);
$this->cacheCount = count($this->caches);
$this->defaultLifetime = 0 < $defaultLifetime ? (int) $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}
*/
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($values, $cacheIndex, $miss, $default)
{
$missing = array();
$nextCacheIndex = $cacheIndex + 1;
$nextCache = isset($this->caches[$nextCacheIndex]) ? $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}
*/
public function has($key)
{
foreach ($this->caches as $cache) {
if ($cache->has($key)) {
return true;
}
}
return false;
}
/**
* {@inheritdoc}
*/
public function clear()
{
$cleared = true;
$i = $this->cacheCount;
while ($i--) {
$cleared = $this->caches[$i]->clear() && $cleared;
}
return $cleared;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$deleted = true;
$i = $this->cacheCount;
while ($i--) {
$deleted = $this->caches[$i]->delete($key) && $deleted;
}
return $deleted;
}
/**
* {@inheritdoc}
*/
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}
*/
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}
*/
public function setMultiple($values, $ttl = null)
{
if ($values instanceof \Traversable) {
$valuesIterator = $values;
$values = function () use ($valuesIterator, &$values) {
$generatedValues = array();
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;
}
}

View 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\Simple;
use Doctrine\Common\Cache\CacheProvider;
use Symfony\Component\Cache\Traits\DoctrineTrait;
class DoctrineCache extends AbstractCache
{
use DoctrineTrait;
public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0)
{
parent::__construct('', $defaultLifetime);
$this->provider = $provider;
$provider->setNamespace($namespace);
}
}

View 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\Simple;
use Symfony\Component\Cache\Traits\FilesystemTrait;
class FilesystemCache extends AbstractCache
{
use FilesystemTrait;
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
}
}

View File

@ -0,0 +1,26 @@
<?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\Traits\MemcachedTrait;
class MemcachedCache extends AbstractCache
{
use MemcachedTrait;
protected $maxIdLength = 250;
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
{
$this->init($client, $namespace, $defaultLifetime);
}
}

View 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\Simple;
use Psr\SimpleCache\CacheInterface;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class NullCache implements CacheInterface
{
/**
* {@inheritdoc}
*/
public function get($key, $default = null)
{
return $default;
}
/**
* {@inheritdoc}
*/
public function getMultiple($keys, $default = null)
{
foreach ($keys as $key) {
yield $key => $default;
}
}
/**
* {@inheritdoc}
*/
public function has($key)
{
return false;
}
/**
* {@inheritdoc}
*/
public function clear()
{
return true;
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
return true;
}
/**
* {@inheritdoc}
*/
public function deleteMultiple($keys)
{
return true;
}
/**
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
return false;
}
/**
* {@inheritdoc}
*/
public function setMultiple($values, $ttl = null)
{
return false;
}
}

View File

@ -0,0 +1,52 @@
<?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\Traits\PdoTrait;
class PdoCache extends AbstractCache
{
use PdoTrait;
protected $maxIdLength = 255;
/**
* Constructor.
*
* 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.
*
* 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: array()]
*
* @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null
* @param string $namespace
* @param int $defaultLifetime
* @param array $options An associative array of options
*
* @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, $namespace = '', $defaultLifetime = 0, array $options = array())
{
$this->init($connOrDsn, $namespace, $defaultLifetime, $options);
}
}

View 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;
use Symfony\Component\Cache\Exception\InvalidArgumentException;
use Symfony\Component\Cache\Traits\PhpArrayTrait;
/**
* 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 PhpArrayCache implements CacheInterface
{
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
*/
public function __construct($file, CacheInterface $fallbackPool)
{
$this->file = $file;
$this->fallbackPool = $fallbackPool;
}
/**
* This adapter should only be used on PHP 7.0+ to take advantage of how PHP
* stores arrays in its latest versions. This factory method decorates the given
* fallback pool with this adapter only if the current PHP version is supported.
*
* @param string $file The PHP file were values are cached
*
* @return CacheInterface
*/
public static function create($file, CacheInterface $fallbackPool)
{
// Shared memory is available in PHP 7.0+ with OPCache enabled and in HHVM
if ((PHP_VERSION_ID >= 70000 && ini_get('opcache.enable')) || defined('HHVM_VERSION')) {
return new static($file, $fallbackPool);
}
return $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->values[$key])) {
return $this->fallbackPool->get($key, $default);
}
$value = $this->values[$key];
if ('N;' === $value) {
$value = null;
} elseif (is_string($value) && isset($value[2]) && ':' === $value[1]) {
try {
$e = null;
$value = unserialize($value);
} catch (\Error $e) {
} catch (\Exception $e) {
}
if (null !== $e) {
return $default;
}
}
return $value;
}
/**
* {@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)));
}
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}
*/
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->values[$key]) || $this->fallbackPool->has($key);
}
/**
* {@inheritdoc}
*/
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->values[$key]) && $this->fallbackPool->delete($key);
}
/**
* {@inheritdoc}
*/
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 = array();
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->values[$key])) {
$deleted = false;
} else {
$fallbackKeys[] = $key;
}
}
if (null === $this->values) {
$this->initialize();
}
if ($fallbackKeys) {
$deleted = $this->fallbackPool->deleteMultiple($fallbackKeys) && $deleted;
}
return $deleted;
}
/**
* {@inheritdoc}
*/
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->values[$key]) && $this->fallbackPool->set($key, $value, $ttl);
}
/**
* {@inheritdoc}
*/
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 = array();
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->values[$key])) {
$saved = false;
} else {
$fallbackValues[$key] = $value;
}
}
if ($fallbackValues) {
$saved = $this->fallbackPool->setMultiple($fallbackValues, $ttl) && $saved;
}
return $saved;
}
private function generateItems(array $keys, $default)
{
$fallbackKeys = array();
foreach ($keys as $key) {
if (isset($this->values[$key])) {
$value = $this->values[$key];
if ('N;' === $value) {
yield $key => null;
} elseif (is_string($value) && isset($value[2]) && ':' === $value[1]) {
try {
yield $key => unserialize($value);
} catch (\Error $e) {
yield $key => $default;
} catch (\Exception $e) {
yield $key => $default;
}
} else {
yield $key => $value;
}
} else {
$fallbackKeys[] = $key;
}
}
if ($fallbackKeys) {
foreach ($this->fallbackPool->getMultiple($fallbackKeys, $default) as $key => $item) {
yield $key => $item;
}
}
}
}

View 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\Simple;
use Symfony\Component\Cache\Exception\CacheException;
use Symfony\Component\Cache\Traits\PhpFilesTrait;
class PhpFilesCache extends AbstractCache
{
use PhpFilesTrait;
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{
if (!static::isSupported()) {
throw new CacheException('OPcache is not enabled');
}
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
$e = new \Exception();
$this->includeHandler = function () use ($e) { throw $e; };
}
}

View 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\Simple;
use Symfony\Component\Cache\Traits\RedisTrait;
class RedisCache extends AbstractCache
{
use RedisTrait;
/**
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient
*/
public function __construct($redisClient, $namespace = '', $defaultLifetime = 0)
{
$this->init($redisClient, $namespace, $defaultLifetime);
}
}

View File

@ -0,0 +1,190 @@
<?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;
/**
* An adapter that collects data about all cache calls.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class TraceableCache implements CacheInterface
{
private $pool;
private $miss;
private $calls = array();
public function __construct(CacheInterface $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__, compact('key', 'default'));
try {
$value = $this->pool->get($key, $miss);
} finally {
$event->end = microtime(true);
}
if ($miss !== $value) {
++$event->hits;
} else {
++$event->misses;
$value = $default;
}
return $event->result = $value;
}
/**
* {@inheritdoc}
*/
public function has($key)
{
$event = $this->start(__FUNCTION__, compact('key'));
try {
return $event->result = $this->pool->has($key);
} finally {
$event->end = microtime(true);
}
}
/**
* {@inheritdoc}
*/
public function delete($key)
{
$event = $this->start(__FUNCTION__, compact('key'));
try {
return $event->result = $this->pool->delete($key);
} finally {
$event->end = microtime(true);
}
}
/**
* {@inheritdoc}
*/
public function set($key, $value, $ttl = null)
{
$event = $this->start(__FUNCTION__, compact('key', 'value', 'ttl'));
try {
return $event->result = $this->pool->set($key, $value, $ttl);
} finally {
$event->end = microtime(true);
}
}
/**
* {@inheritdoc}
*/
public function setMultiple($values, $ttl = null)
{
$event = $this->start(__FUNCTION__, compact('values', 'ttl'));
try {
return $event->result = $this->pool->setMultiple($values, $ttl);
} finally {
$event->end = microtime(true);
}
}
/**
* {@inheritdoc}
*/
public function getMultiple($keys, $default = null)
{
$miss = null !== $default && is_object($default) ? $default : $this->miss;
$event = $this->start(__FUNCTION__, compact('keys', 'default'));
try {
$result = $this->pool->getMultiple($keys, $miss);
} finally {
$event->end = microtime(true);
}
$f = function () use ($result, $event, $miss, $default) {
$event->result = array();
foreach ($result as $key => $value) {
if ($miss !== $value) {
++$event->hits;
} else {
++$event->misses;
$value = $default;
}
yield $key => $event->result[$key] = $value;
}
};
return $f();
}
/**
* {@inheritdoc}
*/
public function clear()
{
$event = $this->start(__FUNCTION__);
try {
return $event->result = $this->pool->clear();
} finally {
$event->end = microtime(true);
}
}
/**
* {@inheritdoc}
*/
public function deleteMultiple($keys)
{
$event = $this->start(__FUNCTION__, compact('keys'));
try {
return $event->result = $this->pool->deleteMultiple($keys);
} finally {
$event->end = microtime(true);
}
}
public function getCalls()
{
try {
return $this->calls;
} finally {
$this->calls = array();
}
}
private function start($name, array $arguments = null)
{
$this->calls[] = $event = new TraceableCacheEvent();
$event->name = $name;
$event->arguments = $arguments;
$event->start = microtime(true);
return $event;
}
}
class TraceableCacheEvent
{
public $name;
public $arguments;
public $start;
public $end;
public $result;
public $hits = 0;
public $misses = 0;
}

View File

@ -48,7 +48,7 @@ class ApcuAdapterTest extends AdapterTestCase
public function testVersion()
{
$namespace = str_replace('\\', '.', __CLASS__);
$namespace = str_replace('\\', '.', get_class($this));
$pool1 = new ApcuAdapter($namespace, 0, 'p1');

View File

@ -22,7 +22,7 @@ class MemcachedAdapterTest extends AdapterTestCase
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
);
private static $client;
protected static $client;
public static function setupBeforeClass()
{

View File

@ -51,7 +51,7 @@ class PhpArrayAdapterTest extends AdapterTestCase
'testDefaultLifeTime' => 'PhpArrayAdapter does not allow configuring a default lifetime.',
);
private static $file;
protected static $file;
public static function setupBeforeClass()
{

View File

@ -25,10 +25,9 @@ class PhpArrayAdapterWithFallbackTest extends AdapterTestCase
'testHasItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
'testDeleteItemInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
'testDeleteItemsInvalidKeys' => 'PhpArrayAdapter does not throw exceptions on invalid key.',
'testDefaultLifeTime' => 'PhpArrayAdapter does not allow configuring a default lifetime.',
);
private static $file;
protected static $file;
public static function setupBeforeClass()
{
@ -42,8 +41,8 @@ class PhpArrayAdapterWithFallbackTest extends AdapterTestCase
}
}
public function createCachePool()
public function createCachePool($defaultLifetime = 0)
{
return new PhpArrayAdapter(self::$file, new FilesystemAdapter('php-array-fallback'));
return new PhpArrayAdapter(self::$file, new FilesystemAdapter('php-array-fallback', $defaultLifetime));
}
}

View File

@ -11,9 +11,8 @@
namespace Symfony\Component\Cache\Tests\Adapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Simple\FilesystemCache;
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
use Symfony\Component\Cache\Simple\Psr6Cache;
/**
* @group time-sensitive
@ -22,6 +21,6 @@ class SimpleCacheAdapterTest extends AdapterTestCase
{
public function createCachePool($defaultLifetime = 0)
{
return new SimpleCacheAdapter(new Psr6Cache(new FilesystemAdapter()), '', $defaultLifetime);
return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
}
}

View File

@ -32,10 +32,10 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertEquals('getItem', $call->name);
$this->assertEquals('k', $call->argument);
$this->assertEquals(0, $call->hits);
$this->assertEquals(1, $call->misses);
$this->assertSame('getItem', $call->name);
$this->assertSame('k', $call->argument);
$this->assertSame(0, $call->hits);
$this->assertSame(1, $call->misses);
$this->assertNull($call->result);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
@ -51,8 +51,8 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(3, $calls);
$call = $calls[2];
$this->assertEquals(1, $call->hits);
$this->assertEquals(0, $call->misses);
$this->assertSame(1, $call->hits);
$this->assertSame(0, $call->misses);
}
public function testGetItemsMiss()
@ -66,9 +66,9 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertEquals('getItems', $call->name);
$this->assertEquals($arg, $call->argument);
$this->assertEquals(2, $call->misses);
$this->assertSame('getItems', $call->name);
$this->assertSame($arg, $call->argument);
$this->assertSame(2, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
@ -81,8 +81,8 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertEquals('hasItem', $call->name);
$this->assertEquals('k', $call->argument);
$this->assertSame('hasItem', $call->name);
$this->assertSame('k', $call->argument);
$this->assertFalse($call->result);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
@ -98,8 +98,8 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(3, $calls);
$call = $calls[2];
$this->assertEquals('hasItem', $call->name);
$this->assertEquals('k', $call->argument);
$this->assertSame('hasItem', $call->name);
$this->assertSame('k', $call->argument);
$this->assertTrue($call->result);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
@ -113,10 +113,10 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertEquals('deleteItem', $call->name);
$this->assertEquals('k', $call->argument);
$this->assertEquals(0, $call->hits);
$this->assertEquals(0, $call->misses);
$this->assertSame('deleteItem', $call->name);
$this->assertSame('k', $call->argument);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
@ -130,10 +130,10 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertEquals('deleteItems', $call->name);
$this->assertEquals($arg, $call->argument);
$this->assertEquals(0, $call->hits);
$this->assertEquals(0, $call->misses);
$this->assertSame('deleteItems', $call->name);
$this->assertSame($arg, $call->argument);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
@ -147,10 +147,10 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(2, $calls);
$call = $calls[1];
$this->assertEquals('save', $call->name);
$this->assertEquals($item, $call->argument);
$this->assertEquals(0, $call->hits);
$this->assertEquals(0, $call->misses);
$this->assertSame('save', $call->name);
$this->assertSame($item, $call->argument);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
@ -164,10 +164,10 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(2, $calls);
$call = $calls[1];
$this->assertEquals('saveDeferred', $call->name);
$this->assertEquals($item, $call->argument);
$this->assertEquals(0, $call->hits);
$this->assertEquals(0, $call->misses);
$this->assertSame('saveDeferred', $call->name);
$this->assertSame($item, $call->argument);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
@ -180,10 +180,10 @@ class TraceableAdapterTest extends AdapterTestCase
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertEquals('commit', $call->name);
$this->assertSame('commit', $call->name);
$this->assertNull(null, $call->argument);
$this->assertEquals(0, $call->hits);
$this->assertEquals(0, $call->misses);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}

View File

@ -0,0 +1,47 @@
<?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\RedisCache;
abstract class AbstractRedisCacheTest extends CacheTestCase
{
protected $skippedTests = array(
'testSetTtl' => 'Testing expiration slows down the test suite',
'testSetMultipleTtl' => 'Testing expiration slows down the test suite',
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
);
protected static $redis;
public function createSimpleCache($defaultLifetime = 0)
{
return new RedisCache(self::$redis, str_replace('\\', '.', __CLASS__), $defaultLifetime);
}
public static function setupBeforeClass()
{
if (!extension_loaded('redis')) {
self::markTestSkipped('Extension redis required.');
}
if (!@((new \Redis())->connect(getenv('REDIS_HOST')))) {
$e = error_get_last();
self::markTestSkipped($e['message']);
}
}
public static function tearDownAfterClass()
{
self::$redis->flushDB();
self::$redis = null;
}
}

View File

@ -0,0 +1,35 @@
<?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\ApcuCache;
class ApcuCacheTest extends CacheTestCase
{
protected $skippedTests = array(
'testSetTtl' => 'Testing expiration slows down the test suite',
'testSetMultipleTtl' => 'Testing expiration slows down the test suite',
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
);
public function createSimpleCache($defaultLifetime = 0)
{
if (!function_exists('apcu_fetch') || !ini_get('apc.enabled') || ('cli' === PHP_SAPI && !ini_get('apc.enable_cli'))) {
$this->markTestSkipped('APCu extension is required.');
}
if ('\\' === DIRECTORY_SEPARATOR) {
$this->markTestSkipped('Fails transiently on Windows.');
}
return new ApcuCache(str_replace('\\', '.', __CLASS__), $defaultLifetime);
}
}

View 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\Tests\Simple;
use Symfony\Component\Cache\Simple\ArrayCache;
/**
* @group time-sensitive
*/
class ArrayCacheTest extends CacheTestCase
{
public function createSimpleCache($defaultLifetime = 0)
{
return new ArrayCache($defaultLifetime);
}
}

View File

@ -0,0 +1,66 @@
<?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 Cache\IntegrationTests\SimpleCacheTest;
abstract class CacheTestCase extends SimpleCacheTest
{
public function testDefaultLifeTime()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}
$cache = $this->createSimpleCache(2);
$cache->set('key.dlt', 'value');
sleep(1);
$this->assertSame('value', $cache->get('key.dlt'));
sleep(2);
$this->assertNull($cache->get('key.dlt'));
}
public function testNotUnserializable()
{
if (isset($this->skippedTests[__FUNCTION__])) {
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
}
$cache = $this->createSimpleCache();
$cache->set('foo', new NotUnserializable());
$this->assertNull($cache->get('foo'));
$cache->setMultiple(array('foo' => new NotUnserializable()));
foreach ($cache->getMultiple(array('foo')) as $value) {
}
$this->assertNull($value);
}
}
class NotUnserializable implements \Serializable
{
public function serialize()
{
return serialize(123);
}
public function unserialize($ser)
{
throw new \Exception(__CLASS__);
}
}

View 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\Tests\Simple;
use Symfony\Component\Cache\Simple\ArrayCache;
use Symfony\Component\Cache\Simple\ChainCache;
use Symfony\Component\Cache\Simple\FilesystemCache;
/**
* @group time-sensitive
*/
class ChainCacheTest extends CacheTestCase
{
public function createSimpleCache($defaultLifetime = 0)
{
return new ChainCache(array(new ArrayCache($defaultLifetime), new FilesystemCache('', $defaultLifetime)), $defaultLifetime);
}
/**
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedExceptionMessage At least one cache must be specified.
*/
public function testEmptyCachesException()
{
new ChainCache(array());
}
/**
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedExceptionMessage The class "stdClass" does not implement
*/
public function testInvalidCacheException()
{
new Chaincache(array(new \stdClass()));
}
}

View 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\Tests\Simple;
use Doctrine\Common\Cache\ArrayCache;
use Symfony\Component\Cache\Simple\DoctrineCache;
/**
* @group time-sensitive
*/
class DoctrineCacheTest extends CacheTestCase
{
protected $skippedTests = array(
'testObjectDoesNotChangeInCache' => 'ArrayCache does not use serialize/unserialize',
'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize',
);
public function createSimpleCache($defaultLifetime = 0)
{
return new DoctrineCache(new ArrayCache($defaultLifetime), '', $defaultLifetime);
}
}

View 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\Tests\Simple;
use Symfony\Component\Cache\Simple\FilesystemCache;
/**
* @group time-sensitive
*/
class FilesystemCacheTest extends CacheTestCase
{
public function createSimpleCache($defaultLifetime = 0)
{
return new FilesystemCache('', $defaultLifetime);
}
}

View File

@ -0,0 +1,165 @@
<?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\Adapter\AbstractAdapter;
use Symfony\Component\Cache\Simple\MemcachedCache;
class MemcachedCacheTest extends CacheTestCase
{
protected $skippedTests = array(
'testSetTtl' => 'Testing expiration slows down the test suite',
'testSetMultipleTtl' => 'Testing expiration slows down the test suite',
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
);
protected static $client;
public static function setupBeforeClass()
{
if (!MemcachedCache::isSupported()) {
self::markTestSkipped('Extension memcached >=2.2.0 required.');
}
self::$client = AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'));
self::$client->get('foo');
$code = self::$client->getResultCode();
if (\Memcached::RES_SUCCESS !== $code && \Memcached::RES_NOTFOUND !== $code) {
self::markTestSkipped('Memcached error: '.strtolower(self::$client->getResultMessage()));
}
}
public function createSimpleCache($defaultLifetime = 0)
{
$client = $defaultLifetime ? AbstractAdapter::createConnection('memcached://'.getenv('MEMCACHED_HOST'), array('binary_protocol' => false)) : self::$client;
return new MemcachedCache($client, str_replace('\\', '.', __CLASS__), $defaultLifetime);
}
public function testOptions()
{
$client = MemcachedCache::createConnection(array(), array(
'libketama_compatible' => false,
'distribution' => 'modula',
'compression' => true,
'serializer' => 'php',
'hash' => 'md5',
));
$this->assertSame(\Memcached::SERIALIZER_PHP, $client->getOption(\Memcached::OPT_SERIALIZER));
$this->assertSame(\Memcached::HASH_MD5, $client->getOption(\Memcached::OPT_HASH));
$this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION));
$this->assertSame(0, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE));
$this->assertSame(\Memcached::DISTRIBUTION_MODULA, $client->getOption(\Memcached::OPT_DISTRIBUTION));
}
/**
* @dataProvider provideBadOptions
* @expectedException \ErrorException
* @expectedExceptionMessage constant(): Couldn't find constant Memcached::
*/
public function testBadOptions($name, $value)
{
MemcachedCache::createConnection(array(), array($name => $value));
}
public function provideBadOptions()
{
return array(
array('foo', 'bar'),
array('hash', 'zyx'),
array('serializer', 'zyx'),
array('distribution', 'zyx'),
);
}
public function testDefaultOptions()
{
$this->assertTrue(MemcachedCache::isSupported());
$client = MemcachedCache::createConnection(array());
$this->assertTrue($client->getOption(\Memcached::OPT_COMPRESSION));
$this->assertSame(1, $client->getOption(\Memcached::OPT_BINARY_PROTOCOL));
$this->assertSame(1, $client->getOption(\Memcached::OPT_LIBKETAMA_COMPATIBLE));
}
/**
* @expectedException \Symfony\Component\Cache\Exception\CacheException
* @expectedExceptionMessage MemcachedAdapter: "serializer" option must be "php" or "igbinary".
*/
public function testOptionSerializer()
{
if (!\Memcached::HAVE_JSON) {
$this->markTestSkipped('Memcached::HAVE_JSON required');
}
new MemcachedCache(MemcachedCache::createConnection(array(), array('serializer' => 'json')));
}
/**
* @dataProvider provideServersSetting
*/
public function testServersSetting($dsn, $host, $port)
{
$client1 = MemcachedCache::createConnection($dsn);
$client2 = MemcachedCache::createConnection(array($dsn));
$client3 = MemcachedCache::createConnection(array(array($host, $port)));
$expect = array(
'host' => $host,
'port' => $port,
);
$f = function ($s) { return array('host' => $s['host'], 'port' => $s['port']); };
$this->assertSame(array($expect), array_map($f, $client1->getServerList()));
$this->assertSame(array($expect), array_map($f, $client2->getServerList()));
$this->assertSame(array($expect), array_map($f, $client3->getServerList()));
}
public function provideServersSetting()
{
yield array(
'memcached://127.0.0.1/50',
'127.0.0.1',
11211,
);
yield array(
'memcached://localhost:11222?weight=25',
'localhost',
11222,
);
if (ini_get('memcached.use_sasl')) {
yield array(
'memcached://user:password@127.0.0.1?weight=50',
'127.0.0.1',
11211,
);
}
yield array(
'memcached:///var/run/memcached.sock?weight=25',
'/var/run/memcached.sock',
0,
);
yield array(
'memcached:///var/local/run/memcached.socket?weight=25',
'/var/local/run/memcached.socket',
0,
);
if (ini_get('memcached.use_sasl')) {
yield array(
'memcached://user:password@/var/local/run/memcached.socket?weight=25',
'/var/local/run/memcached.socket',
0,
);
}
}
}

View File

@ -0,0 +1,95 @@
<?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\NullCache;
/**
* @group time-sensitive
*/
class NullCacheTest extends \PHPUnit_Framework_TestCase
{
public function createCachePool()
{
return new NullCache();
}
public function testGetItem()
{
$cache = $this->createCachePool();
$this->assertNull($cache->get('key'));
}
public function testHas()
{
$this->assertFalse($this->createCachePool()->has('key'));
}
public function testGetMultiple()
{
$cache = $this->createCachePool();
$keys = array('foo', 'bar', 'baz', 'biz');
$default = new \stdClass();
$items = $cache->getMultiple($keys, $default);
$count = 0;
foreach ($items as $key => $item) {
$this->assertTrue(in_array($key, $keys), 'Cache key can not change.');
$this->assertSame($default, $item);
// Remove $key for $keys
foreach ($keys as $k => $v) {
if ($v === $key) {
unset($keys[$k]);
}
}
++$count;
}
$this->assertSame(4, $count);
}
public function testClear()
{
$this->assertTrue($this->createCachePool()->clear());
}
public function testDelete()
{
$this->assertTrue($this->createCachePool()->delete('key'));
}
public function testDeleteMultiple()
{
$this->assertTrue($this->createCachePool()->deleteMultiple(array('key', 'foo', 'bar')));
}
public function testSet()
{
$cache = $this->createCachePool();
$this->assertFalse($cache->set('key', 'val'));
$this->assertNull($cache->get('key'));
}
public function testSetMultiple()
{
$cache = $this->createCachePool();
$this->assertFalse($cache->setMultiple(array('key' => 'val')));
$this->assertNull($cache->get('key'));
}
}

View File

@ -0,0 +1,44 @@
<?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\PdoCache;
/**
* @group time-sensitive
*/
class PdoCacheTest extends CacheTestCase
{
protected static $dbFile;
public static function setupBeforeClass()
{
if (!extension_loaded('pdo_sqlite')) {
throw new \PHPUnit_Framework_SkippedTestError('Extension pdo_sqlite required.');
}
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
$pool = new PdoCache('sqlite:'.self::$dbFile);
$pool->createTable();
}
public static function tearDownAfterClass()
{
@unlink(self::$dbFile);
}
public function createSimpleCache($defaultLifetime = 0)
{
return new PdoCache('sqlite:'.self::$dbFile, 'ns', $defaultLifetime);
}
}

View 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\Tests\Simple;
use Doctrine\DBAL\DriverManager;
use Symfony\Component\Cache\Simple\PdoCache;
/**
* @group time-sensitive
*/
class PdoDbalCacheTest extends CacheTestCase
{
protected static $dbFile;
public static function setupBeforeClass()
{
if (!extension_loaded('pdo_sqlite')) {
throw new \PHPUnit_Framework_SkippedTestError('Extension pdo_sqlite required.');
}
self::$dbFile = tempnam(sys_get_temp_dir(), 'sf_sqlite_cache');
$pool = new PdoCache(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)));
$pool->createTable();
}
public static function tearDownAfterClass()
{
@unlink(self::$dbFile);
}
public function createSimpleCache($defaultLifetime = 0)
{
return new PdoCache(DriverManager::getConnection(array('driver' => 'pdo_sqlite', 'path' => self::$dbFile)), '', $defaultLifetime);
}
}

View File

@ -0,0 +1,139 @@
<?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\Tests\Adapter\FilesystemAdapterTest;
use Symfony\Component\Cache\Simple\NullCache;
use Symfony\Component\Cache\Simple\PhpArrayCache;
/**
* @group time-sensitive
*/
class PhpArrayCacheTest extends CacheTestCase
{
protected $skippedTests = array(
'testDelete' => 'PhpArrayCache does no writes',
'testDeleteMultiple' => 'PhpArrayCache does no writes',
'testDeleteMultipleGenerator' => 'PhpArrayCache does no writes',
'testSetTtl' => 'PhpArrayCache does no expiration',
'testSetMultipleTtl' => 'PhpArrayCache does no expiration',
'testSetExpiredTtl' => 'PhpArrayCache does no expiration',
'testSetMultipleExpiredTtl' => 'PhpArrayCache does no expiration',
'testGetInvalidKeys' => 'PhpArrayCache does no validation',
'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
'testSetInvalidKeys' => 'PhpArrayCache does no validation',
'testDeleteInvalidKeys' => 'PhpArrayCache does no validation',
'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation',
'testSetInvalidTtl' => 'PhpArrayCache does no validation',
'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation',
'testHasInvalidKeys' => 'PhpArrayCache does no validation',
'testSetValidData' => 'PhpArrayCache does no validation',
'testDefaultLifeTime' => 'PhpArrayCache does not allow configuring a default lifetime.',
);
protected static $file;
public static function setupBeforeClass()
{
self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php';
}
protected function tearDown()
{
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
}
}
public function createSimpleCache()
{
return new PhpArrayCacheWrapper(self::$file, new NullCache());
}
public function testStore()
{
$arrayWithRefs = array();
$arrayWithRefs[0] = 123;
$arrayWithRefs[1] = &$arrayWithRefs[0];
$object = (object) array(
'foo' => 'bar',
'foo2' => 'bar2',
);
$expected = array(
'null' => null,
'serializedString' => serialize($object),
'arrayWithRefs' => $arrayWithRefs,
'object' => $object,
'arrayWithObject' => array('bar' => $object),
);
$cache = new PhpArrayCache(self::$file, new NullCache());
$cache->warmUp($expected);
foreach ($expected as $key => $value) {
$this->assertSame(serialize($value), serialize($cache->get($key)), 'Warm up should create a PHP file that OPCache can load in memory');
}
}
public function testStoredFile()
{
$expected = array(
'integer' => 42,
'float' => 42.42,
'boolean' => true,
'array_simple' => array('foo', 'bar'),
'array_associative' => array('foo' => 'bar', 'foo2' => 'bar2'),
);
$cache = new PhpArrayCache(self::$file, new NullCache());
$cache->warmUp($expected);
$values = eval(substr(file_get_contents(self::$file), 6));
$this->assertSame($expected, $values, 'Warm up should create a PHP file that OPCache can load in memory');
}
}
class PhpArrayCacheWrapper extends PhpArrayCache
{
public function set($key, $value, $ttl = null)
{
call_user_func(\Closure::bind(function () use ($key, $value) {
$this->values[$key] = $value;
$this->warmUp($this->values);
$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);
}
call_user_func(\Closure::bind(function () use ($values) {
foreach ($values as $key => $value) {
$this->values[$key] = $value;
}
$this->warmUp($this->values);
$this->values = eval(substr(file_get_contents($this->file), 6));
}, $this, PhpArrayCache::class));
return true;
}
}

View 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\Tests\Simple;
use Symfony\Component\Cache\Simple\FilesystemCache;
use Symfony\Component\Cache\Simple\PhpArrayCache;
use Symfony\Component\Cache\Tests\Adapter\FilesystemAdapterTest;
/**
* @group time-sensitive
*/
class PhpArrayCacheWithFallbackTest extends CacheTestCase
{
protected $skippedTests = array(
'testGetInvalidKeys' => 'PhpArrayCache does no validation',
'testGetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
'testDeleteInvalidKeys' => 'PhpArrayCache does no validation',
'testDeleteMultipleInvalidKeys' => 'PhpArrayCache does no validation',
//'testSetValidData' => 'PhpArrayCache does no validation',
'testSetInvalidKeys' => 'PhpArrayCache does no validation',
'testSetInvalidTtl' => 'PhpArrayCache does no validation',
'testSetMultipleInvalidKeys' => 'PhpArrayCache does no validation',
'testSetMultipleInvalidTtl' => 'PhpArrayCache does no validation',
'testHasInvalidKeys' => 'PhpArrayCache does no validation',
);
protected static $file;
public static function setupBeforeClass()
{
self::$file = sys_get_temp_dir().'/symfony-cache/php-array-adapter-test.php';
}
protected function tearDown()
{
if (file_exists(sys_get_temp_dir().'/symfony-cache')) {
FilesystemAdapterTest::rmdir(sys_get_temp_dir().'/symfony-cache');
}
}
public function createSimpleCache($defaultLifetime = 0)
{
return new PhpArrayCache(self::$file, new FilesystemCache('php-array-fallback', $defaultLifetime));
}
}

View 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\Tests\Simple;
use Symfony\Component\Cache\Simple\PhpFilesCache;
/**
* @group time-sensitive
*/
class PhpFilesCacheTest extends CacheTestCase
{
protected $skippedTests = array(
'testDefaultLifeTime' => 'PhpFilesCache does not allow configuring a default lifetime.',
);
public function createSimpleCache()
{
if (!PhpFilesCache::isSupported()) {
$this->markTestSkipped('OPcache extension is not enabled.');
}
return new PhpFilesCache('sf-cache');
}
}

View File

@ -0,0 +1,26 @@
<?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\Adapter\FilesystemAdapter;
use Symfony\Component\Cache\Simple\Psr6Cache;
/**
* @group time-sensitive
*/
class Psr6CacheTest extends CacheTestCase
{
public function createSimpleCache($defaultLifetime = 0)
{
return new Psr6Cache(new FilesystemAdapter('', $defaultLifetime));
}
}

View File

@ -0,0 +1,24 @@
<?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;
class RedisArrayCacheTest extends AbstractRedisCacheTest
{
public static function setupBeforeClass()
{
parent::setupBeforeClass();
if (!class_exists('RedisArray')) {
self::markTestSkipped('The RedisArray class is required.');
}
self::$redis = new \RedisArray(array(getenv('REDIS_HOST')), array('lazy_connect' => true));
}
}

View 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\Cache\Tests\Simple;
use Symfony\Component\Cache\Simple\RedisCache;
class RedisCacheTest extends AbstractRedisCacheTest
{
public static function setupBeforeClass()
{
parent::setupBeforeClass();
self::$redis = RedisCache::createConnection('redis://'.getenv('REDIS_HOST'));
}
public function testCreateConnection()
{
$redisHost = getenv('REDIS_HOST');
$redis = RedisCache::createConnection('redis://'.$redisHost);
$this->assertInstanceOf(\Redis::class, $redis);
$this->assertTrue($redis->isConnected());
$this->assertSame(0, $redis->getDbNum());
$redis = RedisCache::createConnection('redis://'.$redisHost.'/2');
$this->assertSame(2, $redis->getDbNum());
$redis = RedisCache::createConnection('redis://'.$redisHost, array('timeout' => 3));
$this->assertEquals(3, $redis->getTimeout());
$redis = RedisCache::createConnection('redis://'.$redisHost.'?timeout=4');
$this->assertEquals(4, $redis->getTimeout());
$redis = RedisCache::createConnection('redis://'.$redisHost, array('read_timeout' => 5));
$this->assertEquals(5, $redis->getReadTimeout());
}
/**
* @dataProvider provideFailedCreateConnection
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedExceptionMessage Redis connection failed
*/
public function testFailedCreateConnection($dsn)
{
RedisCache::createConnection($dsn);
}
public function provideFailedCreateConnection()
{
return array(
array('redis://localhost:1234'),
array('redis://foo@localhost'),
array('redis://localhost/123'),
);
}
/**
* @dataProvider provideInvalidCreateConnection
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
* @expectedExceptionMessage Invalid Redis DSN
*/
public function testInvalidCreateConnection($dsn)
{
RedisCache::createConnection($dsn);
}
public function provideInvalidCreateConnection()
{
return array(
array('foo://localhost'),
array('redis://'),
);
}
}

View File

@ -0,0 +1,170 @@
<?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\FilesystemCache;
use Symfony\Component\Cache\Simple\TraceableCache;
/**
* @group time-sensitive
*/
class TraceableCacheTest extends CacheTestCase
{
public function createSimpleCache($defaultLifetime = 0)
{
return new TraceableCache(new FilesystemCache('', $defaultLifetime));
}
public function testGetMiss()
{
$pool = $this->createSimpleCache();
$pool->get('k');
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('get', $call->name);
$this->assertSame(array('key' => 'k', 'default' => null), $call->arguments);
$this->assertSame(0, $call->hits);
$this->assertSame(1, $call->misses);
$this->assertNull($call->result);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testGetHit()
{
$pool = $this->createSimpleCache();
$pool->set('k', 'foo');
$pool->get('k');
$calls = $pool->getCalls();
$this->assertCount(2, $calls);
$call = $calls[1];
$this->assertSame(1, $call->hits);
$this->assertSame(0, $call->misses);
}
public function testGetMultipleMiss()
{
$pool = $this->createSimpleCache();
$arg = array('k0', 'k1');
$values = $pool->getMultiple($arg);
foreach ($values as $value) {
}
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('getMultiple', $call->name);
$this->assertSame(array('keys' => $arg, 'default' => null), $call->arguments);
$this->assertSame(2, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testHasMiss()
{
$pool = $this->createSimpleCache();
$pool->has('k');
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('has', $call->name);
$this->assertSame(array('key' => 'k'), $call->arguments);
$this->assertFalse($call->result);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testHasHit()
{
$pool = $this->createSimpleCache();
$pool->set('k', 'foo');
$pool->has('k');
$calls = $pool->getCalls();
$this->assertCount(2, $calls);
$call = $calls[1];
$this->assertSame('has', $call->name);
$this->assertSame(array('key' => 'k'), $call->arguments);
$this->assertTrue($call->result);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testDelete()
{
$pool = $this->createSimpleCache();
$pool->delete('k');
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('delete', $call->name);
$this->assertSame(array('key' => 'k'), $call->arguments);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testDeleteMultiple()
{
$pool = $this->createSimpleCache();
$arg = array('k0', 'k1');
$pool->deleteMultiple($arg);
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('deleteMultiple', $call->name);
$this->assertSame(array('keys' => $arg), $call->arguments);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testSet()
{
$pool = $this->createSimpleCache();
$pool->set('k', 'foo');
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('set', $call->name);
$this->assertSame(array('key' => 'k', 'value' => 'foo', 'ttl' => null), $call->arguments);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
public function testSetMultiple()
{
$pool = $this->createSimpleCache();
$pool->setMultiple(array('k' => 'foo'));
$calls = $pool->getCalls();
$this->assertCount(1, $calls);
$call = $calls[0];
$this->assertSame('setMultiple', $call->name);
$this->assertSame(array('values' => array('k' => 'foo'), 'ttl' => null), $call->arguments);
$this->assertSame(0, $call->hits);
$this->assertSame(0, $call->misses);
$this->assertNotEmpty($call->start);
$this->assertNotEmpty($call->end);
}
}

View File

@ -26,7 +26,7 @@ trait ApcuTrait
return function_exists('apcu_fetch') && ini_get('apc.enabled') && !('cli' === PHP_SAPI && !ini_get('apc.enable_cli'));
}
public function __construct($namespace = '', $defaultLifetime = 0, $version = null)
private function init($namespace, $defaultLifetime, $version)
{
if (!static::isSupported()) {
throw new CacheException('APCu is not enabled');

View File

@ -11,8 +11,6 @@
namespace Symfony\Component\Cache\Traits;
use Doctrine\Common\Cache\CacheProvider;
/**
* @author Nicolas Grekas <p@tchwork.com>
*
@ -22,13 +20,6 @@ trait DoctrineTrait
{
private $provider;
public function __construct(CacheProvider $provider, $namespace = '', $defaultLifetime = 0)
{
parent::__construct('', $defaultLifetime);
$this->provider = $provider;
$provider->setNamespace($namespace);
}
/**
* {@inheritdoc}
*/

View File

@ -22,12 +22,6 @@ trait FilesystemTrait
{
use FilesystemCommonTrait;
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
}
/**
* {@inheritdoc}
*/

View File

@ -35,7 +35,7 @@ trait MemcachedTrait
return extension_loaded('memcached') && version_compare(phpversion('memcached'), '2.2.0', '>=');
}
public function __construct(\Memcached $client, $namespace = '', $defaultLifetime = 0)
private function init(\Memcached $client, $namespace, $defaultLifetime)
{
if (!static::isSupported()) {
throw new CacheException('Memcached >= 2.2.0 is required');

View File

@ -35,33 +35,7 @@ trait PdoTrait
private $password = '';
private $connectionOptions = array();
/**
* Constructor.
*
* 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.
*
* 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: array()]
*
* @param \PDO|Connection|string $connOrDsn A \PDO or Connection instance or DSN string or null
* @param string $namespace
* @param int $defaultLifetime
* @param array $options An associative array of options
*
* @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, $namespace = '', $defaultLifetime = 0, array $options = array())
private function init($connOrDsn, $namespace, $defaultLifetime, array $options)
{
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]));

View File

@ -31,18 +31,6 @@ trait PhpFilesTrait
return function_exists('opcache_compile_file') && ini_get('opcache.enable');
}
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
{
if (!static::isSupported()) {
throw new CacheException('OPcache is not enabled');
}
parent::__construct('', $defaultLifetime);
$this->init($namespace, $directory);
$e = new \Exception();
$this->includeHandler = function () use ($e) { throw $e; };
}
/**
* {@inheritdoc}
*/

View File

@ -37,7 +37,7 @@ trait RedisTrait
/**
* @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient
*/
public function __construct($redisClient, $namespace = '', $defaultLifetime = 0)
public function init($redisClient, $namespace = '', $defaultLifetime = 0)
{
parent::__construct($namespace, $defaultLifetime);