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 NCache extends NObject 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->namespace . md5(is_scalar($key) ? $key : serialize($key)));
107: if ($data === NULL && $fallback) {
108: return $this->save($key, 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->namespace . md5(is_scalar($key) ? $key : serialize($key));
136:
137: if ($data instanceof NCallback || $data instanceof Closure) {
138: $this->storage->lock($key);
139: $data = callback($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: NClassReflection::from($data)->getAnnotation('serializationVersion'));
157: }
158:
159:
160: if (isset($dp[NCache::EXPIRATION])) {
161: $dp[NCache::EXPIRATION] = NDateTime53::from($dp[NCache::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->namespace . md5(is_scalar($item) ? $item : serialize($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 call_user_func_array($function, $key);
238: '));
239: }
240:
241:
242:
243: 244: 245: 246: 247:
248: public function wrap($function)
249: {
250: $cache = $this;
251: return create_function('', 'extract(NCFix::$vars['.NCFix::uses(array('cache'=>$cache,'function'=> $function)).'], EXTR_REFS);
252: $key = array($function, func_get_args());
253: $data = $cache->load($key);
254: if ($data === NULL) {
255: $data = $cache->save($key, call_user_func_array($function, $key[1]));
256: }
257: return $data;
258: ');
259: }
260:
261:
262:
263: 264: 265: 266: 267:
268: public function start($key)
269: {
270: $data = $this->load($key);
271: if ($data === NULL) {
272: return new NCachingHelper($this, $key);
273: }
274: echo $data;
275: }
276:
277:
278:
279:
280:
281:
282:
283: 284: 285: 286: 287: 288: 289:
290: public function offsetSet($key, $data)
291: {
292: $this->save($key, $data);
293: }
294:
295:
296:
297: 298: 299: 300: 301: 302:
303: public function offsetGet($key)
304: {
305: $key = is_scalar($key) ? (string) $key : serialize($key);
306: if ($this->key !== $key) {
307: $this->key = $key;
308: $this->data = $this->load($key);
309: }
310: return $this->data;
311: }
312:
313:
314:
315: 316: 317: 318: 319: 320:
321: public function offsetExists($key)
322: {
323: $this->release();
324: return $this->offsetGet($key) !== NULL;
325: }
326:
327:
328:
329: 330: 331: 332: 333: 334:
335: public function offsetUnset($key)
336: {
337: $this->save($key, NULL);
338: }
339:
340:
341:
342: 343: 344: 345:
346: public function release()
347: {
348: $this->key = $this->data = NULL;
349: }
350:
351:
352:
353:
354:
355:
356:
357: 358: 359: 360: 361:
362: public static function checkCallbacks($callbacks)
363: {
364: foreach ($callbacks as $callback) {
365: $func = array_shift($callback);
366: if (!call_user_func_array($func, $callback)) {
367: return FALSE;
368: }
369: }
370: return TRUE;
371: }
372:
373:
374:
375: 376: 377: 378: 379: 380:
381: private static function checkConst($const, $value)
382: {
383: return defined($const) && constant($const) === $value;
384: }
385:
386:
387:
388: 389: 390: 391: 392: 393:
394: private static function checkFile($file, $time)
395: {
396: return @filemtime($file) == $time;
397: }
398:
399:
400:
401: 402: 403: 404: 405: 406:
407: private static function checkSerializationVersion($class, $value)
408: {
409: return NClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
410: }
411:
412: }
413: