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