1111
1212final class FileCache implements FileCacheInterface
1313{
14+ /**
15+ * @var array<string, CacheItemInterface>
16+ */
1417 private array $ deferred = [];
1518
1619 /**
@@ -27,7 +30,7 @@ public function __construct(
2730 $ directory = $ this ->getDefaultDirectory ();
2831 }
2932
30- $ this ->validateNamespace ($ namespace );
33+ $ this ->validateNamespace ();
3134
3235 $ directory .= DIRECTORY_SEPARATOR . $ namespace ;
3336
@@ -57,31 +60,46 @@ public function getItem(string $key): CacheItemInterface
5760 return $ item ;
5861 }
5962
60- $ handle = fopen ($ filepath , 'r ' );
61- if (flock ($ handle , LOCK_SH )) { // Shared lock for reading
62- try {
63- $ data = fread ($ handle , filesize ($ filepath ));
64- $ value = unserialize ($ data );
63+ $ data = \PhpSpellcheck \file_get_contents ($ filepath );
64+
65+ if ($ data === '' ) {
66+ return $ item ;
67+ }
68+
69+ $ value = unserialize ($ data );
6570
66- if ($ value && (!$ value ->expiresAt || $ value ->expiresAt > time ())) {
67- $ item ->set ($ value ->data );
68- $ item ->setIsHit (true );
69- if ($ value ->expiresAt ) {
70- $ item ->expiresAt (new \DateTime ('@ ' . $ value ->expiresAt ));
71- }
72- }
73- } finally {
74- flock ($ handle , LOCK_UN );
75- fclose ($ handle );
76- }
71+ if (! is_object ($ value )
72+ || ! property_exists ($ value , 'data ' )
73+ || ! property_exists ($ value , 'expiresAt ' )
74+ ) {
75+ return $ item ;
76+ }
77+
78+ if ($ value ->expiresAt !== 0
79+ && $ value ->expiresAt !== null
80+ && $ value ->expiresAt <= time ()
81+ ) {
82+ unlink ($ filepath );
83+
84+ return $ item ;
85+ }
86+
87+ $ item ->set ($ value ->data )->setIsHit (true );
88+
89+ if (is_int ($ value ->expiresAt ) && $ value ->expiresAt > 0 ) {
90+ $ item ->expiresAt (new \DateTime ('@ ' . $ value ->expiresAt ));
7791 }
7892
7993 return $ item ;
8094 }
8195
96+ /**
97+ * @param array<string> $keys
98+ * @return iterable<CacheItemInterface>
99+ */
82100 public function getItems (array $ keys = []): iterable
83101 {
84- return array_map (fn ($ key ) => $ this ->getItem ($ key ), $ keys );
102+ return array_map (fn ($ key ): CacheItemInterface => $ this ->getItem ($ key ), $ keys );
85103 }
86104
87105 public function hasItem (string $ key ): bool
@@ -132,13 +150,16 @@ public function save(CacheItemInterface $item): bool
132150 {
133151 $ this ->validateKey ($ item ->getKey ());
134152
135- $ expiresAt = null ;
136- if ($ item ->expiry ) {
137- $ expiresAt = $ item ->expiry ->getTimestamp ();
138- } elseif ($ this ->defaultLifetime > 0 ) {
139- $ expiresAt = time () + $ this ->defaultLifetime ;
153+ if (! property_exists ($ item , 'expiry ' )) {
154+ throw new InvalidArgumentException ('CacheItem expiry property is required ' );
140155 }
141156
157+ $ expiresAt = match (true ) {
158+ $ item ->expiry instanceof \DateTimeInterface => $ item ->expiry ->getTimestamp (),
159+ $ this ->defaultLifetime > 0 => time () + $ this ->defaultLifetime ,
160+ default => null
161+ };
162+
142163 $ value = (object ) [
143164 'data ' => $ item ->get (),
144165 'expiresAt ' => $ expiresAt ,
@@ -147,22 +168,11 @@ public function save(CacheItemInterface $item): bool
147168 $ serialized = serialize ($ value );
148169 $ filepath = $ this ->getFilePath ($ item ->getKey ());
149170
150- $ handle = fopen ($ filepath , 'w ' );
151- if (!$ handle ) {
171+ try {
172+ return (bool ) \PhpSpellcheck \file_put_contents ($ filepath , $ serialized , LOCK_EX );
173+ } catch (\Exception $ e ) {
152174 return false ;
153175 }
154-
155- $ success = false ;
156- if (flock ($ handle , LOCK_EX )) { // Exclusive lock for writing
157- try {
158- $ success = fwrite ($ handle , $ serialized ) !== false ;
159- } finally {
160- flock ($ handle , LOCK_UN );
161- fclose ($ handle );
162- }
163- }
164-
165- return $ success ;
166176 }
167177
168178 public function saveDeferred (CacheItemInterface $ item ): bool
@@ -194,9 +204,9 @@ public function getFilePath(string $key): string
194204 return $ this ->directory . $ key ;
195205 }
196206
197- private function validateNamespace (string $ namespace ): void
207+ private function validateNamespace (): void
198208 {
199- if (\PhpSpellcheck \preg_match ('#[^-+_.A-Za-z0-9]# ' , $ namespace , $ match ) === 1 ) {
209+ if (\PhpSpellcheck \preg_match ('#[^-+_.A-Za-z0-9]# ' , $ this -> namespace , $ match ) === 1 ) {
200210 throw new InvalidArgumentException (sprintf ('Namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed. ' , $ match [0 ]));
201211 }
202212 }
0 commit comments