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 <[email protected] >
18
19
*/
19
- class FilesystemAdapter extends AbstractAdapter
20
+ class FilesystemAdapter extends AbstractAdapter implements TagInvalidationInterface
20
21
{
21
22
private $ directory ;
22
23
@@ -50,6 +51,23 @@ 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
+ foreach ($ this ->getInvalidatedIds ($ tag ) as $ id ) {
63
+ $ file = $ this ->getFile ($ id , false );
64
+ $ ok = (!file_exists ($ file ) || @unlink ($ file ) || !file_exists ($ file )) && $ ok ;
65
+ }
66
+ }
67
+
68
+ return $ ok ;
69
+ }
70
+
53
71
/**
54
72
* {@inheritdoc}
55
73
*/
@@ -59,7 +77,7 @@ protected function doFetch(array $ids)
59
77
$ now = time ();
60
78
61
79
foreach ($ ids as $ id ) {
62
- $ file = $ this ->getFile ($ id );
80
+ $ file = $ this ->getFile ($ id, false );
63
81
if (!$ h = @fopen ($ file , 'rb ' )) {
64
82
continue ;
65
83
}
@@ -89,7 +107,7 @@ protected function doFetch(array $ids)
89
107
*/
90
108
protected function doHave ($ id )
91
109
{
92
- $ file = $ this ->getFile ($ id );
110
+ $ file = $ this ->getFile ($ id, false );
93
111
94
112
return file_exists ($ file ) && (@filemtime ($ file ) > time () || $ this ->doFetch (array ($ id )));
95
113
}
@@ -116,7 +134,7 @@ protected function doDelete(array $ids)
116
134
$ ok = true ;
117
135
118
136
foreach ($ ids as $ id ) {
119
- $ file = $ this ->getFile ($ id );
137
+ $ file = $ this ->getFile ($ id, false );
120
138
$ ok = (!file_exists ($ file ) || @unlink ($ file ) || !file_exists ($ file )) && $ ok ;
121
139
}
122
140
@@ -130,28 +148,120 @@ protected function doSave(array $values, $lifetime)
130
148
{
131
149
$ ok = true ;
132
150
$ expiresAt = $ lifetime ? time () + $ lifetime : PHP_INT_MAX ;
151
+ $ tags = array ();
133
152
134
153
foreach ($ values as $ id => $ value ) {
135
- $ file = $ this ->getFile ($ id );
136
- $ dir = dirname ($ file );
137
- if (!file_exists ($ dir )) {
138
- @mkdir ($ dir , 0777 , true );
154
+ if (file_exists ($ file = $ this ->getFile ($ id .':tag ' , false ))) {
155
+ $ tags = file ($ file , FILE_IGNORE_NEW_LINES );
156
+ @unlink ($ file );
139
157
}
158
+ $ file = $ this ->getFile ($ id , true );
140
159
$ value = $ expiresAt ."\n" .rawurlencode ($ id )."\n" .serialize ($ value );
141
160
if (false !== @file_put_contents ($ file , $ value , LOCK_EX )) {
142
161
@touch ($ file , $ expiresAt );
143
162
} else {
144
163
$ ok = false ;
145
164
}
165
+ if ($ tags ) {
166
+ $ this ->removeTags ($ id , $ tags );
167
+ $ tags = array ();
168
+ }
169
+ }
170
+
171
+ return $ ok ;
172
+ }
173
+
174
+ /**
175
+ * {@inheritdoc}
176
+ */
177
+ protected function doTag (array $ tags )
178
+ {
179
+ $ ok = true ;
180
+ $ byIds = array ();
181
+
182
+ foreach ($ tags as $ tag => $ ids ) {
183
+ $ file = $ this ->getFile ($ tag , true );
184
+ $ h = fopen ($ file , 'ab ' );
185
+
186
+ foreach ($ ids as $ id ) {
187
+ $ ok = fwrite ($ h , rawurlencode ($ id )."\n" ) && $ ok ;
188
+ $ byIds [$ id ][] = $ tag ;
189
+ }
190
+ fclose ($ h );
191
+ }
192
+ foreach ($ byIds as $ id => $ tags ) {
193
+ $ file = $ this ->getFile ($ id .':tag ' , true );
194
+ $ h = fopen ($ file , 'ab ' );
195
+
196
+ foreach ($ tags as $ tag ) {
197
+ fwrite ($ h , rawurlencode ($ tag )."\n" );
198
+ }
199
+ fclose ($ h );
200
+ }
201
+ while (0 < $ r = strrpos ($ tag , '/ ' )) {
202
+ $ parent = substr ($ tag , 0 , $ r );
203
+ $ ok = file_put_contents ($ this ->getFile ($ parent , true ), rawurlencode ($ tag )."\n" , FILE_APPEND ) && $ ok ;
204
+ $ tag = $ parent ;
146
205
}
147
206
148
207
return $ ok ;
149
208
}
150
209
151
- private function getFile ($ id )
210
+ private function getInvalidatedIds ($ tag )
211
+ {
212
+ $ file = $ this ->getFile ($ tag , false );
213
+
214
+ if ($ h = @fopen ($ file , 'rb ' )) {
215
+ while (false !== $ id = fgets ($ h )) {
216
+ if ('! ' === $ id [0 ]) {
217
+ continue ;
218
+ }
219
+ $ id = rawurldecode (substr ($ id , 0 , -1 ));
220
+
221
+ if ('/ ' === $ id [0 ]) {
222
+ foreach ($ this ->getInvalidatedIds ($ id ) as $ id ) {
223
+ yield $ id ;
224
+ }
225
+ } else {
226
+ yield $ id ;
227
+ }
228
+ }
229
+
230
+ fclose ($ h );
231
+ @unlink ($ file );
232
+ }
233
+ }
234
+
235
+ private function removeTags ($ id , $ tags )
236
+ {
237
+ $ idLine = rawurlencode ($ id )."\n" ;
238
+ $ idSeek = -strlen ($ idLine );
239
+
240
+ foreach ($ tags as $ tag ) {
241
+ if (!file_exists ($ file = $ this ->getFile (rawurldecode ($ tag ), false ))) {
242
+ continue ;
243
+ }
244
+ $ h = fopen ($ file , 'r+b ' );
245
+
246
+ while (false !== $ line = fgets ($ h )) {
247
+ if ($ line === $ idLine ) {
248
+ fseek ($ h , $ idSeek , SEEK_CUR );
249
+ fwrite ($ h , '! ' );
250
+ }
251
+ }
252
+ fclose ($ h );
253
+ }
254
+ }
255
+
256
+ private function getFile ($ id , $ mkdir )
152
257
{
153
258
$ hash = str_replace ('/ ' , '- ' , base64_encode (md5 ($ id , true )));
259
+ $ dir = $ this ->directory .$ hash [0 ].DIRECTORY_SEPARATOR .$ hash [1 ].DIRECTORY_SEPARATOR ;
260
+
261
+ if ($ mkdir && !file_exists ($ dir )) {
262
+ @mkdir ($ dir , 0777 , true );
263
+ }
154
264
155
- return $ this -> directory . $ hash [ 0 ]. DIRECTORY_SEPARATOR . $ hash [ 1 ]. DIRECTORY_SEPARATOR .substr ($ hash , 2 , -2 );
265
+ return $ dir .substr ($ hash , 2 , -2 );
156
266
}
157
267
}
0 commit comments