11
11
12
12
namespace Symfony \Component \Cache \Adapter ;
13
13
14
+ use Symfony \Component \Cache \CacheItem ;
15
+
14
16
/**
15
17
* @author Nicolas Grekas <[email protected] >
16
18
*/
17
- class FilesystemAdapter extends AbstractAdapter
19
+ class FilesystemAdapter extends AbstractTagsInvalidatingAdapter
18
20
{
19
21
use FilesystemAdapterTrait;
20
22
@@ -24,6 +26,20 @@ public function __construct($namespace = '', $defaultLifetime = 0, $directory =
24
26
$ this ->init ($ namespace , $ directory );
25
27
}
26
28
29
+ /**
30
+ * {@inheritdoc}
31
+ */
32
+ public function invalidateTags ($ tags )
33
+ {
34
+ $ ok = true ;
35
+
36
+ foreach (CacheItem::normalizeTags ($ tags ) as $ tag ) {
37
+ $ ok = $ this ->doInvalidateTag ($ tag ) && $ ok ;
38
+ }
39
+
40
+ return $ ok ;
41
+ }
42
+
27
43
/**
28
44
* {@inheritdoc}
29
45
*/
@@ -68,15 +84,123 @@ protected function doHave($id)
68
84
/**
69
85
* {@inheritdoc}
70
86
*/
71
- protected function doSave (array $ values , $ lifetime )
87
+ protected function doSaveWithTags (array $ values , $ lifetime, array $ tags )
72
88
{
73
89
$ ok = true ;
74
90
$ expiresAt = $ lifetime ? time () + $ lifetime : PHP_INT_MAX ;
91
+ $ newTags = $ oldTags = array ();
75
92
76
93
foreach ($ values as $ id => $ value ) {
94
+ $ newIdTags = $ tags [$ id ];
95
+ $ file = $ this ->getFile ($ id , true );
96
+ $ tagFile = $ this ->getFile ($ id .':tag ' , $ newIdTags );
97
+ $ hasFile = file_exists ($ file );
98
+
99
+ if ($ hadTags = file_exists ($ tagFile )) {
100
+ foreach (file ($ tagFile , FILE_IGNORE_NEW_LINES ) as $ tag ) {
101
+ if (isset ($ newIdTags [$ tag = rawurldecode ($ tag )])) {
102
+ if ($ hasFile ) {
103
+ unset($ newIdTags [$ tag ]);
104
+ }
105
+ } else {
106
+ $ oldTags [] = $ tag ;
107
+ }
108
+ }
109
+ if ($ oldTags ) {
110
+ $ this ->removeTags ($ id , $ oldTags );
111
+ $ oldTags = array ();
112
+ }
113
+ }
114
+ foreach ($ newIdTags as $ tag ) {
115
+ $ newTags [$ tag ][] = $ id ;
116
+ }
117
+
77
118
$ ok = $ this ->write ($ this ->getFile ($ id , true ), $ expiresAt ."\n" .rawurlencode ($ id )."\n" .serialize ($ value ), $ expiresAt ) && $ ok ;
119
+
120
+ if ($ tags [$ id ]) {
121
+ $ ok = $ this ->write ($ tagFile , implode ("\n" , array_map ('rawurlencode ' , $ tags [$ id ]))."\n" ) && $ ok ;
122
+ } elseif ($ hadTags ) {
123
+ @unlink ($ tagFile );
124
+ }
125
+ }
126
+ if ($ newTags ) {
127
+ $ ok = $ this ->doTag ($ newTags ) && $ ok ;
78
128
}
79
129
80
130
return $ ok ;
81
131
}
132
+
133
+ private function doTag (array $ tags )
134
+ {
135
+ $ ok = true ;
136
+ $ linkedTags = array ();
137
+
138
+ foreach ($ tags as $ tag => $ ids ) {
139
+ $ file = $ this ->getFile ($ tag , true );
140
+ $ linkedTags [$ tag ] = file_exists ($ file ) ?: null ;
141
+ $ h = fopen ($ file , 'ab ' );
142
+
143
+ foreach ($ ids as $ id ) {
144
+ $ ok = fwrite ($ h , rawurlencode ($ id )."\n" ) && $ ok ;
145
+ }
146
+ fclose ($ h );
147
+
148
+ while (!isset ($ linkedTags [$ tag ]) && 0 < $ r = strrpos ($ tag , '/ ' )) {
149
+ $ linkedTags [$ tag ] = true ;
150
+ $ parent = substr ($ tag , 0 , $ r );
151
+ $ file = $ this ->getFile ($ parent , true );
152
+ $ linkedTags [$ parent ] = file_exists ($ file ) ?: null ;
153
+ $ ok = file_put_contents ($ file , rawurlencode ($ tag )."\n" , FILE_APPEND ) && $ ok ;
154
+ $ tag = $ parent ;
155
+ }
156
+ }
157
+
158
+ return $ ok ;
159
+ }
160
+
161
+ private function doInvalidateTag ($ tag )
162
+ {
163
+ if (!$ h = @fopen ($ this ->getFile ($ tag ), 'r+b ' )) {
164
+ return true ;
165
+ }
166
+ $ ok = true ;
167
+
168
+ while (false !== $ id = fgets ($ h )) {
169
+ if ('! ' === $ id [0 ]) {
170
+ continue ;
171
+ }
172
+ $ id = rawurldecode (substr ($ id , 0 , -1 ));
173
+
174
+ if ('/ ' === $ id [0 ]) {
175
+ $ ok = $ this ->doInvalidateTag ($ id ) && $ ok ;
176
+ } else {
177
+ $ file = $ this ->getFile ($ id );
178
+ $ ok = (!file_exists ($ file ) || @unlink ($ file ) || !file_exists ($ file )) && $ ok ;
179
+ }
180
+ }
181
+
182
+ ftruncate ($ h , 0 );
183
+ fclose ($ h );
184
+
185
+ return $ ok ;
186
+ }
187
+
188
+ private function removeTags ($ id , $ tags )
189
+ {
190
+ $ idLine = rawurlencode ($ id )."\n" ;
191
+ $ idSeek = -strlen ($ idLine );
192
+
193
+ foreach ($ tags as $ tag ) {
194
+ if (!$ h = @fopen ($ this ->getFile ($ tag ), 'r+b ' )) {
195
+ continue ;
196
+ }
197
+ while (false !== $ line = fgets ($ h )) {
198
+ if ($ line === $ idLine ) {
199
+ fseek ($ h , $ idSeek , SEEK_CUR );
200
+ fwrite ($ h , '! ' );
201
+ }
202
+ }
203
+ fclose ($ h );
204
+ }
205
+ }
82
206
}
0 commit comments