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: const EXPIRE = 'expire';
28: const SLIDING = 'sliding';
29: const TAGS = 'tags';
30: const FILES = 'files';
31: const ITEMS = 'items';
32: const CONSTS = 'consts';
33: const CALLBACKS = 'callbacks';
34: const ALL = 'all';
35:
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 = (string) $namespace;
58:
59: if (strpos($this->namespace, self::NAMESPACE_SEPARATOR) !== FALSE) {
60: throw new \InvalidArgumentException("Namespace name contains forbidden NUL character.");
61: }
62: }
63:
64:
65:
66: 67: 68: 69:
70: public function getStorage()
71: {
72: return $this->storage;
73: }
74:
75:
76:
77: 78: 79: 80:
81: public function getNamespace()
82: {
83: return $this->namespace;
84: }
85:
86:
87:
88: 89: 90: 91:
92: public function release()
93: {
94: $this->key = $this->data = NULL;
95: }
96:
97:
98:
99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115:
116: public function save($key, $data, array $dp = NULL)
117: {
118: if (!is_string($key) && !is_int($key)) {
119: throw new \InvalidArgumentException("Cache key name must be string or integer, " . gettype($key) . " given.");
120: }
121: $this->key = (string) $key;
122: $key = $this->namespace . self::NAMESPACE_SEPARATOR . $key;
123:
124: 125: if (isset($dp[Cache::EXPIRE])) {
126: $dp[Cache::EXPIRE] = Nette\Tools::createDateTime($dp[Cache::EXPIRE])->format('U') - time();
127: }
128:
129: 130: if (isset($dp[self::FILES])) {
131: 132: foreach ((array) $dp[self::FILES] as $item) {
133: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); 134: }
135: unset($dp[self::FILES]);
136: }
137:
138: 139: if (isset($dp[self::ITEMS])) {
140: $dp[self::ITEMS] = (array) $dp[self::ITEMS];
141: foreach ($dp[self::ITEMS] as $k => $item) {
142: $dp[self::ITEMS][$k] = $this->namespace . self::NAMESPACE_SEPARATOR . $item;
143: }
144: }
145:
146: 147: if (isset($dp[self::CONSTS])) {
148: foreach ((array) $dp[self::CONSTS] as $item) {
149: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
150: }
151: unset($dp[self::CONSTS]);
152: }
153:
154: if ($data instanceof Nette\Callback || $data instanceof \Closure) {
155: Nette\Tools::enterCriticalSection();
156: $data = $data->__invoke();
157: Nette\Tools::leaveCriticalSection();
158: }
159:
160: if (is_object($data)) {
161: $dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
162: Nette\Reflection\ClassReflection::from($data)->getAnnotation('serializationVersion'));
163: }
164:
165: $this->data = $data;
166: if ($data === NULL) {
167: $this->storage->remove($key);
168: } else {
169: $this->storage->write($key, $data, (array) $dp);
170: }
171: return $data;
172: }
173:
174:
175:
176: 177: 178: 179: 180: 181: 182: 183: 184: 185:
186: public function clean(array $conds = NULL)
187: {
188: $this->release();
189: $this->storage->clean((array) $conds);
190: }
191:
192:
193:
194:
195:
196:
197:
198: 199: 200: 201: 202: 203: 204:
205: public function offsetSet($key, $data)
206: {
207: $this->save($key, $data);
208: }
209:
210:
211:
212: 213: 214: 215: 216: 217:
218: public function offsetGet($key)
219: {
220: if (!is_string($key) && !is_int($key)) {
221: throw new \InvalidArgumentException("Cache key name must be string or integer, " . gettype($key) . " given.");
222: }
223:
224: $key = (string) $key;
225: if ($this->key === $key) {
226: return $this->data;
227: }
228: $this->key = $key;
229: $this->data = $this->storage->read($this->namespace . self::NAMESPACE_SEPARATOR . $key);
230: return $this->data;
231: }
232:
233:
234:
235: 236: 237: 238: 239: 240:
241: public function offsetExists($key)
242: {
243: return $this->offsetGet($key) !== NULL;
244: }
245:
246:
247:
248: 249: 250: 251: 252: 253:
254: public function offsetUnset($key)
255: {
256: $this->save($key, NULL);
257: }
258:
259:
260:
261:
262:
263:
264:
265: 266: 267: 268: 269:
270: public static function checkCallbacks($callbacks)
271: {
272: foreach ($callbacks as $callback) {
273: $func = array_shift($callback);
274: if (!call_user_func_array($func, $callback)) {
275: return FALSE;
276: }
277: }
278: return TRUE;
279: }
280:
281:
282:
283: 284: 285: 286: 287: 288:
289: private static function checkConst($const, $value)
290: {
291: return defined($const) && constant($const) === $value;
292: }
293:
294:
295:
296: 297: 298: 299: 300: 301:
302: private static function checkFile($file, $time)
303: {
304: return @filemtime($file) == $time; 305: }
306:
307:
308:
309: 310: 311: 312: 313: 314:
315: private static function checkSerializationVersion($class, $value)
316: {
317: return Nette\Reflection\ClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
318: }
319:
320: }
321: