8000 cache-serializer · symfony/symfony@b279460 · GitHub
[go: up one dir, main page]

Skip to content

Commit b279460

Browse files
author
Aleksey Prilipko
committed
cache-serializer
1 parent 84ada0c commit b279460

17 files changed

+437
-84
lines changed

src/Symfony/Component/Cache/Adapter/ArrayAdapter.php

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use Psr\Log\LoggerAwareInterface;
1616
use Symfony\Component\Cache\CacheInterface;
1717
use Symfony\Component\Cache\CacheItem;
18+
use Symfony\Component\Cache\Serializer\IdentitySerializer;
19+
use Symfony\Component\Cache\Serializer\PhpSerializer;
1820
use Symfony\Component\Cache\ResettableInterface;
1921
use Symfony\Component\Cache\Traits\ArrayTrait;
2022
use Symfony\Component\Cache\Traits\GetTrait;
@@ -35,7 +37,7 @@ class ArrayAdapter implements AdapterInterface, CacheInterface, LoggerAwareInter
3537
*/
3638
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
3739
{
38-
$this->storeSerialized = $storeSerialized;
40+
$this->setSerializer($storeSerialized ? new PhpSerializer() : new IdentitySerializer());
3941
$this->createCacheItem = \Closure::bind(
4042
function ($key, $value, $isHit) use ($defaultLifetime) {
4143
$item = new CacheItem();
@@ -60,13 +62,8 @@ public function getItem($key)
6062
try {
6163
if (!$isHit) {
6264
$this->values[$key] = $value = null;
63-
} elseif (!$this->storeSerialized) {
64-
$value = $this->values[$key];
65-
} elseif ('b:0;' === $value = $this->values[$key]) {
66-
$value = false;
67-
} elseif (false === $value = unserialize($value)) {
68-
$this->values[$key] = $value = null;
69-
$isHit = false;
65+
} else {
66+
$value = $this->unserialize($this->values[$key]);
7067
}
7168
} catch (\Exception $e) {
7269
CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e));
@@ -120,15 +117,13 @@ public function save(CacheItemInterface $item)
120117

121118
return true;
122119
}
123-
if ($this->storeSerialized) {
124-
try {
125-
$value = serialize($value);
126-
} catch (\Exception $e) {
127-
$type = is_object($value) ? get_class($value) : gettype($value);
128-
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e));
129-
130-
return false;
131-
}
120+
try {
121+
$value = $this->serialize($value);
122+
} catch (\Exception $e) {
123+
$type = is_object($value) ? get_class($value) : gettype($value);
124+
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e));
125+
126+
return false;
132127
}
133128
if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) {
134129
$expiry = time() + $item["\0*\0defaultLifetime"];
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Serializer;
13+
14+
use Symfony\Component\Cache\SerializerInterface;
15+
16+
class IdentitySerializer implements SerializerInterface
17+
{
18+
public function serialize($data)
19+
{
20+
return $data;
21+
}
22+
23+
public function unserialize($serialized)
24+
{
25+
return $serialized;
26+
}
27+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Serializer;
13+
14+
use Symfony\Component\Cache\Exception\CacheException;
15+
use Symfony\Component\Cache\SerializerInterface;
16+
17+
class IgbinarySerializer implements SerializerInterface
18+
{
19+
public function serialize($data)
20+
{
21+
return igbinary_serialize($data);
22+
}
23+
24+
public function unserialize($serialized)
25+
{
26+
$unserializeCallbackHandler = ini_set(
27+
'unserialize_callback_func',
28+
PhpSerializer::class.'::handleUnserializeCallback'
29+
);
30+
try {
31+
$value = igbinary_unserialize($serialized);
32+
if (false === $value && igbinary_serialize(false) !== $serialized) {
33+
throw new CacheException('failed to unserialize value');
34+
}
35+
36+
return $value;
37+
} catch (\Error $e) {
38+
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
39+
} finally {
40+
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
41+
}
42+
}
43+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Serializer;
13+
14+
use Symfony\Component\Cache\Exception\CacheException;
15+
use Symfony\Component\Cache\SerializerInterface;
16+
17+
class PhpSerializer implements SerializerInterface
18+
{
19+
public function serialize($data)
20+
{
21+
return serialize($data);
22+
}
23+
24+
public function unserialize($serialized)
25+
{
26+
$unserializeCallbackHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback');
27+
try {
28+
if ('b:0;' === $serialized) {
29+
return false;
30+
} elseif (false === $value = unserialize($serialized)) {
31+
throw new CacheException('failed to unserialize value');
32+
}
33+
34+
return $value;
35+
} catch (\Error $e) {
36+
throw new \ErrorException($e->getMessage(), $e->getCode(), E_ERROR, $e->getFile(), $e->getLine());
37+
} finally {
38+
ini_set('unserialize_callback_func', $unserializeCallbackHandler);
39+
}
40+
}
41+
42+
/**
43+
* @internal
44+
*/
45+
public static function handleUnserializeCallback($class)
46+
{
47+
throw new \DomainException('Class not found: '.$class);
48+
}
49+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache;
13+
14+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
/**
17+
* @author Alexei Prilipko <palex.fpt@gmail.com>
18+
*/
19+
interface SerializerInterface
20+
{
21+
/**
22+
* Generates a storable representation of a value.
23+
*
24+
* @param $data mixed
25+
*
26+
* @return string|mixed serialized value
27+
*
28+
* @throws InvalidArgumentException when $data can not be serialized
29+
*/
30+
public function serialize($data);
31+
32+
/**
33+
* Creates a PHP value from a stored representation.
34+
*
35+
* @param string|mixed $serialized the serialized string
36+
*
37+
* @return mixed Original value
38+
*/
39+
public function unserialize($serialized);
40+
}

src/Symfony/Component/Cache/Simple/ArrayCache.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use Psr\SimpleCache\CacheInterface;
1616
use Symfony\Component\Cache\CacheItem;
1717
use Symfony\Component\Cache\Exception\InvalidArgumentException;
18+
use Symfony\Component\Cache\Serializer\IdentitySerializer;
19+
use Symfony\Component\Cache\Serializer\PhpSerializer;
1820
use Symfony\Component\Cache\ResettableInterface;
1921
use Symfony\Component\Cache\Traits\ArrayTrait;
2022

@@ -37,7 +39,7 @@ class ArrayCache implements CacheInterface, LoggerAwareInterface, ResettableInte
3739
public function __construct(int $defaultLifetime = 0, bool $storeSerialized = true)
3840
{
3941
$this->defaultLifetime = $defaultLifetime;
40-
$this->storeSerialized = $storeSerialized;
42+
$this->setSerializer($storeSerialized ? new PhpSerializer() : new IdentitySerializer());
4143
}
4244

4345
/**
@@ -109,16 +111,14 @@ public function setMultiple($values, $ttl = null)
109111
if (false === $ttl = $this->normalizeTtl($ttl)) {
110112
return $this->deleteMultiple(array_keys($valuesArray));
111113
}
112-
if ($this->storeSerialized) {
113-
foreach ($valuesArray as $key => $value) {
114-
try {
115-
$valuesArray[$key] = serialize($value);
116-
} catch (\Exception $e) {
117-
$type = is_object($value) ? get_class($value) : gettype($value);
118-
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e));
119-
120-
return false;
121-
}
114+
foreach ($valuesArray as $key => $value) {
115+
try {
116+
$valuesArray[$key] = $this->serialize($value);
117+
} catch (\Exception $e) {
118+
$type = is_object($value) ? get_class($value) : gettype($value);
119+
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e));
120+
121+
return false;
122122
}
123123
}
124124
$expiry = 0 < $ttl ? time() + $ttl : PHP_INT_MAX;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Serializer;
13+
14+
use Symfony\Component\Cache\Serializer\IgbinarySerializer;
15+
use Symfony\Component\Cache\SerializerInterface;
16+
17+
class IgbinarySerializerTest extends SerializerTest
18+
{
19+
public static function setUpBeforeClass()
20+
{
21+
parent::setUpBeforeClass();
22+
if (!extension_loaded('igbinary')) {
23+
self::markTestSkipped('Extension igbinary is not loaded.');
24+
}
25+
}
26+
27+
protected function createSerializer(): SerializerInterface
28+
{
29+
return new IgbinarySerializer();
30+
}
31+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Serializer;
13+
14+
use Symfony\Component\Cache\Serializer\PhpSerializer;
15+
use Symfony\Component\Cache\SerializerInterface;
16+
17+
class PhpSerializerTest extends SerializerTest
18+
{
19+
protected function createSerializer(): SerializerInterface
20+
{
21+
return new PhpSerializer();
22+
}
23+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Tests\Serializer;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Cache\SerializerInterface;
16+
17+
abstract class SerializerTest extends TestCase
18+
{
19+
/** @var SerializerInterface */
20+
private $serializer;
21+
22+
protected function setUp()
23+
{
24+
parent::setUp();
25+
26+
$this->serializer = $this->createSerializer();
27+
}
28+
29+
abstract protected function createSerializer(): SerializerInterface;
30+
31+
/**
32+
* @dataProvider validData
33+
*/
34+
public function testSerializeInvariants($value)
35+
{
36+
$serialized = $this->serializer->serialize($value);
37+
$restored = $this->serializer->unserialize($serialized);
38+
$this->assertEquals($value, $restored);
39+
}
40+
41+
/**
42+
* Data provider for valid data to store.
43+
*
44+
* @return array
45+
*/
46+
public static function validData()
47+
{
48+
return array(
49+
array(false),
50+
array('AbC19_.'),
51+
array(4711),
52+
array(47.11),
53+
array(true),
54+
array(null),
55+
array(array('key' => 'value')),
56+
array(new \stdClass()),
57+
);
58+
}
59+
}
60+
61+
class NotUnserializable implements \Serializable
62+
{
63+
public function serialize()
64+
{
65+
return serialize(123);
66+
}
67+
68+
public function unserialize($ser)
69+
{
70+
throw new \Exception(__CLASS__);
71+
}
72+
}

0 commit comments

Comments
 (0)
0