22
33namespace ByJG \Cache \Psr16 ;
44
5- use ByJG \Cache \CacheLockInterface ;
5+ use ByJG \Cache \GarbageCollectorInterface ;
66use DateInterval ;
77use Exception ;
88use Psr \Container \ContainerExceptionInterface ;
99use Psr \Container \NotFoundExceptionInterface ;
1010use Psr \Log \LoggerInterface ;
1111use Psr \Log \NullLogger ;
12- use Psr \SimpleCache \InvalidArgumentException ;
1312
14- class FileSystemCacheEngine extends BaseCacheEngine implements CacheLockInterface
13+ class FileSystemCacheEngine extends BaseCacheEngine implements GarbageCollectorInterface
1514{
1615
1716 protected ?LoggerInterface $ logger = null ;
@@ -45,29 +44,11 @@ public function get(string $key, mixed $default = null): mixed
4544 {
4645 // Check if file is Locked
4746 $ fileKey = $ this ->fixKey ($ key );
48- $ lockFile = $ fileKey . ".lock " ;
49- if (file_exists ($ lockFile )) {
50- $ this ->logger ->info ("[Filesystem cache] Locked! $ key. Waiting... " );
51- $ lockTime = filemtime ($ lockFile );
52-
53- while (true ) {
54- if (!file_exists ($ lockFile )) {
55- $ this ->logger ->info ("[Filesystem cache] Lock released for ' $ key' " );
56- break ;
57- }
58- if (intval (time () - $ lockTime ) > 20 ) { // Wait for 10 seconds
59- $ this ->logger ->info ("[Filesystem cache] Gave up to wait unlock. Release lock for ' $ key' " );
60- $ this ->unlock ($ key );
61- return $ default ;
62- }
63- sleep (1 ); // 1 second
64- }
65- }
6647
6748 // Check if file exists
6849 if ($ this ->has ($ key )) {
6950 $ this ->logger ->info ("[Filesystem cache] Get ' $ key' " );
70- return unserialize ( file_get_contents ( $ fileKey) );
51+ return $ this -> getContents ( $ fileKey, $ default );
7152 } else {
7253 $ this ->logger ->info ("[Filesystem cache] Not found ' $ key' " );
7354 return $ default ;
@@ -108,12 +89,7 @@ public function set(string $key, mixed $value, DateInterval|int|null $ttl = null
10889 if (is_string ($ value ) && (strlen ($ value ) === 0 )) {
10990 touch ($ fileKey );
11091 } else {
111- file_put_contents ($ fileKey , serialize ($ value ));
112- }
113-
114- $ validUntil = $ this ->addToNow ($ ttl );
115- if (!empty ($ validUntil )) {
116- file_put_contents ($ fileKey . ".ttl " , (string )$ validUntil );
92+ $ this ->putContents ($ fileKey , $ value , $ this ->addToNow ($ ttl ));
11793 }
11894 } catch (Exception $ ex ) {
11995 $ this ->logger ->warning ("[Filesystem cache] I could not write to cache on file ' " . basename ($ key ) . "'. Switching to nocache=true mode. " );
@@ -133,39 +109,6 @@ public function delete(string $key): bool
133109 return true ;
134110 }
135111
136- /**
137- * Lock resource before set it.
138- * @param string $key
139- */
140- public function lock (string $ key ): void
141- {
142- $ this ->logger ->info ("[Filesystem cache] Lock ' $ key' " );
143-
144- $ lockFile = $ this ->fixKey ($ key ) . ".lock " ;
145-
146- try {
147- file_put_contents ($ lockFile , date ('c ' ));
148- } catch (Exception $ ex ) {
149- // Ignoring... Set will cause an error
150- }
151- }
152-
153- /**
154- * UnLock resource after set it.
155- * @param string $key
156- */
157- public function unlock (string $ key ): void
158- {
159-
160- $ this ->logger ->info ("[Filesystem cache] Unlock ' $ key' " );
161-
162- $ lockFile = $ this ->fixKey ($ key ) . ".lock " ;
163-
164- if (file_exists ($ lockFile )) {
165- unlink ($ lockFile );
166- }
167- }
168-
169112 /**
170113 * @throws ContainerExceptionInterface
171114 * @throws NotFoundExceptionInterface
@@ -227,9 +170,10 @@ public function clear(): bool
227170 public function has (string $ key ): bool
228171 {
229172 $ fileKey = $ this ->fixKey ($ key );
173+ $ fileTtl = null ;
230174 if (file_exists ($ fileKey )) {
231175 if (file_exists ("$ fileKey.ttl " )) {
232- $ fileTtl = intval (file_get_contents ("$ fileKey.ttl " ));
176+ $ fileTtl = intval ($ this -> getContents ("$ fileKey.ttl " ));
233177 }
234178
235179 if (!empty ($ fileTtl ) && time () >= $ fileTtl ) {
@@ -244,4 +188,66 @@ public function has(string $key): bool
244188
245189 return false ;
246190 }
191+
192+ protected function getContents (string $ fileKey , mixed $ default = null ): mixed
193+ {
194+ if (!file_exists ($ fileKey )) {
195+ return $ default ;
196+ }
197+
198+ $ fo = fopen ($ fileKey , 'r ' );
199+ $ waitIfLocked = 1 ;
200+ $ lock = flock ($ fo , LOCK_EX , $ waitIfLocked );
201+ try {
202+ $ content = unserialize (file_get_contents ($ fileKey ));
203+ } finally {
204+ flock ($ fo , LOCK_UN );
205+ fclose ($ fo );
206+ }
207+
208+ return $ content ;
209+ }
210+
211+ protected function putContents (string $ fileKey , mixed $ value , ?string $ ttl ): void
212+ {
213+ $ fo = fopen ($ fileKey , 'w ' );
214+ $ waitIfLocked = 1 ;
215+ $ lock = flock ($ fo , LOCK_EX , $ waitIfLocked );
216+ try {
217+ file_put_contents ($ fileKey , serialize ($ value ));
218+ if (!is_null ($ ttl )) {
219+ file_put_contents ("$ fileKey.ttl " , serialize ($ ttl ));
220+ }
221+ } finally {
222+ flock ($ fo , LOCK_UN );
223+ fclose ($ fo );
224+ }
225+ }
226+
227+ public function collectGarbage ()
228+ {
229+ $ patternKey = $ this ->fixKey ('* ' );
230+ $ list = glob ("$ patternKey.ttl " );
231+ foreach ($ list as $ file ) {
232+ $ fileTtl = intval ($ this ->getContents ($ file ));
233+ if (time () >= $ fileTtl ) {
234+ $ fileContent = str_replace ('.ttl ' , '' , $ file );
235+ if (file_exists ($ fileContent )) {
236+ unlink ($ fileContent );
237+ }
238+ unlink ($ file );
239+ }
240+ }
241+ return true ;
242+ }
243+
244+
245+ public function getTtl (string $ key ): ?int
246+ {
247+ $ fileKey = $ this ->fixKey ($ key );
248+ if (file_exists ("$ fileKey.ttl " )) {
249+ return intval ($ this ->getContents ("$ fileKey.ttl " ));
250+ }
251+ return null ;
252+ }
247253}
0 commit comments