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