11
11
12
12
namespace Symfony \Component \Cache \Adapter ;
13
13
14
+ use Symfony \Component \Cache \CacheItem ;
14
15
use Symfony \Component \Cache \Exception \InvalidArgumentException ;
15
16
16
17
/**
17
18
* @author Nicolas Grekas <p@tchwork.com>
18
19
*/
19
- class FilesystemAdapter extends AbstractAdapter
20
+ class FilesystemAdapter extends AbstractInvalidatingAdapter
20
21
{
21
22
private $ directory ;
22
23
@@ -50,6 +51,20 @@ public function __construct($namespace = '', $defaultLifetime = 0, $directory =
50
51
$ this ->directory = $ dir ;
51
52
}
52
53
54
+ /**
55
+ * {@inheritdoc}
56
+ */
57
+ public function invalidate ($ tags )
58
+ {
59
+ $ ok = true ;
60
+
61
+ foreach (CacheItem::normalizeTags ($ tags ) as $ tag ) {
62
+ $ ok = $ this ->doInvalidate ($ tag ) && $ ok ;
63
+ }
64
+
65
+ return $ ok ;
66
+ }
67
+
53
68
/**
54
69
* {@inheritdoc}
55
70
*/
@@ -59,7 +74,7 @@ protected function doFetch(array $ids)
59
74
$ now = time ();
60
75
61
76
foreach ($ ids as $ id ) {
62
- $ file = $ this ->getFile ($ id );
77
+ $ file = $ this ->getFile ($ id, false );
63
78
if (!$ h = @fopen ($ file , 'rb ' )) {
64
79
continue ;
65
80
}
@@ -89,7 +104,7 @@ protected function doFetch(array $ids)
89
104
*/
90
105
protected function doHave ($ id )
91
106
{
92
- $ file = $ this ->getFile ($ id );
107
+ $ file = $ this ->getFile ($ id, false );
93
108
94
109
return file_exists ($ file ) && (@filemtime ($ file ) > time () || $ this ->doFetch (array ($ id )));
95
110
}
@@ -116,7 +131,7 @@ protected function doDelete(array $ids)
116
131
$ ok = true ;
117
132
118
133
foreach ($ ids as $ id ) {
119
- $ file = $ this ->getFile ($ id );
134
+ $ file = $ this ->getFile ($ id, false );
120
135
$ ok = (!file_exists ($ file ) || @unlink
B41A
($ file ) || !file_exists ($ file )) && $ ok ;
121
136
}
122
137
@@ -126,32 +141,140 @@ protected function doDelete(array $ids)
126
141
/**
127
142
* {@inheritdoc}
128
143
*/
129
- protected function doSave (array $ values , $ lifetime )
144
+ protected function doSaveWithTags (array $ values , $ lifetime, array $ tags )
130
145
{
131
146
$ ok = true ;
132
147
$ expiresAt = $ lifetime ? time () + $ lifetime : PHP_INT_MAX ;
148
+ $ newTags = $ oldTags = array ();
133
149
134
150
foreach ($ values as $ id => $ value ) {
135
- $ file = $ this ->getFile ($ id );
136
- $ dir = dirname ($ file );
137
- if (!file_exists ($ dir )) {
138
- @mkdir ($ dir , 0777 , true );
151
+ $ newIdTags = $ tags [$ id ];
152
+ $ file = $ this ->getFile ($ id , true );
153
+ $ tagFile = $ this ->getFile ($ id .':tag ' , $ newIdTags );
154
+ $ hasFile = file_exists ($ file );
155
+
156
+ if ($ hadTags = file_exists ($ tagFile )) {
157
+ foreach (file ($ tagFile , FILE_IGNORE_NEW_LINES ) as $ tag ) {
158
+ if (isset ($ newIdTags [$ tag = rawurldecode ($ tag )])) {
159
+ if ($ hasFile ) {
160
+ unset($ newIdTags [$ tag ]);
161
+ }
162
+ } else {
163
+ $ oldTags [] = $ tag ;
164
+ }
165
+ }
166
+ if ($ oldTags ) {
167
+ $ this ->removeTags ($ id , $ oldTags );
168
+ $ oldTags = array ();
169
+ }
170
+ }
171
+ foreach ($ newIdTags as $ tag ) {
172
+ $ newTags [$ tag ][] = $ id ;
139
173
}
174
+
140
175
$ value = $ expiresAt ."\n" .rawurlencode ($ id )."\n" .serialize ($ value );
141
176
if (false !== @file_put_contents ($ file , $ value , LOCK_EX )) {
142
177
@touch ($ file , $ expiresAt );
143
178
} else {
144
179
$ ok = false ;
145
180
}
181
+
182
+ if ($ tags [$ id ]) {
183
+ $ ok = file_put_contents ($ tagFile , implode ("\n" , array_map ('rawurlencode ' , $ tags [$ id ]))."\n" ) && $ ok ;
184
+ } elseif ($ hadTags ) {
185
+ @unlink ($ tagFile );
186
+ }
187
+ }
188
+ if ($ newTags ) {
189
+ $ ok = $ this ->doTag ($ newTags ) && $ ok ;
146
190
}
147
191
148
192
return $ ok ;
149
193
}
150
194
151
- private function getFile ($ id )
195
+ private function doTag (array $ tags )
196
+ {
197
+ $ ok = true ;
198
+ $ linkedTags = array ();
199
+
200
+ foreach ($ tags as $ tag => $ ids ) {
201
+ $ file = $ this ->getFile ($ tag , true );
202
+ $ linkedTags [$ tag ] = file_exists ($ file ) ?: null ;
203
+ $ h = fopen ($ file , 'ab ' );
204
+
205
+ foreach ($ ids as $ id ) {
206
+ $ ok = fwrite ($ h , rawurlencode ($ id )."\n" ) && $ ok ;
207
+ }
208
+ fclose ($ h );
209
+
210
+ while (!isset ($ linkedTags [$ tag ]) && 0 < $ r = strrpos ($ tag , '/ ' )) {
211
+ $ linkedTags [$ tag ] = true ;
212
+ $ parent = substr ($ tag , 0 , $ r );
213
+ $ file = $ this ->getFile ($ parent , true );
214
+ $ linkedTags [$ parent ] = file_exists ($ file ) ?: null ;
215
+ $ ok = file_put_contents ($ file , rawurlencode ($ tag )."\n" , FILE_APPEND ) && $ ok ;
216
+ $ tag = $ parent ;
217
+ }
218
+ }
219
+
220
+ return $ ok ;
221
+ }
222
+
223
+ private function doInvalidate ($ tag )
224
+ {
225
+ if (!$ h = @fopen ($ this ->getFile ($ tag , false ), 'r+b ' )) {
226
+ return true ;
227
+ }
228
+ $ ok = true ;
229
+
230
+ while (false !== $ id = fgets ($ h )) {
231
+ if ('! ' === $ id [0 ]) {
232
+ continue ;
233
+ }
234
+ $ id = rawurldecode (substr ($ id , 0 , -1 ));
235
+
236
+ if ('/ ' === $ id [0 ]) {
237
+ $ ok = $ this ->doInvalidate ($ id ) && $ ok ;
238
+ } else {
239
+ $ file = $ this ->getFile ($ id , false );
240
+ $ ok = (!file_exists ($ file ) || @unlink ($ file ) || !file_exists ($ file )) && $ ok ;
241
+ }
242
+ }
243
+
244
+ ftruncate ($ h , 0 );
245
+ fclose ($ h );
246
+
247
+ return $ ok ;
248
+ }
249
+
250
+ private function removeTags ($ id , $ tags )
251
+ {
252
+ $ idLine = rawurlencode ($ id )."\n" ;
253
+ $ idSeek = -strlen ($ idLine );
254
+
255
+ foreach ($ tags as $ tag ) {
256
+ if (!$ h = @fopen ($ this ->getFile ($ tag , false ), 'r+b ' )) {
257
+ continue ;
258
+ }
259
+ while (false !== $ line = fgets ($ h )) {
260
+ if ($ line === $ idLine ) {
261
+ fseek ($ h , $ idSeek , SEEK_CUR );
262
+ fwrite ($ h , '! ' );
263
+ }
264
+ }
265
+ fclose ($ h );
266
+ }
267
+ }
268
+
269
+ private function getFile ($ id , $ mkdir )
152
270
{
153
271
$ hash = str_replace ('/ ' , '- ' , base64_encode (md5 ($ id , true )));
272
+ $ dir = $ this ->directory .$ hash [0 ].DIRECTORY_SEPARATOR .$ hash [1 ].DIRECTORY_SEPARATOR ;
273
+
274
+ if ($ mkdir && !file_exists ($ dir )) {
275
+ @mkdir ($ dir , 0777 , true );
276
+ }
154
277
155
- return $ this -> directory . $ hash [ 0 ]. DIRECTORY_SEPARATOR . $ hash [ 1 ]. DIRECTORY_SEPARATOR .substr ($ hash , 2 , -2 );
278
+ return $ dir .substr ($ hash , 2 , -2 );
156
279
}
157
280
}
0 commit comments