Skip to content

Commit 657d8c7

Browse files
[Cache] Improve perf of array-based pools
1 parent 5c338cc commit 657d8c7

File tree

4 files changed

+102
-28
lines changed

4 files changed

+102
-28
lines changed

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ public function getItem($key)
6262
$this->values[$key] = $value = null;
6363
} elseif (!$this->storeSerialized) {
6464
$value = $this->values[$key];
65-
} elseif ('b:0;' === $value = $this->values[$key]) {
66-
$value = false;
67-
} elseif (false === $value = unserialize($value)) {
65+
} elseif ('N;' === $value = $this->values[$key]) {
66+
$value = null;
67+
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1] && false === $value = unserialize($value)) {
6868
$this->values[$key] = $value = null;
6969
$isHit = false;
7070
}
@@ -84,7 +84,9 @@ public function getItem($key)
8484
public function getItems(array $keys = array())
8585
{
8686
foreach ($keys as $key) {
87-
CacheItem::validateKey($key);
87+
if (!\is_string($key) || !isset($this->expiries[$key])) {
88+
CacheItem::validateKey($key);
89+
}
8890
}
8991

9092
return $this->generateItems($keys, microtime(true), $this->createCacheItem);
@@ -120,15 +122,28 @@ public function save(CacheItemInterface $item)
120122

121123
return true;
122124
}
123-
if ($this->storeSerialized) {
124-
try {
125+
if (!$this->storeSerialized) {
126+
// no-op
127+
} elseif (\is_string($value)) {
128+
// Serialize strings if they could be confused with serialized objects or arrays
129+
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
125130
$value = serialize($value);
131+
}
132+
} elseif (null === $value) {
133+
$value = 'N;';
134+
} elseif (!\is_scalar($value)) {
135+
try {
136+
$serialized = serialize($value);
126137
} catch (\Exception $e) {
127138
$type = is_object($value) ? get_class($value) : gettype($value);
128139
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e));
129140

130141
return false;
131142
}
143+
// Keep value serialized if it contains any objects or any internal references
144+
if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
145+
$value = $serialized;
146+
}
132147
}
133148
if (null === $expiry && 0 < $item["\0*\0defaultLifetime"]) {
134149
$expiry = microtime(true) + $item["\0*\0defaultLifetime"];

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

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,33 @@ public function __construct(int $defaultLifetime = 0, bool $storeSerialized = tr
4545
*/
4646
public function get($key, $default = null)
4747
{
48-
foreach ($this->getMultiple(array($key), $default) as $v) {
49-
return $v;
48+
if (!\is_string($key) || !isset($this->expiries[$key])) {
49+
CacheItem::validateKey($key);
50+
}
51+
if (!isset($this->expiries[$key]) || ($this->expiries[$key] <= microtime(true) && $this->deleteItem($key))) {
52+
$this->values[$key] = null;
53+
54+
return $default;
55+
}
56+
if (!$this->storeSerialized) {
57+
return $this->values[$key];
58+
}
59+
if ('N;' === $value = $this->values[$key]) {
60+
return null;
61+
}
62+
if (!\is_string($value) || !isset($value[2]) || ':' !== $value[1]) {
63+
return $value;
5064
}
65+
try {
66+
if (false !== $value = unserialize($value)) {
67+
return $value;
68+
}
69+
} catch (\Exception $e) {
70+
CacheItem::log($this->logger, 'Failed to unserialize key "{key}"', array('key' => $key, 'exception' => $e));
71+
}
72+
$this->values[$key] = null;
73+
74+
return $default;
5175
}
5276

5377
/**
@@ -61,7 +85,9 @@ public function getMultiple($keys, $default = null)
6185
throw new InvalidArgumentException(sprintf('Cache keys must be array or Traversable, "%s" given', is_object($keys) ? get_class($keys) : gettype($keys)));
6286
}
6387
foreach ($keys as $key) {
64-
CacheItem::validateKey($key);
88+
if (!\is_string($key) || !isset($this->expiries[$key])) {
89+
CacheItem::validateKey($key);
90+
}
6591
}
6692

6793
return $this->generateItems($keys, microtime(true), function ($k, $v, $hit) use ($default) { return $hit ? $v : $default; });
@@ -87,7 +113,9 @@ public function deleteMultiple($keys)
87113
*/
88114
public function set($key, $value, $ttl = null)
89115
{
90-
CacheItem::validateKey($key);
116+
if (!\is_string($key)) {
117+
CacheItem::validateKey($key);
118+
}
91119

92120
return $this->setMultiple(array($key => $value), $ttl);
93121
}
@@ -103,27 +131,40 @@ public function setMultiple($values, $ttl = null)
103131
$valuesArray = array();
104132

105133
foreach ($values as $key => $value) {
106-
\is_int($key) || CacheItem::validateKey($key);
134+
if (!\is_int($key) && !(\is_string($key) && isset($this->expiries[$key]))) {
135+
CacheItem::validateKey($key);
136+
}
107137
$valuesArray[$key] = $value;
108138
}
109139
if (false === $ttl = $this->normalizeTtl($ttl)) {
110140
return $this->deleteMultiple(array_keys($valuesArray));
111141
}
112-
if ($this->storeSerialized) {
113-
foreach ($valuesArray as $key => $value) {
142+
$expiry = 0 < $ttl ? microtime(true) + $ttl : PHP_INT_MAX;
143+
144+
foreach ($valuesArray as $key => $value) {
145+
if (!$this->storeSerialized) {
146+
// no-op
147+
} elseif (\is_string($value)) {
148+
// Serialize strings if they could be confused with serialized objects or arrays
149+
if ('N;' === $value || (isset($value[2]) && ':' === $value[1])) {
150+
$value = serialize($value);
151+
}
152+
} elseif (null === $value) {
153+
$value = 'N;';
154+
} elseif (!\is_scalar($value)) {
114155
try {
115-
$valuesArray[$key] = serialize($value);
156+
$serialized = serialize($value);
116157
} catch (\Exception $e) {
117158
$type = is_object($value) ? get_class($value) : gettype($value);
118159
CacheItem::log($this->logger, 'Failed to save key "{key}" ({type})', array('key' => $key, 'type' => $type, 'exception' => $e));
119160

120161
return false;
121162
}
163+
// Keep value serialized if it contains any objects or any internal references
164+
if ('C' === $serialized[0] || 'O' === $serialized[0] || preg_match('/;[OCRr]:[1-9]/', $serialized)) {
165+
$value = $serialized;
166+
}
122167
}
123-
}
124-
$expiry = 0 < $ttl ? microtime(true) + $ttl : PHP_INT_MAX;
125-
126-
foreach ($valuesArray as $key => $value) {
127168
$this->values[$key] = $value;
128169
$this->expiries[$key] = $expiry;
129170
}

src/Symfony/Component/Cache/Tests/Adapter/ArrayAdapterTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ public function testGetValuesHitAndMiss()
3636

3737
// Hit
3838
$item = $cache->getItem('foo');
39-
$item->set('4711');
39+
$item->set('::4711');
4040
$cache->save($item);
4141

4242
$fooItem = $cache->getItem('foo');
4343
$this->assertTrue($fooItem->isHit());
44-
$this->assertEquals('4711', $fooItem->get());
44+
$this->assertEquals('::4711', $fooItem->get());
4545

4646
// Miss (should be present as NULL in $values)
4747
$cache->getItem('bar');
@@ -50,7 +50,7 @@ public function testGetValuesHitAndMiss()
5050

5151
$this->assertCount(2, $values);
5252
$this->assertArrayHasKey('foo', $values);
53-
$this->assertSame(serialize('4711'), $values['foo']);
53+
$this->assertSame(serialize('::4711'), $values['foo']);
5454
$this->assertArrayHasKey('bar', $values);
5555
$this->assertNull($values['bar']);
5656
}

src/Symfony/Component/Cache/Traits/ArrayTrait.php

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,34 @@ trait ArrayTrait
3434
*/
3535
public function getValues()
3636
{
37-
return $this->values;
37+
if (!$this->storeSerialized) {
38+
return $this->values;
39+
}
40+
41+
$values = $this->values;
42+
foreach ($values as $k => $v) {
43+
if (null === $v || 'N;' === $v) {
44+
continue;
45+
}
46+
if (!\is_string($v) || !isset($v[2]) || ':' !== $v[1]) {
47+
$values[$k] = serialize($v);
48+
}
49+
}
50+
51+
return $values;
3852
}
3953

4054
/**
4155
* {@inheritdoc}
4256
*/
4357
public function hasItem($key)
4458
{
59+
if (\is_string($key) && isset($this->expiries[$key]) && $this->expiries[$key] > microtime(true)) {
60+
return true;
61+
}
4562
CacheItem::validateKey($key);
4663

47-
return isset($this->expiries[$key]) && ($this->expiries[$key] > microtime(true) || !$this->deleteItem($key));
64+
return isset($this->expiries[$key]) && !$this->deleteItem($key);
4865
}
4966

5067
/**
@@ -62,8 +79,9 @@ public function clear()
6279
*/
6380
public function deleteItem($key)
6481
{
65-
CacheItem::validateKey($key);
66-
82+
if (!\is_string($key) || !isset($this->expiries[$key])) {
83+
CacheItem::validateKey($key);
84+
}
6785
unset($this->values[$key], $this->expiries[$key]);
6886

6987
return true;
@@ -85,9 +103,9 @@ private function generateItems(array $keys, $now, $f)
85103
$this->values[$key] = $value = null;
86104
} elseif (!$this->storeSerialized) {
87105
$value = $this->values[$key];
88-
} elseif ('b:0;' === $value = $this->values[$key]) {
89-
$value = false;
90-
} elseif (false === $value = unserialize($value)) {
106+
} elseif ('N;' === $value = $this->values[$key]) {
107+
$value = null;
108+
} elseif (\is_string($value) && isset($value[2]) && ':' === $value[1] && false === $value = unserialize($value)) {
91109
$this->values[$key] = $value = null;
92110
$isHit = false;
93111
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy