From 9a94e6cfd139135864a1d1f20ff918a501703c6a Mon Sep 17 00:00:00 2001 From: Aleksey Prilipko Date: Fri, 1 Jun 2018 15:10:10 +1000 Subject: [PATCH] cache-serializer --- .../Component/Cache/Adapter/ArrayAdapter.php | 29 ++++---- .../Cache/Serializer/IdentitySerializer.php | 27 +++++++ .../Cache/Serializer/IgbinarySerializer.php | 43 +++++++++++ .../Cache/Serializer/PhpSerializer.php | 49 +++++++++++++ .../Component/Cache/SerializerInterface.php | 40 +++++++++++ .../Component/Cache/Simple/ArrayCache.php | 22 +++--- .../Serializer/IgbinarySerializerTest.php | 31 ++++++++ .../Tests/Serializer/PhpSerializerTest.php | 23 ++++++ .../Cache/Tests/Serializer/SerializerTest.php | 72 +++++++++++++++++++ .../Component/Cache/Traits/AbstractTrait.php | 35 --------- .../Component/Cache/Traits/ArrayTrait.php | 11 +-- .../Cache/Traits/FilesystemTrait.php | 16 ++++- .../Component/Cache/Traits/MemcachedTrait.php | 5 +- .../Component/Cache/Traits/PdoTrait.php | 10 ++- .../Component/Cache/Traits/PhpFilesTrait.php | 32 ++++++++- .../Component/Cache/Traits/RedisTrait.php | 8 ++- .../Cache/Traits/SerializerTrait.php | 68 ++++++++++++++++++ 17 files changed, 437 insertions(+), 84 deletions(-) create mode 100644 src/Symfony/Component/Cache/Serializer/IdentitySerializer.php create mode 100644 src/Symfony/Component/Cache/Serializer/IgbinarySerializer.php create mode 100644 src/Symfony/Component/Cache/Serializer/PhpSerializer.php create mode 100644 src/Symfony/Component/Cache/SerializerInterface.php create mode 100644 src/Symfony/Component/Cache/Tests/Serializer/IgbinarySerializerTest.php create mode 100644 src/Symfony/Component/Cache/Tests/Serializer/PhpSerializerTest.php create mode 100644 src/Symfony/Component/Cache/Tests/Serializer/SerializerTest.php create mode 100644 src/Symfony/Component/Cache/Traits/SerializerTrait.php diff --git a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php index 07fc198115a50..2f27e7881865c 100644 --- a/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php +++ b/src/Symfony/Component/Cache/Adapter/ArrayAdapter.php @@ -15,6 +15,8 @@ use Psr\Log\LoggerAwareInterface; use Symfony\Component\Cache\CacheInterface; use Symfony\Component\Cache\CacheItem; +use Symfony\Component\Cache\Serializer\IdentitySerializer; +use Symfony\Component\Cache\Serializer\PhpSerializer; use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\Traits\ArrayTrait; use Symfony\Component\Cache\Traits\GetTrait; @@ -35,7 +37,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter */ public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true) { - $this->storeSerialized = $storeSerialized; + $this->setSerializer($storeSerialized ? new PhpSerializer() : new IdentitySerializer()); $this->createCacheItem = \Closure::bind( function ($key, $value, $isHit) use ($defaultLifetime) { $item = new CacheItem(); @@ -60,13 +62,8 @@ public function getItem($key) try { if (!$isHit) { $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; + } else { + $value = $this->unserialize($this->values[$key]); } } catch (\Exception $e) { CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e)); @@ -120,15 +117,13 @@ public function save(CacheItemInterface $item) return true; } - if ($this->storeSerialized) { - try { - $value = 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; - } + try { + $value = $this->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; } if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) { $expiry = microtime(true) + $item["\0*\0defaultLifetime"]; diff --git a/src/Symfony/Component/Cache/Serializer/IdentitySerializer.php b/src/Symfony/Component/Cache/Serializer/IdentitySerializer.php new file mode 100644 index 0000000000000..266da75780f24 --- /dev/null +++ b/src/Symfony/Component/Cache/Serializer/IdentitySerializer.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Serializer; + +use Symfony\Component\Cache\SerializerInterface; + +class IdentitySerializer implements SerializerInterface +{ + public function serialize($data) + { + return $data; + } + + public function unserialize($serialized) + { + return $serialized; + } +} diff --git a/src/Symfony/Component/Cache/Serializer/IgbinarySerializer.php b/src/Symfony/Component/Cache/Serializer/IgbinarySerializer.php new file mode 100644 index 0000000000000..ffa8bf2939196 --- /dev/null +++ b/src/Symfony/Component/Cache/Serializer/IgbinarySerializer.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Serializer; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\SerializerInterface; + +class IgbinarySerializer implements SerializerInterface +{ + public function serialize($data) + { + return igbinary_serialize($data); + } + + public function unserialize($serialized) + { + $unserializeCallbackHandler = ini_set( + 'unserialize_callback_func', + PhpSerializer::class.'::handleUnserializeCallback' + ); + try { + $value = igbinary_unserialize($serialized); + if (false === $value && igbinary_serialize(false) !== $serialized) { + throw new CacheException('failed to unserialize value'); + } + + return $value; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } +} diff --git a/src/Symfony/Component/Cache/Serializer/PhpSerializer.php b/src/Symfony/Component/Cache/Serializer/PhpSerializer.php new file mode 100644 index 0000000000000..74ce4946af159 --- /dev/null +++ b/src/Symfony/Component/Cache/Serializer/PhpSerializer.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Serializer; + +use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\SerializerInterface; + +class PhpSerializer implements SerializerInterface +{ + public function serialize($data) + { + return serialize($data); + } + + public function unserialize($serialized) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + try { + if ('b:0;' === $serialized) { + return false; + } elseif (false === $value = unserialize($serialized)) { + throw new CacheException('failed to unserialize value'); + } + + return $value; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } + + /** + * @internal + */ + public static function handleUnserializeCallback($class) + { + throw new \DomainException('Class not found: '.$class); + } +} diff --git a/src/Symfony/Component/Cache/SerializerInterface.php b/src/Symfony/Component/Cache/SerializerInterface.php new file mode 100644 index 0000000000000..6235f5fcdfb7c --- /dev/null +++ b/src/Symfony/Component/Cache/SerializerInterface.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache; + +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Alexei Prilipko + */ +interface SerializerInterface +{ + /** + * Generates a storable representation of a value. + * + * @param $data mixed + * + * @return string|mixed serialized value + * + * @throws InvalidArgumentException when $data can not be serialized + */ + public function serialize($data); + + /** + * Creates a PHP value from a stored representation. + * + * @param string|mixed $serialized the serialized string + * + * @return mixed Original value + */ + public function unserialize($serialized); +} diff --git a/src/Symfony/Component/Cache/Simple/ArrayCache.php b/src/Symfony/Component/Cache/Simple/ArrayCache.php index 6cef8b6e745c4..5271b568b26cb 100644 --- a/src/Symfony/Component/Cache/Simple/ArrayCache.php +++ b/src/Symfony/Component/Cache/Simple/ArrayCache.php @@ -15,6 +15,8 @@ use Psr\SimpleCache\CacheInterface; use Symfony\Component\Cache\CacheItem; use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Serializer\IdentitySerializer; +use Symfony\Component\Cache\Serializer\PhpSerializer; use Symfony\Component\Cache\ResettableInterface; use Symfony\Component\Cache\Traits\ArrayTrait; @@ -37,7 +39,7 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true) { $this->defaultLifetime = $defaultLifetime; - $this->storeSerialized = $storeSerialized; + $this->setSerializer($storeSerialized ? new PhpSerializer() : new IdentitySerializer()); } /** @@ -109,16 +111,14 @@ public function setMultiple($values, $ttl = null) 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; - } + foreach ($valuesArray as $key => $value) { + try { + $valuesArray[$key] = $this->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 ? microtime(true) + $ttl : PHP_INT_MAX; diff --git a/src/Symfony/Component/Cache/Tests/Serializer/IgbinarySerializerTest.php b/src/Symfony/Component/Cache/Tests/Serializer/IgbinarySerializerTest.php new file mode 100644 index 0000000000000..5d6502e255e8c --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Serializer/IgbinarySerializerTest.php @@ -0,0 +1,31 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Serializer; + +use Symfony\Component\Cache\Serializer\IgbinarySerializer; +use Symfony\Component\Cache\SerializerInterface; + +class IgbinarySerializerTest extends SerializerTest +{ + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + if (!extension_loaded('igbinary')) { + self::markTestSkipped('Extension igbinary is not loaded.'); + } + } + + protected function createSerializer(): SerializerInterface + { + return new IgbinarySerializer(); + } +} diff --git a/src/Symfony/Component/Cache/Tests/Serializer/PhpSerializerTest.php b/src/Symfony/Component/Cache/Tests/Serializer/PhpSerializerTest.php new file mode 100644 index 0000000000000..d2099f2f1c77c --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Serializer/PhpSerializerTest.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Serializer; + +use Symfony\Component\Cache\Serializer\PhpSerializer; +use Symfony\Component\Cache\SerializerInterface; + +class PhpSerializerTest extends SerializerTest +{ + protected function createSerializer(): SerializerInterface + { + return new PhpSerializer(); + } +} diff --git a/src/Symfony/Component/Cache/Tests/Serializer/SerializerTest.php b/src/Symfony/Component/Cache/Tests/Serializer/SerializerTest.php new file mode 100644 index 0000000000000..5258d89abc25b --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Serializer/SerializerTest.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Serializer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\SerializerInterface; + +abstract class SerializerTest extends TestCase +{ + /** @var SerializerInterface */ + private $serializer; + + protected function setUp() + { + parent::setUp(); + + $this->serializer = $this->createSerializer(); + } + + abstract protected function createSerializer(): SerializerInterface; + + /** + * @dataProvider validData + */ + public function testSerializeInvariants($value) + { + $serialized = $this->serializer->serialize($value); + $restored = $this->serializer->unserialize($serialized); + $this->assertEquals($value, $restored); + } + + /** + * Data provider for valid data to store. + * + * @return array + */ + public static function validData() + { + return array( + array(false), + array('AbC19_.'), + array(4711), + array(47.11), + array(true), + array(null), + array(array('key' => 'value')), + array(new \stdClass()), + ); + } +} + +class NotUnserializable implements \Serializable +{ + public function serialize() + { + return serialize(123); + } + + public function unserialize($ser) + { + throw new \Exception(__CLASS__); + } +} diff --git a/src/Symfony/Component/Cache/Traits/AbstractTrait.php b/src/Symfony/Component/Cache/Traits/AbstractTrait.php index 60a9e77abac48..e7b146eef5159 100644 --- a/src/Symfony/Component/Cache/Traits/AbstractTrait.php +++ b/src/Symfony/Component/Cache/Traits/AbstractTrait.php @@ -202,33 +202,6 @@ public function reset() $this->ids = array(); } - /** - * 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) { if ($this->versioningIsEnabled && '' === $this->namespaceVersion) { @@ -255,12 +228,4 @@ private function getId($key) return $id; } - - /** - * @internal - */ - public static function handleUnserializeCallback($class) - { - throw new \DomainException('Class not found: '.$class); - } } diff --git a/src/Symfony/Component/Cache/Traits/ArrayTrait.php b/src/Symfony/Component/Cache/Traits/ArrayTrait.php index 86ad17e9c52e6..aa07bfa561965 100644 --- a/src/Symfony/Component/Cache/Traits/ArrayTrait.php +++ b/src/Symfony/Component/Cache/Traits/ArrayTrait.php @@ -22,8 +22,8 @@ trait ArrayTrait { use LoggerAwareTrait; + use SerializerTrait; - private $storeSerialized; private $values = array(); private $expiries = array(); @@ -83,13 +83,8 @@ private function generateItems(array $keys, $now, $f) 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; + } else { + $value = $this->unserialize($this->values[$key]); } } catch (\Exception $e) { CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e)); diff --git a/src/Symfony/Component/Cache/Traits/FilesystemTrait.php b/src/Symfony/Component/Cache/Traits/FilesystemTrait.php index 23974b3bc5487..b5f6e401196e4 100644 --- a/src/Symfony/Component/Cache/Traits/FilesystemTrait.php +++ b/src/Symfony/Component/Cache/Traits/FilesystemTrait.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Cache\Traits; use Symfony\Component\Cache\Exception\CacheException; +use Symfony\Component\Cache\Serializer\PhpSerializer; /** * @author Nicolas Grekas @@ -21,7 +22,16 @@ */ trait FilesystemTrait { - use FilesystemCommonTrait; + use FilesystemCommonTrait { + init as initFsCommon; + } + use SerializerTrait; + + private function init($namespace, $directory) + { + $this->initFsCommon($namespace, $directory); + $this->setSerializer(new PhpSerializer()); + } /** * @return bool @@ -68,7 +78,7 @@ protected function doFetch(array $ids) $value = stream_get_contents($h); fclose($h); if ($i === $id) { - $values[$id] = parent::unserialize($value); + $values[$id] = $this->unserialize($value); } } } @@ -95,7 +105,7 @@ protected function doSave(array $values, $lifetime) $expiresAt = $lifetime ? (time() + $lifetime) : 0; foreach ($values as $id => $value) { - $ok = $this->write($this->getFile($id, true), $expiresAt."\n".rawurlencode($id)."\n".serialize($value), $expiresAt) && $ok; + $ok = $this->write($this->getFile($id, true), $expiresAt."\n".rawurlencode($id)."\n".$this->serialize($value), $expiresAt) && $ok; } if (!$ok && !is_writable($this->directory)) { diff --git a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php index c0d43c2e61ba5..938240475834b 100644 --- a/src/Symfony/Component/Cache/Traits/MemcachedTrait.php +++ b/src/Symfony/Component/Cache/Traits/MemcachedTrait.php @@ -13,6 +13,7 @@ use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Serializer\PhpSerializer; /** * @author Rob Frawley 2nd @@ -211,7 +212,7 @@ protected function doSave(array $values, $lifetime) */ protected function doFetch(array $ids) { - $unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + $unserializeCallbackHandler = ini_set('unserialize_callback_func', PhpSerializer::class.'::handleUnserializeCallback'); try { $encodedIds = array_map('rawurlencode', $ids); @@ -223,8 +224,6 @@ protected function doFetch(array $ids) } return $result; - } catch (\Error $e) { - throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); } finally { ini_set('unserialize_callback_func', $unserializeCallbackHandler); } diff --git a/src/Symfony/Component/Cache/Traits/PdoTrait.php b/src/Symfony/Component/Cache/Traits/PdoTrait.php index a88099ecb183e..0c1f72fd4c3be 100644 --- a/src/Symfony/Component/Cache/Traits/PdoTrait.php +++ b/src/Symfony/Component/Cache/Traits/PdoTrait.php @@ -12,16 +12,19 @@ namespace Symfony\Component\Cache\Traits; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Driver\ServerInfoAwareConnection; use Doctrine\DBAL\Schema\Schema; use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Serializer\PhpSerializer; /** * @internal */ trait PdoTrait { + use SerializerTrait; + private $conn; private $dsn; private $driver; @@ -66,6 +69,7 @@ private function init($connOrDsn, $namespace, $defaultLifetime, array $options) $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions; $this->namespace = $namespace; + $this->setSerializer(new PhpSerializer()); parent::__construct($namespace, $defaultLifetime); } @@ -181,7 +185,7 @@ protected function doFetch(array $ids) if (null === $row[1]) { $expired[] = $row[0]; } else { - yield $row[0] => parent::unserialize(is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); + yield $row[0] => $this->unserialize(is_resource($row[1]) ? stream_get_contents($row[1]) : $row[1]); } } @@ -257,7 +261,7 @@ protected function doSave(array $values, $lifetime) foreach ($values as $id => $value) { try { - $serialized[$id] = serialize($value); + $serialized[$id] = $this->serialize($value); } catch (\Exception $e) { $failed[] = $id; } diff --git a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php index 2c0ff3aef1577..0671d6012be33 100644 --- a/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php +++ b/src/Symfony/Component/Cache/Traits/PhpFilesTrait.php @@ -13,6 +13,7 @@ use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Serializer\PhpSerializer; /** * @author Piotr Stankowski @@ -89,7 +90,7 @@ protected function doFetch(array $ids) if ('N;' === $value) { $values[$id] = null; } elseif (\is_string($value) && isset($value[2]) && ':' === $value[1]) { - $values[$id] = parent::unserialize($value); + $values[$id] = $this->unserialize($value); } } @@ -118,7 +119,7 @@ protected function doSave(array $values, $lifetime) $value = serialize($value); } elseif (\is_array($value)) { $serialized = serialize($value); - $unserialized = parent::unserialize($serialized); + $unserialized = $this->unserialize($serialized); // 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; @@ -157,4 +158,31 @@ protected function doUnlink($file) return @unlink($file); } + + /** + * Like the native unserialize() function but throws an exception if anything goes wrong. + * + * @param string $value + * + * @return mixed + * + * @throws \Exception + */ + protected function unserialize($serialized) + { + $unserializeCallbackHandler = ini_set('unserialize_callback_func', PhpSerializer::class.'::handleUnserializeCallback'); + try { + if ('b:0;' === $serialized) { + return false; + } elseif (false === $value = unserialize($serialized)) { + throw new CacheException('failed to unserialize value'); + } + + return $value; + } catch (\Error $e) { + throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine()); + } finally { + ini_set('unserialize_callback_func', $unserializeCallbackHandler); + } + } } diff --git a/src/Symfony/Component/Cache/Traits/RedisTrait.php b/src/Symfony/Component/Cache/Traits/RedisTrait.php index 9fc6177c6bd78..69a519fe04cb6 100644 --- a/src/Symfony/Component/Cache/Traits/RedisTrait.php +++ b/src/Symfony/Component/Cache/Traits/RedisTrait.php @@ -18,6 +18,7 @@ use Predis\Response\Status; use Symfony\Component\Cache\Exception\CacheException; use Symfony\Component\Cache\Exception\InvalidArgumentException; +use Symfony\Component\Cache\Serializer\PhpSerializer; /** * @author Aurimas Niekis @@ -27,6 +28,8 @@ */ trait RedisTrait { + use SerializerTrait; + private static $defaultConnectionOptions = array( 'class' => null, 'persistent' => 0, @@ -54,6 +57,7 @@ public function init($redisClient, $namespace = '', $defaultLifetime = 0) throw new InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\Client, %s given', __METHOD__, is_object($redisClient) ? get_class($redisClient) : gettype($redisClient))); } $this->redis = $redisClient; + $this->setSerializer(new PhpSerializer()); } /** @@ -177,7 +181,7 @@ protected function doFetch(array $ids) }); foreach ($values as $id => $v) { if ($v) { - yield $id => parent::unserialize($v); + yield $id => $this->unserialize($v); } } } @@ -278,7 +282,7 @@ protected function doSave(array $values, $lifetime) foreach ($values as $id => $value) { try { - $serialized[$id] = serialize($value); + $serialized[$id] = $this->serialize($value); } catch (\Exception $e) { $failed[] = $id; } diff --git a/src/Symfony/Component/Cache/Traits/SerializerTrait.php b/src/Symfony/Component/Cache/Traits/SerializerTrait.php new file mode 100644 index 0000000000000..5538b587db173 --- /dev/null +++ b/src/Symfony/Component/Cache/Traits/SerializerTrait.php @@ -0,0 +1,68 @@ + + * + * 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\SerializerInterface; + +/** + * @author Alexei Prilipko + * + * @internal + */ +trait SerializerTrait +{ + /** @var SerializerInterface */ + private $serializer; + + public function setSerializer(SerializerInterface $serializer) + { + $this->serializer = $serializer; + } + + /** + * Generates a storable representation of a value. + * + * @param $data mixed + * + * @return string|mixed serialized value + */ + protected function serialize($data) + { + return $this->serializer->serialize($data); + } + + /** + * Creates a PHP value from a stored representation. + * + * @param string|mixed $serialized the serialized string + * + * @return mixed Original value + */ + protected function unserialize($serialized) + { + return $this->serializer->unserialize($serialized); + } + + protected function serializeMultiple(iterable $values) + { + foreach ($values as $key => $value) { + yield $key => $this->serialize($value); + } + } + + protected function unserializeMultiple(iterable $serializedValues) + { + foreach ($serializedValues as $key => $value) { + yield $key => $this->unserialize($value); + } + } +}