1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19: 20: 21: 22: 23:
24: class Cache extends Object implements ArrayAccess
25: {
26:
27: const PRIORITY = 'priority',
28: EXPIRATION = 'expire',
29: EXPIRE = 'expire',
30: SLIDING = 'sliding',
31: TAGS = 'tags',
32: FILES = 'files',
33: ITEMS = 'items',
34: CONSTS = 'consts',
35: CALLBACKS = 'callbacks',
36: ALL = 'all';
37:
38:
39: const NAMESPACE_SEPARATOR = "\x00";
40:
41:
42: private $storage;
43:
44:
45: private $namespace;
46:
47:
48: private $key;
49:
50:
51: private $data;
52:
53:
54:
55: public function __construct(ICacheStorage $storage, $namespace = NULL)
56: {
57: $this->storage = $storage;
58: $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
59: }
60:
61:
62:
63: 64: 65: 66:
67: public function getStorage()
68: {
69: return $this->storage;
70: }
71:
72:
73:
74: 75: 76: 77:
78: public function getNamespace()
79: {
80: return (string) substr($this->namespace, 0, -1);
81: }
82:
83:
84:
85: 86: 87: 88: 89:
90: public function derive($namespace)
91: {
92: $derived = new self($this->storage, $this->namespace . $namespace);
93: return $derived;
94: }
95:
96:
97:
98: 99: 100: 101: 102: 103:
104: public function load($key, $fallback = NULL)
105: {
106: $data = $this->storage->read($this->generateKey($key));
107: if ($data === NULL && $fallback) {
108: return $this->save($key, new Callback($fallback));
109: }
110: return $data;
111: }
112:
113:
114:
115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131:
132: public function save($key, $data, array $dp = NULL)
133: {
134: $this->release();
135: $key = $this->generateKey($key);
136:
137: if ($data instanceof Callback || $data instanceof Closure) {
138: $this->storage->lock($key);
139: $data = Callback::create($data)->invokeArgs(array(&$dp));
140: }
141:
142: if ($data === NULL) {
143: $this->storage->remove($key);
144: } else {
145: $this->storage->write($key, $data, $this->completeDependencies($dp, $data));
146: return $data;
147: }
148: }
149:
150:
151:
152: private function completeDependencies($dp, $data)
153: {
154: if (is_object($data)) {
155: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
156: ClassReflection::from($data)->getAnnotation('serializationVersion'));
157: }
158:
159:
160: if (isset($dp[Cache::EXPIRATION])) {
161: $dp[Cache::EXPIRATION] = DateTime53::from($dp[Cache::EXPIRATION])->format('U') - time();
162: }
163:
164:
165: if (isset($dp[self::FILES])) {
166:
167: foreach (array_unique((array) $dp[self::FILES]) as $item) {
168: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item));
169: }
170: unset($dp[self::FILES]);
171: }
172:
173:
174: if (isset($dp[self::ITEMS])) {
175: $dp[self::ITEMS] = array_unique((array) $dp[self::ITEMS]);
176: foreach ($dp[self::ITEMS] as $k => $item) {
177: $dp[self::ITEMS][$k] = $this->generateKey($item);
178: }
179: }
180:
181:
182: if (isset($dp[self::CONSTS])) {
183: foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
184: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
185: }
186: unset($dp[self::CONSTS]);
187: }
188:
189: if (!is_array($dp)) {
190: $dp = array();
191: }
192: return $dp;
193: }
194:
195:
196:
197: 198: 199: 200: 201:
202: public function remove($key)
203: {
204: $this->save($key, NULL);
205: }
206:
207:
208:
209: 210: 211: 212: 213: 214: 215: 216: 217: 218:
219: public function clean(array $conds = NULL)
220: {
221: $this->release();
222: $this->storage->clean((array) $conds);
223: }
224:
225:
226:
227: 228: 229: 230: 231:
232: public function call($function)
233: {
234: $key = func_get_args();
235: return $this->load($key, create_function('', 'extract(NCFix::$vars['.NCFix::uses(array('function'=>$function,'key'=> $key)).'], EXTR_REFS);
236: array_shift($key);
237: return Callback::create($function)->invokeArgs($key);
238: '));
239: }
240:
241:
242:
243: 244: 245: 246: 247: 248:
249: public function wrap($function, array $dp = NULL)
250: {
251: $cache = $this;
252: return create_function('', 'extract(NCFix::$vars['.NCFix::uses(array('cache'=>$cache,'function'=> $function,'dp'=> $dp)).'], EXTR_REFS);
253: $key = array($function, func_get_args());
254: $data = $cache->load($key);
255: if ($data === NULL) {
256: $data = $cache->save($key, Callback::create($function)->invokeArgs($key[1]), $dp);
257: }
258: return $data;
259: ');
260: }
261:
262:
263:
264: 265: 266: 267: 268:
269: public function start($key)
270: {
271: $data = $this->load($key);
272: if ($data === NULL) {
273: return new CachingHelper($this, $key);
274: }
275: echo $data;
276: }
277:
278:
279:
280: 281: 282: 283: 284: 285:
286: protected function generateKey($key)
287: {
288: return $this->namespace . md5(is_scalar($key) ? $key : serialize($key));
289: }
290:
291:
292:
293:
294:
295:
296:
297: 298: 299: 300: 301: 302: 303:
304: public function offsetSet($key, $data)
305: {
306: $this->save($key, $data);
307: }
308:
309:
310:
311: 312: 313: 314: 315: 316:
317: public function offsetGet($key)
318: {
319: $key = is_scalar($key) ? (string) $key : serialize($key);
320: if ($this->key !== $key) {
321: $this->key = $key;
322: $this->data = $this->load($key);
323: }
324: return $this->data;
325: }
326:
327:
328:
329: 330: 331: 332: 333: 334:
335: public function offsetExists($key)
336: {
337: $this->release();
338: return $this->offsetGet($key) !== NULL;
339: }
340:
341:
342:
343: 344: 345: 346: 347: 348:
349: public function offsetUnset($key)
350: {
351: $this->save($key, NULL);
352: }
353:
354:
355:
356: 357: 358: 359:
360: public function release()
361: {
362: $this->key = $this->data = NULL;
363: }
364:
365:
366:
367:
368:
369:
370:
371: 372: 373: 374: 375:
376: public static function checkCallbacks($callbacks)
377: {
378: foreach ($callbacks as $callback) {
379: $func = array_shift($callback);
380: if (!call_user_func_array($func, $callback)) {
381: return FALSE;
382: }
383: }
384: return TRUE;
385: }
386:
387:
388:
389: 390: 391: 392: 393: 394:
395: private static function checkConst($const, $value)
396: {
397: return defined($const) && constant($const) === $value;
398: }
399:
400:
401:
402: 403: 404: 405: 406: 407:
408: private static function checkFile($file, $time)
409: {
410: return @filemtime($file) == $time;
411: }
412:
413:
414:
415: 416: 417: 418: 419: 420:
421: private static function checkSerializationVersion($class, $value)
422: {
423: return ClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
424: }
425:
426: }
427: