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