@@ -12,7 +12,7 @@ final class Cache
1212 /**
1313 * The cache version.
1414 */
15- private const string CACHE_VERSION = 'v1 ' ;
15+ private const string CACHE_VERSION = 'v2 ' ;
1616
1717 /**
1818 * The cache instance.
@@ -30,15 +30,20 @@ public static function instance(): self
3030 */
3131 public function get (string $ file , callable $ callback ): array
3232 {
33+ $ fileHash = md5_file ($ file );
34+ if ($ fileHash === false ) {
35+ return $ callback ();
36+ }
37+
3338 $ items = $ this ->all ();
3439
35- if (array_key_exists (md5_file ( $ file ) , $ items )) {
36- return $ items [md5_file ( $ file ) ];
40+ if (array_key_exists ($ fileHash , $ items )) {
41+ return $ items [$ fileHash ];
3742 }
3843
3944 $ values = $ callback ();
4045
41- $ this ->persist (md5_file ( $ file ) , $ values );
46+ $ this ->persist ($ fileHash , $ values );
4247
4348 return $ values ;
4449 }
@@ -78,11 +83,7 @@ private function all(): array
7883
7984 $ cache = include $ this ->file ();
8085
81- if (! is_array ($ cache )) {
82- return [];
83- }
84-
85- return $ cache ;
86+ return is_array ($ cache ) ? $ cache : [];
8687 });
8788 }
8889
@@ -91,38 +92,71 @@ private function all(): array
9192 */
9293 private function persist (string $ key , array $ values ): void
9394 {
94- $ cache = $ this ->all ();
95+ $ dirPath = dirname ($ this ->file ());
96+ if (! is_dir ($ dirPath )) {
97+ if (! mkdir ($ dirPath , 0777 , true )) {
98+ return ;
99+ }
100+ chmod ($ dirPath , 0777 );
101+ }
95102
96- $ cache [$ key ] = $ values ;
103+ $ this ->withinLock (function () use ($ key , $ values ) {
104+ $ filePath = $ this ->file ();
105+ $ cache = [];
97106
98- // ensure folder exists
99- if (! is_dir (dirname ($ this ->file ()))) {
100- mkdir (dirname ($ this ->file ()), 0755 , true );
101- }
107+ if (is_file ($ filePath )) {
108+ $ existingCache = include $ filePath ;
109+ if (is_array ($ existingCache )) {
110+ $ cache = $ existingCache ;
111+ }
112+ }
113+
114+ $ cache [$ key ] = $ values ;
115+
116+ $ content = '<?php return ' .var_export ($ cache , true ).'; ' ;
102117
103- $ this ->withinLock (
104- fn () => file_put_contents ($ this ->file (), '<?php return ' .var_export ($ cache , true ).'; ' )
105- );
118+ if (file_put_contents ($ filePath , $ content ) !== false ) {
119+ chmod ($ filePath , 0666 );
120+ }
121+
122+ return null ;
123+ });
106124 }
107125
108126 /**
109127 * Executes the callback within a lock.
110128 */
111129 private function withinLock (callable $ callback ): mixed
112130 {
113- if (! is_file ($ this ->file ())) {
114- return $ callback ();
131+ $ filePath = $ this ->file ();
132+ $ lockPath = $ filePath .'.lock ' ;
133+ $ dirPath = dirname ($ filePath );
134+
135+ if (! is_dir ($ dirPath )) {
136+ mkdir ($ dirPath , 0777 , true );
137+ chmod ($ dirPath , 0777 );
115138 }
116139
117- $ lock = fopen ($ this ->file (), 'c+ ' );
140+ if (! is_file ($ lockPath )) {
141+ touch ($ lockPath );
142+ chmod ($ lockPath , 0666 );
143+ }
118144
145+ $ lock = fopen ($ lockPath , 'c+ ' );
119146 if ($ lock === false ) {
120147 return $ callback ();
121148 }
122149
123- // wait for the lock
124- while (! flock ($ lock , LOCK_EX | LOCK_NB )) {
125- usleep (1 );
150+ $ attempts = 0 ;
151+ while (! flock ($ lock , LOCK_EX | LOCK_NB ) && $ attempts < 100 ) {
152+ usleep (1000 );
153+ $ attempts ++;
154+ }
155+
156+ if ($ attempts >= 100 ) {
157+ fclose ($ lock );
158+
159+ return $ callback ();
126160 }
127161
128162 try {
0 commit comments