Skip to content

Commit a046b58

Browse files
[Cache] Symfony PSR-6 implementation
1 parent a4f3baa commit a046b58

13 files changed

+678
-0
lines changed

composer.json

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"replace": {
3131
"symfony/asset": "self.version",
3232
"symfony/browser-kit": "self.version",
33+
"symfony/cache": "self.version",
3334
"symfony/class-loader": "self.version",
3435
"symfony/config": "self.version",
3536
"symfony/console": "self.version",
@@ -74,6 +75,7 @@
7475
"symfony/yaml": "self.version"
7576
},
7677
"require-dev": {
78+
"doctrine/cache": "~1.6",
7779
"doctrine/data-fixtures": "1.0.*",
7880
"doctrine/dbal": "~2.4",
7981
"doctrine/orm": "~2.4,>=2.4.5",
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
composer.lock
2+
phpunit.xml
3+
vendor/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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\Adapter;
13+
14+
use Psr\Cache\CacheItemInterface;
15+
use Psr\Cache\CacheItemPoolInterface;
16+
use Symfony\Component\Cache\CacheItem;
17+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
18+
19+
/**
20+
* @author Nicolas Grekas <p@tchwork.com>
21+
*/
22+
abstract class AbstractAdapter implements CacheItemPoolInterface
23+
{
24+
private $namespace;
25+
private $deferred = array();
26+
private $createCacheItem;
27+
private $mergeByLifetime;
28+
29+
protected function __construct($namespace = '', $defaultLifetime = 0)
30+
{
31+
$this->namespace = $namespace;
32+
$this->createCacheItem = \Closure::bind(
33+
function ($key, $value, $isHit) use ($defaultLifetime) {
34+
$item = new CacheItem();
35+
$item->key = $key;
36+
$item->value = $value;
37+
$item->isHit = $isHit;
38+
$item->defaultLifetime = $defaultLifetime;
39+
40+
return $item;
41+
},
42+
$this,
43+
CacheItem::class
44+
);
45+
$this->mergeByLifetime = \Closure::bind(
46+
function ($deferred, $namespace) {
47+
$byLifetime = array();
48+
49+
foreach ($deferred as $key => $item) {
50+
if (0 <= $item->lifetime) {
51+
$byLifetime[$item->lifetime][$namespace.$key] = $item->value;
52+
}
53+
}
54+
55+
return $byLifetime;
56+
},
57+
$this,
58+
CacheItem::class
59+
);
60+
}
61+
62+
/**
63+
* Fetches several cache items.
64+
*
65+
* @param array $ids The cache identifiers to fetch.
66+
*
67+
* @return array The corresponding values found in the cache.
68+
*/
69+
abstract protected function doFetch(array $ids);
70+
71+
/**
72+
* Persists several cache items immediately.
73+
*
74+
* @param array $values The values to cache, indexed by their cache identifier.
75+
* @param int $lifetime The lifetime of the cached values, 0 for persisting until manual cleaning.
76+
*
77+
* @return array|bool The identifiers that failed to be cached or a boolean stating if caching succeeded or not.
78+
*/
79+
abstract protected function doSave(array $values, $lifetime);
80+
81+
/**
82+
* {@inheritdoc}
83+
*/
84+
public function getItem($key)
85+
{
86+
$id = $this->getId($key);
87+
88+
if ($this->deferred) {
89+
$this->commit();
90+
}
91+
if (isset($this->deferred[$key])) {
92+
return $this->deferred[$key];
93+
}
94+
95+
$f = $this->createCacheItem;
96+
$isHit = false;
97+
$value = null;
98+
99+
foreach ($this->doFetch(array($id)) as $value) {
100+
$isHit = true;
101+
}
102+
103+
return $f($key, $value, $isHit);
104+
}
105+
106+
/**
107+
* {@inheritdoc}
108+
*/
109+
public function getItems(array $keys = array())
110+
{
111+
if ($this->deferred) {
112+
$this->commit();
113+
}
114+
$f = $this->createCacheItem;
115+
$ids = array();
116+
$items = array();
117+
118+
foreach ($keys as $key) {
119+
$id = $this->getId($key);
120+
121+
if (isset($this->deferred[$key])) {
122+
$items[$key] = $this->deferred[$key];
123+
} else {
124+
$ids[$key] = $id;
125+
}
126+
}
127+
128+
$values = $this->doFetch($ids);
129+
130+
foreach ($ids as $key => $id) {
131+
$isHit = isset($values[$id]);
132+
$items[$key] = $f($key, $isHit ? $values[$id] : null, $isHit);
133+
}
134+
135+
return $items;
136+
}
137+
138+
/**
139+
* {@inheritdoc}
140+
*/
141+
public function deleteItems(array $keys)
142+
{
143+
$ok = true;
144+
145+
foreach ($keys as $key) {
146+
$ok = $this->deleteItem($key) && $ok;
147+
unset($this->deferred[$key]);
148+
}
149+
150+
return $ok;
151+
}
152+
153+
/**
154+
* {@inheritdoc}
155+
*/
156+
public function save(CacheItemInterface $item)
157+
{
158+
if (!$item instanceof CacheItem) {
159+
return false;
160+
}
161+
$key = $item->getKey();
162+
$this->deferred[$key] = $item;
163+
$this->commit();
164+
165+
return !isset($this->deferred[$key]);
166+
}
167+
168+
/**
169+
* {@inheritdoc}
170+
*/
171+
public function saveDeferred(CacheItemInterface $item)
172+
{
173+
if (!$item instanceof CacheItem) {
174+
return false;
175+
}
176+
$this->deferred[$item->getKey()] = clone $item;
177+
178+
return true;
179+
}
180+
181+
/**
182+
* {@inheritdoc}
183+
*/
184+
public function commit()
185+
{
186+
$f = $this->mergeByLifetime;
187+
$ko = array();
188+
$namespaceLen = strlen($this->namespace);
189+
190+
foreach ($f($this->deferred, $this->namespace) as $lifetime => $values) {
191+
if (true === $ok = $this->doSave($values, $lifetime)) {
192+
continue;
193+
}
194+
if (false === $ok) {
195+
$ok = array_keys($values);
196+
}
197+
foreach ($ok as $failedId) {
198+
$key = substr($failedId, $namespaceLen);
199+
$ko[$key] = $this->deferred[$key];
200+
}
201+
}
202+
203+
return !$this->deferred = $ko;
204+
}
205+
206+
public function __destruct()
207+
{
208+
if ($this->deferred) {
209+
$this->commit();
210+
}
211+
}
212+
213+
protected function getId($key)
214+
{
215+
if (!is_string($key)) {
216+
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', is_object($key) ? get_class($key) : gettype($key)));
217+
}
218+
if (!isset($key[0])) {
219+
throw new InvalidArgumentException('Cache key length must be greater than zero');
220+
}
221+
if (isset($key[strcspn($key, '{}()/\@')])) {
222+
throw new InvalidArgumentException('Cache key contains reserved characters {}()/\@');
223+
}
224+
225+
return $this->namespace.$key;
226+
}
227+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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\Adapter;
13+
14+
use Symfony\Component\Cache\Exception\CacheException;
15+
16+
/**
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
class ApcuAdapter extends AbstractAdapter
20+
{
21+
public function __construct($namespace = '', $defaultLifetime = 0)
22+
{
23+
if (!function_exists('apcu_fetch')) {
24+
throw new CacheException('APCu is not installed');
25+
}
26+
parent::__construct($namespace, $defaultLifetime);
27+
}
28+
29+
/**
30+
* {@inheritdoc}
31+
*/
32+
public function hasItem($key)
33+
{
34+
return apcu_exists($this->getId($key));
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function clear()
41+
{
42+
return apcu_clear_cache();
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function deleteItem($key)
49+
{
50+
return apcu_delete($id = $this->getId($key)) || !apcu_exists($id);
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
protected function doFetch(array $ids)
57+
{
58+
return apcu_fetch($ids);
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
protected function doSave(array $values, $lifetime)
65+
{
66+
return apcu_store($values, null, $lifetime);
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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\Adapter;
13+
14+
use Doctrine\Common\Cache\CacheProvider;
15+
16+
/**
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
class DoctrineAdapter extends AbstractAdapter
20+
{
21+
private $provider;
22+
23+
public function __construct(CacheProvider $provider, $defaultLifetime = 0)
24+
{
25+
parent::__construct('', $defaultLifetime);
26+
$this->provider = $provider;
27+
}
28+
29+
/**
30+
* {@inheritdoc}
31+
*/
32+
public function hasItem($key)
33+
{
34+
return $this->provider->contains($this->getId($key));
35+
}
36+
37+
/**
38+
* {@inheritdoc}
39+
*/
40+
public function clear()
41+
{
42+
return $this->provider->flushAll();
43+
}
44+
45+
/**
46+
* {@inheritdoc}
47+
*/
48+
public function deleteItem($key)
49+
{
50+
return $this->provider->delete($this->getId($key));
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
protected function doFetch(array $ids)
57+
{
58+
return $this->provider->fetchMultiple($ids);
59+
}
60+
61+
/**
62+
* {@inheritdoc}
63+
*/
64+
protected function doSave(array $values, $lifetime)
65+
{
66+
return $this->provider->saveMultiple($values, $lifetime);
67+
}
68+
}

0 commit comments

Comments
 (0)