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