22/*
33 Author: Turab Garip
44 https://github.com/Turab
5+ Version: 1.0.1
56 License: GNU GPL v3
67 */
78
89namespace Ellibs \Elcache ;
910
11+ use Exception ;
12+
1013// Maybe later make a layer and move different types of caches to other files?
1114class FileCache {
1215
1316 private $ path ;
1417 private $ options ;
1518 private $ data ;
1619 private $ hash ;
20+ private $ init_time ;
21+ private $ context ;
1722
1823 public function __construct (array $ options ) {
1924
2025 $ this ->options = $ options ;
21- $ this ->path = $ this ->options ['path ' ] . '/elcache.php ' ;
26+ $ this ->init_time = microtime (true );
27+ $ this ->path = $ this ->options ['path ' ] . '/elcache. ' . $ this ->options ['context ' ] . '.php ' ;
2228
23- if (!is_writable ($ this ->options ['path ' ])) {
24- throw new \Exception ('Cache path is not writable! ' );
25- }
29+ if (!is_writable ($ this ->options ['path ' ]))
30+ throw new Exception ('Cache path is not writable! ' );
2631
2732 // Create an empty cache file if it doesn't exist
2833 if (!file_exists ($ this ->path )) {
@@ -33,15 +38,20 @@ public function __construct(array $options) {
3338 return ;
3439 }
3540
36- $ data = file_get_contents ($ this ->path );
41+ list ($ this ->data , $ this ->hash ) = $ this ->retrieve_file ($ this ->path );
42+ }
43+
44+ private function retrieve_file ($ file ): array {
45+ $ data = file_get_contents ($ file );
3746 $ data = substr ($ data , 9 , -6 );
38- $ this -> hash = crc32 ($ data );
47+ $ hash = crc32 ($ data );
3948 $ data = unserialize ($ data );
40- $ this ->data = is_array ($ data ) ? $ data : []; // If data is corrupt, fall back to an empty array
49+ // If data is corrupt, fall back to an empty array
50+ return array (is_array ($ data ) ? $ data : [], $ hash );
4151 }
4252
43- public function set (string $ key , $ value , int $ expiry ): void {
44- $ this ->data [$ key ] = array ($ value , $ expiry );
53+ public function set (string $ key , $ value , int $ ttl ): void {
54+ $ this ->data [$ key ] = array ($ value , $ ttl );
4555 }
4656
4757 public function get (?string $ key = null ) {
@@ -52,24 +62,36 @@ public function revoke(string $key): void {
5262 unset($ this ->data [$ key ]);
5363 }
5464
65+ /**
66+ * @throws Exception
67+ */
5568 public function purge_expired ($ then_write = false ): void {
5669 $ now = time ();
5770 foreach ($ this ->data as $ key => $ cache ) {
58- list (, $ expiry ) = $ cache ;
59- if ($ now > $ expiry )
71+ list (, $ ttl ) = $ cache ;
72+ if ($ now > $ ttl )
6073 $ this ->revoke ($ key );
6174 }
6275 if ($ then_write )
6376 $ this ->write ();
6477 }
6578
79+ /**
80+ * @throws Exception
81+ */
6682 public function purge_all ($ hard = false ): void {
6783 $ this ->data = [];
6884 $ hard ? unlink ($ this ->path ) : $ this ->write (true );
6985 }
7086
7187 public function write ($ force = false ): void {
88+ $ size = $ this ->options ['max_buffer ' ] * 1024 ;
7289 $ data = serialize ($ this ->data );
90+
91+ if (strlen ($ data ) > $ size )
92+ throw new Exception ('Cache data is longer than it is allowed to be!
93+ Either increase the allowed size or revoke keys more occasionally. ' );
94+
7395 $ hash = crc32 ($ data );
7496 // Write the file only if it's forced or cache is updated
7597 if ($ force || $ hash != $ this ->hash ) {
@@ -78,7 +100,16 @@ public function write($force = false): void {
78100 }
79101 }
80102
103+ /**
104+ * @throws Exception
105+ */
81106 public function __destruct () {
107+ // Check if there were other inits that might have modified the cache
108+ if (filemtime ($ this ->path ) > $ this ->init_time ) {
109+ // Re-read the file, because it is modified by another init during our runtime
110+ list ($ addition , ) = $ this ->retrieve_file ($ this ->path );
111+ $ this ->data = array_merge ($ addition , $ this ->data );
112+ }
82113 $ this ->write ();
83114 }
84115}
@@ -95,47 +126,54 @@ class Cache {
95126
96127 private static $ cache ;
97128 private static $ options = array (
98- // Path to store the cache file in
129+ // Path to store the cache files in
99130 'path ' => '/tmp ' ,
100- // Default expiry is 1 hour
101- 'default_expiry ' => 3600
131+ // Default context (Each context will have its own cache file)
132+ 'context ' => 'default ' ,
133+ // Maximum allowed size for the cache, in Kibibytes
134+ 'max_buffer ' => 4096 ,
135+ // Default expiry (time-to-live) is 1 hour
136+ 'ttl ' => 3600
102137 );
103138
104- public static function init (?array $ options = []) {
139+ /**
140+ * @throws Exception
141+ */
142+ public static function init (array $ options = []) {
105143 if (self ::$ cache === null ) {
106144 self ::$ options = array_merge (self ::$ options , $ options );
107145 self ::$ cache = new FileCache (self ::$ options );
108146 }
109147 self ::purge_expired ();
110148 }
111149
112- public static function get (string $ key , $ with_expiry = false ) {
150+ public static function get (string $ key , $ with_ttl = false ) {
113151 $ cache = self ::$ cache ->get ($ key );
114152 if ($ cache !== null ) {
115- list ($ value , $ expiry ) = $ cache ;
153+ list ($ value , $ ttl ) = $ cache ;
116154 // Return with expiry information or only the value
117- if ($ expiry > time ())
118- return !$ with_expiry ? $ value : $ cache ;
155+ if ($ ttl > time ())
156+ return !$ with_ttl ? $ value : $ cache ;
119157 // If it is expired, don't return but rather destroy
120158 self ::revoke ($ key );
121159 }
122- return !$ with_expiry ? null : [null , 0 ];
160+ return !$ with_ttl ? null : [null , 0 ];
123161 }
124162
125- public static function set (string $ key , $ value = null , ?int $ expiry = null ) {
126- if ($ expiry === null )
127- $ expiry = self ::get_option ('default_expiry ' );
128- // Non-positive expiry or null value means revoke
129- if ($ expiry < 1 || $ value === null ) {
130- self ::revoke ($ key );
163+ public static function set (string $ key , $ value = null , ?int $ ttl = null ) {
164+ if ($ ttl === null )
165+ $ ttl = self ::get_option ('ttl ' );
166+ if ($ ttl > 0 && $ value !== null ) {
167+ self ::$ cache ->set ($ key , $ value , $ ttl + time ());
131168 return ;
132169 }
133- $ expiry += time ();
134- self ::$ cache -> set ($ key, $ value , $ expiry );
170+ // Non-positive expiry or null value means revoke
171+ self ::revoke ($ key );
135172 }
136173
137174 public static function check (string $ key , $ value , $ strict = false ): bool {
138- return !$ strict ? self ::get ($ key ) == $ value : self ::get ($ key ) === $ value ;
175+ $ cache = self ::get ($ key );
176+ return !$ strict ? $ cache == $ value : $ cache === $ value ;
139177 }
140178
141179 public static function revoke (string $ key ) {
@@ -158,4 +196,9 @@ public static function write($force = false) {
158196 public static function get_option (string $ option ) {
159197 return self ::$ options [$ option ] ?? null ;
160198 }
199+
200+ // Kill the cache handler (Maybe for reinitializing with a different context.)
201+ public function close () {
202+ self ::$ cache = null ;
203+ }
161204}
0 commit comments