From 99ae9d6a35d2094946f37b4e7094fd5b619770dd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Tue, 3 Jan 2017 19:50:14 +0100 Subject: [PATCH] [Cache] Move adapter implementations to traits --- .../Cache/Adapter/AbstractAdapter.php | 187 +--------------- .../Component/Cache/Adapter/ArrayAdapter.php | 82 +------ .../Cache/Adapter/PhpArrayAdapter.php | 109 +-------- .../Component/Cache/Traits/AbstractTrait.php | 209 ++++++++++++++++++ .../ApcuAdapter.php => Traits/ApcuTrait.php} | 6 +- .../Component/Cache/Traits/ArrayTrait.php | 100 +++++++++ .../DoctrineTrait.php} | 6 +- .../FilesystemCommonTrait.php} | 4 +- .../FilesystemTrait.php} | 8 +- .../MemcachedTrait.php} | 8 +- .../PdoAdapter.php => Traits/PdoTrait.php} | 9 +- .../Component/Cache/Traits/PhpArrayTrait.php | 131 +++++++++++ .../PhpFilesTrait.php} | 8 +- .../RedisTrait.php} | 6 +- 14 files changed, 481 insertions(+), 392 deletions(-) create mode 100644 src/Symfony/Component/Cache/Traits/AbstractTrait.php rename src/Symfony/Component/Cache/{Adapter/ApcuAdapter.php => Traits/ApcuTrait.php} (96%) create mode 100644 src/Symfony/Component/Cache/Traits/ArrayTrait.php rename src/Symfony/Component/Cache/{Adapter/DoctrineAdapter.php => Traits/DoctrineTrait.php} (96%) rename src/Symfony/Component/Cache/{Adapter/FilesystemAdapterTrait.php => Traits/FilesystemCommonTrait.php} (97%) rename src/Symfony/Component/Cache/{Adapter/FilesystemAdapter.php => Traits/FilesystemTrait.php} (94%) rename src/Symfony/Component/Cache/{Adapter/MemcachedAdapter.php => Traits/MemcachedTrait.php} (98%) rename src/Symfony/Component/Cache/{Adapter/PdoAdapter.php => Traits/PdoTrait.php} (99%) create mode 100644 src/Symfony/Component/Cache/Traits/PhpArrayTrait.php rename src/Symfony/Component/Cache/{Adapter/PhpFilesAdapter.php => Traits/PhpFilesTrait.php} (97%) rename src/Symfony/Component/Cache/{Adapter/RedisAdapter.php => Traits/RedisTrait.php} (99%) diff --git a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php index 2134a0efb7..c761b9a201 100644 --- a/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php @@ -13,31 +13,24 @@ namespace Symfony\Component\Cache\Adapter; 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 */ 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).':'; @@ -130,52 +123,6 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface 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} */ @@ -225,87 +172,6 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface 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} */ @@ -394,47 +260,6 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface } } - /** - * 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; @@ -453,12 +278,4 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface yield $key => $f($key, null, false); } } - - /** - * @internal - */ - public static function handleUnserializeCallback($class) - { - throw new \DomainException('Class not found: '.$class); - } } diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php index 2898ba50cd..45c19c7a6c 100644 --- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php @@ -13,19 +13,16 @@ namespace Symfony\Component\Cache\Adapter; use Psr\Cache\CacheItemInterface; use Psr\Log\LoggerAwareInterface; -use Psr\Log\LoggerAwareTrait; use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Traits\ArrayTrait; /** * @author Nicolas Grekas */ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface { - use LoggerAwareTrait; + use ArrayTrait; - private $storeSerialized; - private $values = array(); - private $expiries = array(); private $createCacheItem; /** @@ -86,49 +83,7 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface CacheItem::validateKey($key); } - return $this->generateItems($keys, time()); - } - - /** - * Returns all cached values, with cache miss as null. - * - * @return array - */ - public function getValues() - { - return $this->values; - } - - /** - * {@inheritdoc} - */ - public function hasItem($key) - { - CacheItem::validateKey($key); - - return isset($this->expiries[$key]) && ($this->expiries[$key] >= time() || !$this->deleteItem($key)); - } - - /** - * {@inheritdoc} - */ - public function clear() - { - $this->values = $this->expiries = array(); - - return true; - } - - /** - * {@inheritdoc} - */ - public function deleteItem($key) - { - CacheItem::validateKey($key); - - unset($this->values[$key], $this->expiries[$key]); - - return true; + return $this->generateItems($keys, time(), $this->createCacheItem); } /** @@ -196,35 +151,4 @@ class ArrayAdapter implements AdapterInterface, LoggerAwareInterface { return true; } - - private function generateItems(array $keys, $now) - { - $f = $this->createCacheItem; - - foreach ($keys as $i => $key) { - try { - if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] >= $now || !$this->deleteItem($key))) { - $this->values[$key] = $value = null; - } elseif (!$this->storeSerialized) { - $value = $this->values[$key]; - } elseif ('b:0;' === $value = $this->values[$key]) { - $value = false; - } elseif (false === $value = unserialize($value)) { - $this->values[$key] = $value = null; - $isHit = false; - } - } catch (\Exception $e) { - CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e)); - $this->values[$key] = $value = null; - $isHit = false; - } - unset($keys[$i]); - - yield $key => $f($key, $value, $isHit); - } - - foreach ($keys as $key) { - yield $key => $f($key, null, false); - } - } } diff --git a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php index e4d8ad5eea..ead0213864 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/PhpArrayAdapter.php @@ -15,6 +15,7 @@ use Psr\Cache\CacheItemInterface; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Cache\CacheItem; 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. @@ -25,10 +26,9 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; */ class PhpArrayAdapter implements AdapterInterface { - private $file; - private $values; + use PhpArrayTrait; + private $createCacheItem; - private $fallbackPool; /** * @param string $file The PHP file were values are cached @@ -75,89 +75,6 @@ class PhpArrayAdapter implements AdapterInterface return $fallbackPool; } - /** - * Store an array of cached values. - * - * @param array $values The cached values - */ - public function warmUp(array $values) - { - if (file_exists($this->file)) { - if (!is_file($this->file)) { - throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s.', $this->file)); - } - - if (!is_writable($this->file)) { - throw new InvalidArgumentException(sprintf('Cache file is not writable: %s.', $this->file)); - } - } else { - $directory = dirname($this->file); - - if (!is_dir($directory) && !@mkdir($directory, 0777, true)) { - throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s.', $directory)); - } - - if (!is_writable($directory)) { - throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s.', $directory)); - } - } - - $dump = <<<'EOF' - $value) { - CacheItem::validateKey(is_int($key) ? (string) $key : $key); - - if (null === $value || is_object($value)) { - try { - $value = serialize($value); - } catch (\Exception $e) { - throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, get_class($value)), 0, $e); - } - } elseif (is_array($value)) { - try { - $serialized = serialize($value); - $unserialized = unserialize($serialized); - } catch (\Exception $e) { - throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable array value.', $key), 0, $e); - } - // Store arrays serialized if they contain any objects or references - if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) { - $value = $serialized; - } - } elseif (is_string($value)) { - // Serialize strings if they could be confused with serialized objects or arrays - if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { - $value = serialize($value); - } - } elseif (!is_scalar($value)) { - throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value))); - } - - $dump .= var_export($key, true).' => '.var_export($value, true).",\n"; - } - - $dump .= "\n);\n"; - $dump = str_replace("' . \"\\0\" . '", "\0", $dump); - - $tmpFile = uniqid($this->file, true); - - file_put_contents($tmpFile, $dump); - @chmod($tmpFile, 0666); - unset($serialized, $unserialized, $value, $dump); - - @rename($tmpFile, $this->file); - - $this->values = (include $this->file) ?: array(); - } - /** * {@inheritdoc} */ @@ -228,18 +145,6 @@ EOF; return isset($this->values[$key]) || $this->fallbackPool->hasItem($key); } - /** - * {@inheritdoc} - */ - public function clear() - { - $this->values = array(); - - $cleared = @unlink($this->file) || !file_exists($this->file); - - return $this->fallbackPool->clear() && $cleared; - } - /** * {@inheritdoc} */ @@ -317,14 +222,6 @@ EOF; return $this->fallbackPool->commit(); } - /** - * Load the cache file. - */ - private function initialize() - { - $this->values = file_exists($this->file) ? (include $this->file ?: array()) : array(); - } - /** * Generator for items. * diff --git a/src/Symfony/Component/Cache/Traits/AbstractTrait.php b/src/Symfony/Component/Cache/Traits/AbstractTrait.php new file mode 100644 index 0000000000..375ccf7620 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/AbstractTrait.php @@ -0,0 +1,209 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait AbstractTrait +{ + use LoggerAwareTrait; + + private $namespace; + private $deferred = array(); + + /** + * @var int|null The maximum length to enforce for identifiers or null when no limit applies + */ + protected $maxIdLength; + + /** + * 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} + */ + 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; + } + + /** + * 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; + } + + /** + * @internal + */ + public static function handleUnserializeCallback($class) + { + throw new \DomainException('Class not found: '.$class); + } +} diff --git a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php b/src/Symfony/Component/Cache/Traits/ApcuTrait.php similarity index 96% rename from src/Symfony/Component/Cache/Adapter/ApcuAdapter.php rename to src/Symfony/Component/Cache/Traits/ApcuTrait.php index 67afd5c72a..f0ca04d76b 100644 --- a/src/Symfony/Component/Cache/Adapter/ApcuAdapter.php +++ b/src/Symfony/Component/Cache/Traits/ApcuTrait.php @@ -9,15 +9,17 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\Exception\CacheException; /** * @author Nicolas Grekas + * + * @internal */ -class ApcuAdapter extends AbstractAdapter +trait ApcuTrait { public static function isSupported() { diff --git a/src/Symfony/Component/Cache/Traits/ArrayTrait.php b/src/Symfony/Component/Cache/Traits/ArrayTrait.php new file mode 100644 index 0000000000..3fb5fa36be --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/ArrayTrait.php @@ -0,0 +1,100 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Psr\Log\LoggerAwareTrait; +use Symfony\Component\Cache\CacheItem; + +/** + * @author Nicolas Grekas + * + * @internal + */ +trait ArrayTrait +{ + use LoggerAwareTrait; + + private $storeSerialized; + private $values = array(); + private $expiries = array(); + + /** + * Returns all cached values, with cache miss as null. + * + * @return array + */ + public function getValues() + { + return $this->values; + } + + /** + * {@inheritdoc} + */ + public function hasItem($key) + { + CacheItem::validateKey($key); + + return isset($this->expiries[$key]) && ($this->expiries[$key] >= time() || !$this->deleteItem($key)); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->values = $this->expiries = array(); + + return true; + } + + /** + * {@inheritdoc} + */ + public function deleteItem($key) + { + CacheItem::validateKey($key); + + unset($this->values[$key], $this->expiries[$key]); + + return true; + } + + private function generateItems(array $keys, $now, $f) + { + foreach ($keys as $i => $key) { + try { + if (!$isHit = isset($this->expiries[$key]) && ($this->expiries[$key] >= $now || !$this->deleteItem($key))) { + $this->values[$key] = $value = null; + } elseif (!$this->storeSerialized) { + $value = $this->values[$key]; + } elseif ('b:0;' === $value = $this->values[$key]) { + $value = false; + } elseif (false === $value = unserialize($value)) { + $this->values[$key] = $value = null; + $isHit = false; + } + } catch (\Exception $e) { + CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e)); + $this->values[$key] = $value = null; + $isHit = false; + } + unset($keys[$i]); + + yield $key => $f($key, $value, $isHit); + } + + foreach ($keys as $key) { + yield $key => $f($key, null, false); + } + } +} diff --git a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php b/src/Symfony/Component/Cache/Traits/DoctrineTrait.php similarity index 96% rename from src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php rename to src/Symfony/Component/Cache/Traits/DoctrineTrait.php index ed91bf56cd..3655af3bcf 100644 --- a/src/Symfony/Component/Cache/Adapter/DoctrineAdapter.php +++ b/src/Symfony/Component/Cache/Traits/DoctrineTrait.php @@ -9,14 +9,16 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Doctrine\Common\Cache\CacheProvider; /** * @author Nicolas Grekas + * + * @internal */ -class DoctrineAdapter extends AbstractAdapter +trait DoctrineTrait { private $provider; diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php similarity index 97% rename from src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php rename to src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php index 156fc5c1fb..f9c9b396fc 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapterTrait.php +++ b/src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Symfony\Component\Cache\Exception\InvalidArgumentException; @@ -18,7 +18,7 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; * * @internal */ -trait FilesystemAdapterTrait +trait FilesystemCommonTrait { private $directory; private $tmp; diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Traits/FilesystemTrait.php similarity index 94% rename from src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php rename to src/Symfony/Component/Cache/Traits/FilesystemTrait.php index 1c62641cf6..a06c964adb 100644 --- a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php +++ b/src/Symfony/Component/Cache/Traits/FilesystemTrait.php @@ -9,16 +9,18 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Symfony\Component\Cache\Exception\CacheException; /** * @author Nicolas Grekas + * + * @internal */ -class FilesystemAdapter extends AbstractAdapter +trait FilesystemTrait { - use FilesystemAdapterTrait; + use FilesystemCommonTrait; public function __construct($namespace = '', $defaultLifetime = 0, $directory = null) { diff --git a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php similarity index 98% rename from src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php rename to src/Symfony/Component/Cache/Traits/MemcachedTrait.php index 46b523f726..957595e2cb 100644 --- a/src/Symfony/Component/Cache/Adapter/MemcachedAdapter.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\InvalidArgumentException; @@ -17,8 +17,10 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; /** * @author Rob Frawley 2nd * @author Nicolas Grekas + * + * @internal */ -class MemcachedAdapter extends AbstractAdapter +trait MemcachedTrait { private static $defaultClientOptions = array( 'persistent_id' => null, @@ -26,8 +28,6 @@ class MemcachedAdapter extends AbstractAdapter 'password' => null, ); - protected $maxIdLength = 250; - private $client; public static function isSupported() diff --git a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php b/src/Symfony/Component/Cache/Traits/PdoTrait.php similarity index 99% rename from src/Symfony/Component/Cache/Adapter/PdoAdapter.php rename to src/Symfony/Component/Cache/Traits/PdoTrait.php index 3fa3a40533..90796e0153 100644 --- a/src/Symfony/Component/Cache/Adapter/PdoAdapter.php +++ b/src/Symfony/Component/Cache/Traits/PdoTrait.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\ServerInfoAwareConnection; @@ -17,10 +17,11 @@ use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Schema\Schema; use Symfony\Component\Cache\Exception\InvalidArgumentException; -class PdoAdapter extends AbstractAdapter +/** + * @internal + */ +trait PdoTrait { - protected $maxIdLength = 255; - private $conn; private $dsn; private $driver; diff --git a/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php new file mode 100644 index 0000000000..97a923bfe1 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/PhpArrayTrait.php @@ -0,0 +1,131 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Traits; + +use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Titouan Galopin + * @author Nicolas Grekas + * + * @internal + */ +trait PhpArrayTrait +{ + private $file; + private $values; + private $fallbackPool; + + /** + * Store an array of cached values. + * + * @param array $values The cached values + */ + public function warmUp(array $values) + { + if (file_exists($this->file)) { + if (!is_file($this->file)) { + throw new InvalidArgumentException(sprintf('Cache path exists and is not a file: %s.', $this->file)); + } + + if (!is_writable($this->file)) { + throw new InvalidArgumentException(sprintf('Cache file is not writable: %s.', $this->file)); + } + } else { + $directory = dirname($this->file); + + if (!is_dir($directory) && !@mkdir($directory, 0777, true)) { + throw new InvalidArgumentException(sprintf('Cache directory does not exist and cannot be created: %s.', $directory)); + } + + if (!is_writable($directory)) { + throw new InvalidArgumentException(sprintf('Cache directory is not writable: %s.', $directory)); + } + } + + $dump = <<<'EOF' + $value) { + CacheItem::validateKey(is_int($key) ? (string) $key : $key); + + if (null === $value || is_object($value)) { + try { + $value = serialize($value); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, get_class($value)), 0, $e); + } + } elseif (is_array($value)) { + try { + $serialized = serialize($value); + $unserialized = unserialize($serialized); + } catch (\Exception $e) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable array value.', $key), 0, $e); + } + // Store arrays serialized if they contain any objects or references + if ($unserialized !== $value || (false !== strpos($serialized, ';R:') && preg_match('/;R:[1-9]/', $serialized))) { + $value = $serialized; + } + } elseif (is_string($value)) { + // Serialize strings if they could be confused with serialized objects or arrays + if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) { + $value = serialize($value); + } + } elseif (!is_scalar($value)) { + throw new InvalidArgumentException(sprintf('Cache key "%s" has non-serializable %s value.', $key, gettype($value))); + } + + $dump .= var_export($key, true).' => '.var_export($value, true).",\n"; + } + + $dump .= "\n);\n"; + $dump = str_replace("' . \"\\0\" . '", "\0", $dump); + + $tmpFile = uniqid($this->file, true); + + file_put_contents($tmpFile, $dump); + @chmod($tmpFile, 0666); + unset($serialized, $unserialized, $value, $dump); + + @rename($tmpFile, $this->file); + + $this->values = (include $this->file) ?: array(); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->values = array(); + + $cleared = @unlink($this->file) || !file_exists($this->file); + + return $this->fallbackPool->clear() && $cleared; + } + + /** + * Load the cache file. + */ + private function initialize() + { + $this->values = @(include $this->file) ?: array(); + } +} diff --git a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php similarity index 97% rename from src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php rename to src/Symfony/Component/Cache/Traits/PhpFilesTrait.php index befa38d8d4..d83587e3bc 100644 --- a/src/Symfony/Component/Cache/Adapter/PhpFilesAdapter.php +++ b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\InvalidArgumentException; @@ -17,10 +17,12 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; /** * @author Piotr Stankowski * @author Nicolas Grekas + * + * @internal */ -class PhpFilesAdapter extends AbstractAdapter +trait PhpFilesTrait { - use FilesystemAdapterTrait; + use FilesystemCommonTrait; private $includeHandler; diff --git a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php similarity index 99% rename from src/Symfony/Component/Cache/Adapter/RedisAdapter.php rename to src/Symfony/Component/Cache/Traits/RedisTrait.php index 7fd6921e3f..4ca468a68f 100644 --- a/src/Symfony/Component/Cache/Adapter/RedisAdapter.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\Cache\Adapter; +namespace Symfony\Component\Cache\Traits; use Predis\Connection\Factory; use Predis\Connection\Aggregate\PredisCluster; @@ -19,8 +19,10 @@ use Symfony\Component\Cache\Exception\InvalidArgumentException; /** * @author Aurimas Niekis * @author Nicolas Grekas + * + * @internal */ -class RedisAdapter extends AbstractAdapter +trait RedisTrait { private static $defaultConnectionOptions = array( 'class' => null,