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->generateKey($key));
107: if ($data === NULL && $fallback) {
108: return $this->save($key, new NCallback($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 $dependencies = NULL)
133: {
134: $this->release();
135: $key = $this->generateKey($key);
136:
137: if ($data instanceof NCallback || $data instanceof Closure) {
138: $this->storage->lock($key);
139: $data = NCallback::create($data)->invokeArgs(array(&$dependencies));
140: }
141:
142: if ($data === NULL) {
143: $this->storage->remove($key);
144: } else {
145: $this->storage->write($key, $data, $this->completeDependencies($dependencies, $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_map(array($this, 'generateKey'), (array) $dp[self::ITEMS]));
176: }
177:
178:
179: if (isset($dp[self::CONSTS])) {
180: foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
181: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
182: }
183: unset($dp[self::CONSTS]);
184: }
185:
186: if (!is_array($dp)) {
187: $dp = array();
188: }
189: return $dp;
190: }
191:
192:
193:
194: 195: 196: 197: 198:
199: public function remove($key)
200: {
201: $this->save($key, NULL);
202: }
203:
204:
205:
206: 207: 208: 209: 210: 211: 212: 213:
214: public function clean(array $conditions = NULL)
215: {
216: $this->release();
217: $this->storage->clean((array) $conditions);
218: }
219:
220:
221:
222: 223: 224: 225: 226:
227: public function call($function)
228: {
229: $key = func_get_args();
230: return $this->load($key, create_function('', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('function'=>$function,'key'=> $key)).'-1], EXTR_REFS);
231: array_shift($key);
232: return NCallback::create($function)->invokeArgs($key);
233: '));
234: }
235:
236:
237:
238: 239: 240: 241: 242: 243:
244: public function wrap($function, array $dependencies = NULL)
245: {
246: $cache = $this;
247: return create_function('', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('cache'=>$cache,'function'=> $function,'dependencies'=> $dependencies)).'-1], EXTR_REFS);
248: $_args=func_get_args(); $key = array($function, $_args);
249: $data = $cache->load($key);
250: if ($data === NULL) {
251: $data = $cache->save($key, NCallback::create($function)->invokeArgs($key[1]), $dependencies);
252: }
253: return $data;
254: ');
255: }
256:
257:
258:
259: 260: 261: 262: 263:
264: public function start($key)
265: {
266: $data = $this->load($key);
267: if ($data === NULL) {
268: return new NCachingHelper($this, $key);
269: }
270: echo $data;
271: }
272:
273:
274:
275: 276: 277: 278: 279: 280:
281: protected function generateKey($key)
282: {
283: return $this->namespace . md5(is_scalar($key) ? $key : serialize($key));
284: }
285:
286:
287:
288:
289:
290:
291:
292: 293: 294: 295: 296: 297: 298:
299: public function offsetSet($key, $data)
300: {
301: $this->save($key, $data);
302: }
303:
304:
305:
306: 307: 308: 309: 310: 311:
312: public function offsetGet($key)
313: {
314: $key = is_scalar($key) ? (string) $key : serialize($key);
315: if ($this->key !== $key) {
316: $this->key = $key;
317: $this->data = $this->load($key);
318: }
319: return $this->data;
320: }
321:
322:
323:
324: 325: 326: 327: 328: 329:
330: public function offsetExists($key)
331: {
332: $this->release();
333: return $this->offsetGet($key) !== NULL;
334: }
335:
336:
337:
338: 339: 340: 341: 342: 343:
344: public function offsetUnset($key)
345: {
346: $this->save($key, NULL);
347: }
348:
349:
350:
351: 352: 353: 354:
355: public function release()
356: {
357: $this->key = $this->data = NULL;
358: }
359:
360:
361:
362:
363:
364:
365:
366: 367: 368: 369: 370:
371: public static function checkCallbacks($callbacks)
372: {
373: foreach ($callbacks as $callback) {
374: $func = array_shift($callback);
375: if (!call_user_func_array($func, $callback)) {
376: return FALSE;
377: }
378: }
379: return TRUE;
380: }
381:
382:
383:
384: 385: 386: 387: 388: 389:
390: private static function checkConst($const, $value)
391: {
392: return defined($const) && constant($const) === $value;
393: }
394:
395:
396:
397: 398: 399: 400: 401: 402:
403: private static function checkFile($file, $time)
404: {
405: return @filemtime($file) == $time;
406: }
407:
408:
409:
410: 411: 412: 413: 414: 415:
416: private static function checkSerializationVersion($class, $value)
417: {
418: return NClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
419: }
420:
421: }
422: