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