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: public function __construct(ICacheStorage $storage, $namespace = NULL)
55: {
56: $this->storage = $storage;
57: $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
58: }
59:
60:
61: 62: 63: 64:
65: public function getStorage()
66: {
67: return $this->storage;
68: }
69:
70:
71: 72: 73: 74:
75: public function getNamespace()
76: {
77: return (string) substr($this->namespace, 0, -1);
78: }
79:
80:
81: 82: 83: 84: 85:
86: public function derive($namespace)
87: {
88: $derived = new self($this->storage, $this->namespace . $namespace);
89: return $derived;
90: }
91:
92:
93: 94: 95: 96: 97: 98:
99: public function load($key, $fallback = NULL)
100: {
101: $data = $this->storage->read($this->generateKey($key));
102: if ($data === NULL && $fallback) {
103: return $this->save($key, new NCallback($fallback));
104: }
105: return $data;
106: }
107:
108:
109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125:
126: public function save($key, $data, array $dependencies = NULL)
127: {
128: $this->release();
129: $key = $this->generateKey($key);
130:
131: if ($data instanceof NCallback || $data instanceof Closure) {
132: $this->storage->lock($key);
133: $data = NCallback::create($data)->invokeArgs(array(& $dependencies));
134: }
135:
136: if ($data === NULL) {
137: $this->storage->remove($key);
138: } else {
139: $this->storage->write($key, $data, $this->completeDependencies($dependencies, $data));
140: return $data;
141: }
142: }
143:
144:
145: private function completeDependencies($dp, $data)
146: {
147: if (is_object($data)) {
148: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
149: NClassReflection::from($data)->getAnnotation('serializationVersion'));
150: }
151:
152:
153: if (isset($dp[NCache::EXPIRATION])) {
154: $dp[NCache::EXPIRATION] = NDateTime53::from($dp[NCache::EXPIRATION])->format('U') - time();
155: }
156:
157:
158: if (isset($dp[self::FILES])) {
159:
160: foreach (array_unique((array) $dp[self::FILES]) as $item) {
161: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item));
162: }
163: unset($dp[self::FILES]);
164: }
165:
166:
167: if (isset($dp[self::ITEMS])) {
168: $dp[self::ITEMS] = array_unique(array_map(array($this, 'generateKey'), (array) $dp[self::ITEMS]));
169: }
170:
171:
172: if (isset($dp[self::CONSTS])) {
173: foreach (array_unique((array) $dp[self::CONSTS]) as $item) {
174: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
175: }
176: unset($dp[self::CONSTS]);
177: }
178:
179: if (!is_array($dp)) {
180: $dp = array();
181: }
182: return $dp;
183: }
184:
185:
186: 187: 188: 189: 190:
191: public function remove($key)
192: {
193: $this->save($key, NULL);
194: }
195:
196:
197: 198: 199: 200: 201: 202: 203: 204:
205: public function clean(array $conditions = NULL)
206: {
207: $this->release();
208: $this->storage->clean((array) $conditions);
209: }
210:
211:
212: 213: 214: 215: 216:
217: public function call($function)
218: {
219: $key = func_get_args();
220: return $this->load($key, create_function('', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('function'=>$function,'key'=> $key)).'-1], EXTR_REFS);
221: return NCallback::create($function)->invokeArgs(array_slice($key, 1));
222: '));
223: }
224:
225:
226: 227: 228: 229: 230: 231:
232: public function wrap($function, array $dependencies = NULL)
233: {
234: $cache = $this;
235: return create_function('', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('cache'=>$cache,'function'=> $function,'dependencies'=> $dependencies)).'-1], EXTR_REFS);
236: $_args=func_get_args(); $key = array($function, $_args);
237: $data = $cache->load($key);
238: if ($data === NULL) {
239: $data = $cache->save($key, NCallback::create($function)->invokeArgs($key[1]), $dependencies);
240: }
241: return $data;
242: ');
243: }
244:
245:
246: 247: 248: 249: 250:
251: public function start($key)
252: {
253: $data = $this->load($key);
254: if ($data === NULL) {
255: return new NCachingHelper($this, $key);
256: }
257: echo $data;
258: }
259:
260:
261: 262: 263: 264: 265: 266:
267: protected function generateKey($key)
268: {
269: return $this->namespace . md5(is_scalar($key) ? $key : serialize($key));
270: }
271:
272:
273:
274:
275:
276: 277: 278: 279: 280: 281: 282:
283: public function offsetSet($key, $data)
284: {
285: $this->save($key, $data);
286: }
287:
288:
289: 290: 291: 292: 293: 294:
295: public function offsetGet($key)
296: {
297: $key = is_scalar($key) ? (string) $key : serialize($key);
298: if ($this->key !== $key) {
299: $this->key = $key;
300: $this->data = $this->load($key);
301: }
302: return $this->data;
303: }
304:
305:
306: 307: 308: 309: 310: 311:
312: public function offsetExists($key)
313: {
314: $this->release();
315: return $this->offsetGet($key) !== NULL;
316: }
317:
318:
319: 320: 321: 322: 323: 324:
325: public function offsetUnset($key)
326: {
327: $this->save($key, NULL);
328: }
329:
330:
331: 332: 333: 334:
335: public function release()
336: {
337: $this->key = $this->data = NULL;
338: }
339:
340:
341:
342:
343:
344: 345: 346: 347: 348:
349: public static function checkCallbacks($callbacks)
350: {
351: foreach ($callbacks as $callback) {
352: if (!call_user_func_array(array_shift($callback), $callback)) {
353: return FALSE;
354: }
355: }
356: return TRUE;
357: }
358:
359:
360: 361: 362: 363: 364: 365:
366: private static function checkConst($const, $value)
367: {
368: return defined($const) && constant($const) === $value;
369: }
370:
371:
372: 373: 374: 375: 376: 377:
378: private static function checkFile($file, $time)
379: {
380: return @filemtime($file) == $time;
381: }
382:
383:
384: 385: 386: 387: 388: 389:
390: private static function checkSerializationVersion($class, $value)
391: {
392: return NClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
393: }
394:
395: }
396: