Skip to content

Commit ebdcd16

Browse files
dunglasnicolas-grekas
authored andcommitted
[Cache] Add a Chain adapter
1 parent f29d46f commit ebdcd16

File tree

7 files changed

+338
-5
lines changed

7 files changed

+338
-5
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Cache\Adapter;
1313

1414
use Psr\Cache\CacheItemInterface;
15-
use Psr\Cache\CacheItemPoolInterface;
1615
use Psr\Log\LoggerAwareInterface;
1716
use Psr\Log\LoggerAwareTrait;
1817
use Symfony\Component\Cache\CacheItem;
@@ -21,7 +20,7 @@
2120
/**
2221
* @author Nicolas Grekas <[email protected]>
2322
*/
24-
abstract class AbstractAdapter implements CacheItemPoolInterface, LoggerAwareInterface
23+
abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
2524
{
2625
use LoggerAwareTrait;
2726

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 <[email protected]>
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\CacheItemPoolInterface;
15+
16+
/**
17+
* Marker interface for adapters managing {@see \Symfony\Component\Cache\CacheItem} instances.
18+
*
19+
* @author Kévin Dunglas <[email protected]>
20+
*/
21+
interface AdapterInterface extends CacheItemPoolInterface
22+
{
23+
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\Cache\Adapter;
1313

1414
use Psr\Cache\CacheItemInterface;
15-
use Psr\Cache\CacheItemPoolInterface;
1615
use Psr\Log\LoggerAwareInterface;
1716
use Psr\Log\LoggerAwareTrait;
1817
use Symfony\Component\Cache\CacheItem;
@@ -21,7 +20,7 @@
2120
/**
2221
* @author Nicolas Grekas <[email protected]>
2322
*/
24-
class ArrayAdapter implements CacheItemPoolInterface, LoggerAwareInterface
23+
class ArrayAdapter implements AdapterInterface, LoggerAwareInterface
2524
{
2625
use LoggerAwareTrait;
2726

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Exception\InvalidArgumentException;
17+
18+
/**
19+
* Chains adapters together.
20+
*
21+
* Saves, deletes and clears all registered adapter.
22+
* Gets data from the first chained adapter having it in cache.
23+
*
24+
* @author Kévin Dunglas <[email protected]>
25+
*/
26+
class ChainAdapter implements AdapterInterface
27+
{
28+
private $adapters = array();
29+
30+
/**
31+
* @param AdapterInterface[] $adapters
32+
*/
33+
public function __construct(array $adapters)
34+
{
35+
if (2 > count($adapters)) {
36+
throw new InvalidArgumentException('At least two adapters must be chained.');
37+
}
38+
39+
foreach ($adapters as $adapter) {
40+
if (!$adapter instanceof CacheItemPoolInterface) {
41+
throw new InvalidArgumentException(sprintf('The class "%s" does not implement the "%s" interface.', get_class($adapter), CacheItemPoolInterface::class));
42+
}
43+
44+
if ($adapter instanceof AdapterInterface) {
45+
$this->adapters[] = $adapter;
46+
} else {
47+
$this->adapters[] = new ProxyAdapter($adapter);
48+
}
49+
}
50+
}
51+
52+
/**
53+
* {@inheritdoc}
54+
*/
55+
public function getItem($key)
56+
{
57+
foreach ($this->adapters as $adapter) {
58+
$item = $adapter->getItem($key);
59+
60+
if ($item->isHit()) {
61+
return $item;
62+
}
63+
}
64+
65+
return $item;
66+
}
67+
68+
/**
69+
* {@inheritdoc}
70+
*/
71+
public function getItems(array $keys = array())
72+
{
73+
$items = array();
74+
foreach ($keys as $key) {
75+
$items[$key] = $this->getItem($key);
76+
}
77+
78+
return $items;
79+
}
80+
81+
/**
82+
* {@inheritdoc}
83+
*/
84+
public function hasItem($key)
85+
{
86+
foreach ($this->adapters as $adapter) {
87+
if ($adapter->hasItem($key)) {
88+
return true;
89+
}
90+
}
91+
92+
return false;
93+
}
94+
95+
/**
96+
* {@inheritdoc}
97+
*/
98+
public function clear()
99+
{
100+
$cleared = true;
101+
102+
foreach ($this->adapters as $adapter) {
103+
$cleared = $adapter->clear() && $cleared;
104+
}
105+
106+
return $cleared;
107+
}
108+
109+
/**
110+
* {@inheritdoc}
111+
*/
112+
public function deleteItem($key)
113+
{
114+
$deleted = true;
115+
116+
foreach ($this->adapters as $adapter) {
117+
$deleted = $adapter->deleteItem($key) && $deleted;
118+
}
119+
120+
return $deleted;
121+
}
122+
123+
/**
124+
* {@inheritdoc}
125+
*/
126+
public function deleteItems(array $keys)
127+
{
128+
$deleted = true;
129+
130+
foreach ($this->adapters as $adapter) {
131+
$deleted = $adapter->deleteItems($keys) && $deleted;
132+
}
133+
134+
return $deleted;
135+
}
136+
137+
/**
138+
* {@inheritdoc}
139+
*/
140+
public function save(CacheItemInterface $item)
141+
{
142+
$saved = true;
143+
144+
foreach ($this->adapters as $adapter) {
145+
$saved = $adapter->save($item) && $saved;
146+
}
147+
148+
return $saved;
149+
}
150+
151+
/**
152+
* {@inheritdoc}
153+
*/
154+
public function saveDeferred(CacheItemInterface $item)
155+
{
156+
$saved = true;
157+
158+
foreach ($this->adapters as $adapter) {
159+
$saved = $adapter->saveDeferred($item) && $saved;
160+
}
161+
162+
return $saved;
163+
}
164+
165+
/**
166+
* {@inheritdoc}
167+
*/
168+
public function commit()
169+
{
170+
$committed = true;
171+
172+
foreach ($this->adapters as $adapter) {
173+
$committed = $adapter->commit() && $committed;
174+
}
175+
176+
return $committed;
177+
}
178+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
/**
2020
* @author Nicolas Grekas <[email protected]>
2121
*/
22-
class ProxyAdapter implements CacheItemPoolInterface
22+
class ProxyAdapter implements AdapterInterface
2323
{
2424
private $pool;
2525
private $namespace;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Adapter;
13+
14+
use Cache\IntegrationTests\CachePoolTest;
15+
use Symfony\Component\Cache\Adapter\ApcuAdapter;
16+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17+
use Symfony\Component\Cache\Adapter\ChainAdapter;
18+
use Symfony\Component\Cache\Tests\Fixtures\ExternalAdapter;
19+
20+
/**
21+
* @author Kévin Dunglas <[email protected]>
22+
*/
23+
class ChainAdapterTest extends CachePoolTest
24+
{
25+
protected $skippedTests = array(
26+
'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayAdapter is not.',
27+
'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayAdapter is not.',
28+
'testDeferredExpired' => 'Failing for now, needs to be fixed.',
29+
);
30+
31+
public function createCachePool()
32+
{
33+
if (defined('HHVM_VERSION')) {
34+
$this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM';
35+
}
36+
if (!function_exists('apcu_fetch') || !ini_get('apc.enabled') || ('cli' === PHP_SAPI && !ini_get('apc.enable_cli'))) {
37+
$this->markTestSkipped('APCu extension is required.');
38+
}
39+
40+
return new ChainAdapter(array(new ArrayAdapter(), new ExternalAdapter(), new ApcuAdapter(__CLASS__)));
41+
}
42+
43+
/**
44+
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
45+
*/
46+
public function testLessThanTwoAdapterException()
47+
{
48+
new ChainAdapter(array());
49+
}
50+
51+
/**
52+
* @expectedException \Symfony\Component\Cache\Exception\InvalidArgumentException
53+
*/
54+
public function testInvalidAdapterException()
55+
{
56+
new ChainAdapter(array(new \stdClass(), new \stdClass()));
57+
}
58+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Fixtures;
13+
14+
use Psr\Cache\CacheItemInterface;
15+
use Psr\Cache\CacheItemPoolInterface;
16+
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17+
18+
/**
19+
* Adapter not implementing the {@see \Symfony\Component\Cache\Adapter\AdapterInterface}.
20+
*
21+
* @author Kévin Dunglas <[email protected]>
22+
*/
23+
class ExternalAdapter implements CacheItemPoolInterface
24+
{
25+
private $cache;
26+
27+
public function __construct()
28+
{
29+
$this->cache = new ArrayAdapter();
30+
}
31+
32+
public function getItem($key)
33+
{
34+
return $this->cache->getItem($key);
35+
}
36+
37+
public function getItems(array $keys = array())
38+
{
39+
return $this->cache->getItems($keys);
40+
}
41+
42+
public function hasItem($key)
43+
{
44+
return $this->cache->hasItem($key);
45+
}
46+
47+
public function clear()
48+
{
49+
return $this->cache->clear();
50+
}
51+
52+
public function deleteItem($key)
53+
{
54+
return $this->cache->deleteItem($key);
55+
}
56+
57+
public function deleteItems(array $keys)
58+
{
59+
return $this->cache->deleteItems($keys);
60+
}
61+
62+
public function save(CacheItemInterface $item)
63+
{
64+
return $this->cache->save($item);
65+
}
66+
67+
public function saveDeferred(CacheItemInterface $item)
68+
{
69+
return $this->cache->saveDeferred($item);
70+
}
71+
72+
public function commit()
73+
{
74+
return $this->cache->commit();
75+
}
76+
}

0 commit comments

Comments
 (0)