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