@@ -60,6 +60,9 @@ public static function getCached(string $userAgent): ?array
6060 if ($ exists ) {
6161 $ values = @include ($ path );
6262 if (!empty ($ values ) && is_array ($ values ) && isset ($ values ['os ' ])) {
63+ // Keep the filesystem access metadata in sync with cache reads so later eviction
64+ // can preserve entries that were just reused during the warm-cache command.
65+ self ::refreshAccessTime ($ path );
6366 return $ values ;
6467 }
6568 }
@@ -165,6 +168,26 @@ private static function getCacheFilesInCacheDir(): array
165168 });
166169 }
167170
171+ private static function refreshAccessTime (string $ path ): void
172+ {
173+ // Read the current mtime first so we can move the cache file's recency marker forward even
174+ // when the file was created earlier in the same second as the cache hit.
175+ $ modifiedTime = @filemtime ($ path );
176+ if ($ modifiedTime === false ) {
177+ return ;
178+ }
179+
180+ // Use a strictly newer timestamp for both mtime and atime so cache eviction has a stable
181+ // ordering even on filesystems with one-second timestamp granularity.
182+ $ refreshedTime = max (time (), $ modifiedTime + 1 );
183+
184+ // Refresh the stat cache around touch() so the subsequent file metadata reads in the same
185+ // PHP process observe the updated recency instead of stale stat data.
186+ clearstatcache (true , $ path );
187+ @touch ($ path , $ refreshedTime , $ refreshedTime );
188+ clearstatcache (true , $ path );
189+ }
190+
168191 public static function deleteLeastAccessedFiles (int $ numFilesToDelete ): void
169192 {
170193 if ($ numFilesToDelete < 1 ) {
@@ -173,7 +196,9 @@ public static function deleteLeastAccessedFiles(int $numFilesToDelete): void
173196 $ files = self ::getCacheFilesInCacheDir ();
174197 $ accessed = [];
175198 foreach ($ files as $ file ) {
176- $ accessed [$ file ] = fileatime ($ file );
199+ // Read the same recency marker that getCached() updates so eviction keeps the cache
200+ // entry that was actually reused during the current warm-cache run.
201+ $ accessed [$ file ] = self ::getLastAccessTimestamp ($ file );
177202 }
178203
179204 // have most recently accessed files at the end of the array and delete entries from the beginning of the array
@@ -189,4 +214,18 @@ public static function deleteLeastAccessedFiles(int $numFilesToDelete): void
189214 }
190215 }
191216 }
217+
218+ private static function getLastAccessTimestamp (string $ path ): int
219+ {
220+ // Clear stat cache before reading metadata so the eviction pass sees the timestamp updates
221+ // performed earlier in the same process by refreshAccessTime().
222+ clearstatcache (true , $ path );
223+
224+ // Prefer the newest of atime and mtime because refreshAccessTime() updates both values to
225+ // give us a deterministic recency order on filesystems with coarse timestamp precision.
226+ $ accessTime = @fileatime ($ path );
227+ $ modifiedTime = @filemtime ($ path );
228+
229+ return max ((int ) $ accessTime , (int ) $ modifiedTime );
230+ }
192231}
0 commit comments