13
13
14
14
use Psr \Cache \CacheItemInterface ;
15
15
use Psr \Cache \CacheItemPoolInterface ;
16
+ use Symfony \Component \Cache \CacheItem ;
16
17
use Symfony \Component \Cache \Exception \InvalidArgumentException ;
17
18
18
19
/**
19
- * Chains adapters together.
20
+ * Chains several adapters together.
20
21
*
21
- * Saves, deletes and clears all registered adapter.
22
- * Gets data from the first chained adapter having it in cache .
22
+ * Cached items are fetched from the first adapter having them in its data store .
23
+ * They are saved and deleted in all adapters at once .
23
24
*
24
25
* @author Kévin Dunglas <dunglas@gmail.com>
25
26
*/
26
27
class ChainAdapter implements AdapterInterface
27
28
{
28
29
private $ adapters = array ();
30
+ private $ saveUp ;
29
31
30
32
/**
31
- * @param AdapterInterface[] $adapters
33
+ * @param CacheItemPoolInterface[] $adapters The ordered list of adapters used to fetch cached items.
34
+ * @param int $maxLifetime The max lifetime of items propagated from lower adapters to upper ones.
32
35
*/
33
- public function __construct (array $ adapters )
36
+ public function __construct (array $ adapters, $ maxLifetime = 0 )
34
37
{
35
- if (2 > count ( $ adapters) ) {
36
- throw new InvalidArgumentException ('At least two adapters must be chained . ' );
38
+ if (! $ adapters ) {
39
+ throw new InvalidArgumentException ('At least one adapter must be specified . ' );
37
40
}
38
41
39
42
foreach ($ adapters as $ adapter ) {
@@ -47,17 +50,38 @@ public function __construct(array $adapters)
47
50
$ this ->adapters [] = new ProxyAdapter ($ adapter );
48
51
}
49
52
}
53
+
54
+ $ this ->saveUp = \Closure::bind (
55
+ function ($ adapter , $ item ) use ($ maxLifetime ) {
56
+ $ origDefaultLifetime = $ item ->defaultLifetime ;
57
+
58
+ if (0 < $ maxLifetime && ($ origDefaultLifetime <= 0 || $ maxLifetime < $ origDefaultLifetime )) {
59
+ $ item ->defaultLifetime = $ maxLifetime ;
60
+ }
61
+
62
+ $ adapter ->save ($ item );
63
+ $ item ->defaultLifetime = $ origDefaultLifetime ;
64
+ },
65
+ $ this ,
66
+ CacheItem::class
67
+ );
50
68
}
51
69
52
70
/**
53
71
* {@inheritdoc}
54
72
*/
55
73
public function getItem ($ key )
56
74
{
57
- foreach ($ this ->adapters as $ adapter ) {
75
+ $ saveUp = $ this ->saveUp ;
76
+
77
+ foreach ($ this ->adapters as $ i => $ adapter ) {
58
78
$ item = $ adapter ->getItem ($ key );
59
79
60
80
if ($ item ->isHit ()) {
81
+ while (0 <= --$ i ) {
82
+ $ saveUp ($ this ->adapters [$ i ], $ item );
83
+ }
84
+
61
85
return $ item ;
62
86
}
63
87
}
@@ -70,12 +94,36 @@ public function getItem($key)
70
94
*/
71
95
public function getItems (array $ keys = array ())
72
96
{
73
- $ items = array ();
74
- foreach ($ keys as $ key ) {
75
- $ items [$ key ] = $ this ->getItem ($ key );
97
+ return $ this ->generateItems ($ this ->adapters [0 ]->getItems ($ keys ), 0 );
98
+ }
99
+
100
+ private function generateItems ($ items , $ adapterIndex )
101
+ {
102
+ $ missing = array ();
103
+ $ nextAdapterIndex = $ adapterIndex + 1 ;
104
+ $ nextAdapter = isset ($ this ->adapters [$ nextAdapterIndex ]) ? $ this ->adapters [$ nextAdapterIndex ] : null ;
105
+
106
+ foreach ($ items as $ k => $ item ) {
107
+ if (!$ nextAdapter || $ item ->isHit ()) {
108
+ yield $ k => $ item ;
109
+ } else {
110
+ $ missing [] = $ k ;
111
+ }
76
112
}
77
113
78
- return $ items ;
114
+ if ($ missing ) {
115
+ $ saveUp = $ this ->saveUp ;
116
+ $ adapter = $ this ->adapters [$ adapterIndex ];
117
+ $ items = $ this ->generateItems ($ nextAdapter ->getItems ($ missing ), $ nextAdapterIndex );
118
+
119
+ foreach ($ items as $ k => $ item ) {
120
+ if ($ item ->isHit ()) {
121
+ $ saveUp ($ adapter , $ item );
122
+ }
123
+
124
+ yield $ k => $ item ;
125
+ }
126
+ }
79
127
}
80
128
81
129
/**
0 commit comments