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(IStorage $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 static($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: public function load($key)
114: {
115: $key = is_scalar($key) ? (string) $key : serialize($key);
116: if ($this->key === $key) {
117: return $this->data;
118: }
119: $this->key = $key;
120: $this->data = $this->storage->read($this->namespace . md5($key));
121: return $this->data;
122: }
123:
124:
125:
126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142:
143: public function save($key, $data, array $dp = NULL)
144: {
145: $this->key = is_scalar($key) ? (string) $key : serialize($key);
146: $key = $this->namespace . md5($this->key);
147:
148:
149: if (isset($dp[Cache::EXPIRATION])) {
150: $dp[Cache::EXPIRATION] = Nette\DateTime::from($dp[Cache::EXPIRATION])->format('U') - time();
151: }
152:
153:
154: if (isset($dp[self::FILES])) {
155:
156: foreach ((array) $dp[self::FILES] as $item) {
157: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item));
158: }
159: unset($dp[self::FILES]);
160: }
161:
162:
163: if (isset($dp[self::ITEMS])) {
164: $dp[self::ITEMS] = (array) $dp[self::ITEMS];
165: foreach ($dp[self::ITEMS] as $k => $item) {
166: $dp[self::ITEMS][$k] = $this->namespace . md5(is_scalar($item) ? $item : serialize($item));
167: }
168: }
169:
170:
171: if (isset($dp[self::CONSTS])) {
172: foreach ((array) $dp[self::CONSTS] as $item) {
173: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
174: }
175: unset($dp[self::CONSTS]);
176: }
177:
178: if ($data instanceof Nette\Callback || $data instanceof \Closure) {
179: Nette\Utils\CriticalSection::enter();
180: $data = $data->__invoke();
181: Nette\Utils\CriticalSection::leave();
182: }
183:
184: if (is_object($data)) {
185: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
186: Nette\Reflection\ClassType::from($data)->getAnnotation('serializationVersion'));
187: }
188:
189: $this->data = $data;
190: if ($data === NULL) {
191: $this->storage->remove($key);
192: } else {
193: $this->storage->write($key, $data, (array) $dp);
194: }
195: return $data;
196: }
197:
198:
199:
200: 201: 202: 203: 204: 205: 206: 207: 208: 209:
210: public function clean(array $conds = NULL)
211: {
212: $this->release();
213: $this->storage->clean((array) $conds);
214: }
215:
216:
217:
218: 219: 220: 221: 222:
223: public function call($function)
224: {
225: $key = func_get_args();
226: if ($this->load($key) === NULL) {
227: array_shift($key);
228: return $this->save($this->key, call_user_func_array($function, $key));
229: } else {
230: return $this->data;
231: }
232: }
233:
234:
235:
236: 237: 238: 239: 240:
241: public function start($key)
242: {
243: if ($this->offsetGet($key) === NULL) {
244: return new OutputHelper($this, $key);
245: } else {
246: echo $this->data;
247: }
248: }
249:
250:
251:
252:
253:
254:
255:
256: 257: 258: 259: 260: 261: 262:
263: public function offsetSet($key, $data)
264: {
265: $this->save($key, $data);
266: }
267:
268:
269:
270: 271: 272: 273: 274: 275:
276: public function offsetGet($key)
277: {
278: return $this->load($key);
279: }
280:
281:
282:
283: 284: 285: 286: 287: 288:
289: public function offsetExists($key)
290: {
291: return $this->load($key) !== NULL;
292: }
293:
294:
295:
296: 297: 298: 299: 300: 301:
302: public function offsetUnset($key)
303: {
304: $this->save($key, NULL);
305: }
306:
307:
308:
309:
310:
311:
312:
313: 314: 315: 316: 317:
318: public static function checkCallbacks($callbacks)
319: {
320: foreach ($callbacks as $callback) {
321: $func = array_shift($callback);
322: if (!call_user_func_array($func, $callback)) {
323: return FALSE;
324: }
325: }
326: return TRUE;
327: }
328:
329:
330:
331: 332: 333: 334: 335: 336:
337: private static function checkConst($const, $value)
338: {
339: return defined($const) && constant($const) === $value;
340: }
341:
342:
343:
344: 345: 346: 347: 348: 349:
350: private static function checkFile($file, $time)
351: {
352: return @filemtime($file) == $time;
353: }
354:
355:
356:
357: 358: 359: 360: 361: 362:
363: private static function checkSerializationVersion($class, $value)
364: {
365: return Nette\Reflection\ClassType::from($class)->getAnnotation('serializationVersion') === $value;
366: }
367:
368: }
369: