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