1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19:
20: class NCache extends NObject implements ArrayAccess
21: {
22:
23: const PRIORITY = 'priority',
24: EXPIRATION = 'expire',
25: EXPIRE = 'expire',
26: SLIDING = 'sliding',
27: TAGS = 'tags',
28: FILES = 'files',
29: ITEMS = 'items',
30: CONSTS = 'consts',
31: CALLBACKS = 'callbacks',
32: ALL = 'all';
33:
34:
35: const NAMESPACE_SEPARATOR = "\x00";
36:
37:
38: private $storage;
39:
40:
41: private $namespace;
42:
43:
44: private $key;
45:
46:
47: private $data;
48:
49:
50:
51: public function __construct(ICacheStorage $storage, $namespace = NULL)
52: {
53: $this->storage = $storage;
54: $this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
55: }
56:
57:
58:
59: 60: 61: 62:
63: public function getStorage()
64: {
65: return $this->storage;
66: }
67:
68:
69:
70: 71: 72: 73:
74: public function getNamespace()
75: {
76: return (string) substr($this->namespace, 0, -1);
77: }
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: public function release()
99: {
100: $this->key = $this->data = NULL;
101: }
102:
103:
104:
105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121:
122: public function save($key, $data, array $dp = NULL)
123: {
124: $this->key = is_scalar($key) ? (string) $key : serialize($key);
125: $key = $this->namespace . md5($this->key);
126:
127: 128: if (isset($dp[NCache::EXPIRATION])) {
129: $dp[NCache::EXPIRATION] = NDateTime53::from($dp[NCache::EXPIRATION])->format('U') - time();
130: }
131:
132: 133: if (isset($dp[self::FILES])) {
134: 135: foreach ((array) $dp[self::FILES] as $item) {
136: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); 137: }
138: unset($dp[self::FILES]);
139: }
140:
141: 142: if (isset($dp[self::ITEMS])) {
143: $dp[self::ITEMS] = (array) $dp[self::ITEMS];
144: foreach ($dp[self::ITEMS] as $k => $item) {
145: $dp[self::ITEMS][$k] = $this->namespace . md5(is_scalar($item) ? $item : serialize($item));
146: }
147: }
148:
149: 150: if (isset($dp[self::CONSTS])) {
151: foreach ((array) $dp[self::CONSTS] as $item) {
152: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
153: }
154: unset($dp[self::CONSTS]);
155: }
156:
157: if ($data instanceof NCallback || $data instanceof Closure) {
158: NCriticalSection::enter();
159: $data = $data->__invoke();
160: NCriticalSection::leave();
161: }
162:
163: if (is_object($data)) {
164: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
165: NClassReflection::from($data)->getAnnotation('serializationVersion'));
166: }
167:
168: $this->data = $data;
169: if ($data === NULL) {
170: $this->storage->remove($key);
171: } else {
172: $this->storage->write($key, $data, (array) $dp);
173: }
174: return $data;
175: }
176:
177:
178:
179: 180: 181: 182: 183: 184: 185: 186: 187: 188:
189: public function clean(array $conds = NULL)
190: {
191: $this->release();
192: $this->storage->clean((array) $conds);
193: }
194:
195:
196:
197: 198: 199: 200: 201:
202: public function call($function)
203: {
204: $key = func_get_args();
205: if ($this->offsetGet($key) === NULL) {
206: array_shift($key);
207: return $this->save($this->key, call_user_func_array($function, $key));
208: } else {
209: return $this->data;
210: }
211: }
212:
213:
214:
215:
216:
217:
218:
219: 220: 221: 222: 223: 224: 225:
226: public function offsetSet($key, $data)
227: {
228: $this->save($key, $data);
229: }
230:
231:
232:
233: 234: 235: 236: 237: 238:
239: public function offsetGet($key)
240: {
241: $key = is_scalar($key) ? (string) $key : serialize($key);
242: if ($this->key === $key) {
243: return $this->data;
244: }
245: $this->key = $key;
246: $this->data = $this->storage->read($this->namespace . md5($key));
247: return $this->data;
248: }
249:
250:
251:
252: 253: 254: 255: 256: 257:
258: public function offsetExists($key)
259: {
260: return $this->offsetGet($key) !== NULL;
261: }
262:
263:
264:
265: 266: 267: 268: 269: 270:
271: public function offsetUnset($key)
272: {
273: $this->save($key, NULL);
274: }
275:
276:
277:
278:
279:
280:
281:
282: 283: 284: 285: 286:
287: public static function checkCallbacks($callbacks)
288: {
289: foreach ($callbacks as $callback) {
290: $func = array_shift($callback);
291: if (!call_user_func_array($func, $callback)) {
292: return FALSE;
293: }
294: }
295: return TRUE;
296: }
297:
298:
299:
300: 301: 302: 303: 304: 305:
306: private static function checkConst($const, $value)
307: {
308: return defined($const) && constant($const) === $value;
309: }
310:
311:
312:
313: 314: 315: 316: 317: 318:
319: private static function checkFile($file, $time)
320: {
321: return @filemtime($file) == $time; 322: }
323:
324:
325:
326: 327: 328: 329: 330: 331:
332: private static function checkSerializationVersion($class, $value)
333: {
334: return NClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
335: }
336:
337: }
338: