1: <?php defined('_JOOS_CORE') or exit();
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
19: class joosCache
20: {
21: protected $cache;
22: protected $data_store;
23: protected $state;
24: protected $type;
25:
26: private static $instance = array();
27:
28: 29: 30: 31: 32: 33: 34:
35: public static function instance($type = false)
36: {
37: if (!isset(self::$instance[$type]) || self::$instance[$type] === null) {
38: self::$instance[$type] = new self($type);
39: }
40:
41: return self::$instance[$type];
42: }
43:
44: private function __construct($type = false, $data_store = false)
45: {
46: $type = $type ? $type : joosConfig::get2('cache', 'handler');
47:
48: switch ($type) {
49: case 'file':
50:
51: $data_store = $data_store ? $data_store : joosConfig::get2('cache', 'cachepath');
52:
53: $exists = file_exists($data_store);
54: if (!$exists && !is_writable(dirname($data_store))) {
55: throw new joosCacheException(sprintf('Каталог кеширования %s недоступен для записи', $data_store));
56: }
57: if ($exists && !is_writable($data_store)) {
58: throw new joosCacheException(sprintf('Файл кеша %s недоступен для записи', $data_store));
59: }
60: $this->data_store = $data_store;
61: if ($exists) {
62: $this->cache = unserialize(file_get_contents($data_store));
63: } else {
64: $this->cache = array();
65: }
66: $this->state = 'clean';
67: break;
68:
69: case 'apc':
70: case 'xcache':
71: case 'memcache':
72: if (!extension_loaded($type)) {
73: throw new joosCacheException(sprintf('Расширение кеширования %s недоступно либо не установлено', $type));
74: }
75: if ($type == 'memcache') {
76:
77: if ($data_store == false) {
78: $data_store = new Memcache();
79: $data_store->connect(joosConfig::get2('cache', 'memcache_host'), joosConfig::get2('cache', 'memcache_port'));
80: }
81:
82: if (!$data_store instanceof Memcache) {
83: throw new joosCacheException('Объект кеширования не является допустимым объектом Memcache');
84: }
85: $this->data_store = $data_store;
86: }
87: break;
88:
89: default:
90: throw new joosCacheException(sprintf('Кеширующая система не поддерживает %s, разрешено лишь %s', $type, join(', ', array('apc', 'file', 'memcache', 'xcache'))));
91: }
92:
93: $this->type = $type;
94: }
95:
96: public function __destruct()
97: {
98: $this->save();
99: }
100:
101: public function add($key, $value, $ttl = 0)
102: {
103: switch ($this->type) {
104: case 'apc':
105: return apc_add($key, serialize($value), $ttl);
106:
107: case 'file':
108: if (isset($this->cache[$key]) && $this->cache[$key]['expire'] && $this->cache[$key]['expire'] >= time()) {
109: return FALSE;
110: }
111: $this->cache[$key] = array('value' => $value, 'expire' => (!$ttl) ? 0 : time() + $ttl);
112: $this->state = 'dirty';
113:
114: return TRUE;
115:
116: case 'memcache':
117: if ($ttl > 2592000) {
118: $ttl = time() + 2592000;
119: }
120:
121: return $this->data_store->add($key, $value, 0, $ttl);
122: }
123: }
124:
125: public function clear()
126: {
127: switch ($this->type) {
128: case 'apc':
129: apc_clear_cache('user');
130:
131: return;
132:
133: case 'file':
134: $this->cache = array();
135: $this->state = 'dirty';
136:
137: return;
138:
139: case 'memcache':
140: $this->data_store->flush();
141:
142: return;
143: }
144: }
145:
146: public function delete($key)
147: {
148: switch ($this->type) {
149: case 'apc':
150: apc_delete($key);
151:
152: return;
153:
154: case 'file':
155: if (isset($this->cache[$key])) {
156: unset($this->cache[$key]);
157: $this->state = 'dirty';
158: }
159:
160: return;
161:
162: case 'memcache':
163: $this->data_store->delete($key);
164:
165: return;
166: }
167: }
168:
169: public function get($key, $default = NULL)
170: {
171: switch ($this->type) {
172: case 'apc':
173: $value = apc_fetch($key);
174: if ($value === FALSE) {
175: return $default;
176: }
177:
178: return unserialize($value);
179:
180: case 'file':
181: if (isset($this->cache[$key])) {
182: $expire = $this->cache[$key]['expire'];
183: if (!$expire || $expire >= time()) {
184: return $this->cache[$key]['value'];
185: } elseif ($expire) {
186: unset($this->cache[$key]);
187: $this->state = 'dirty';
188: }
189: }
190:
191: return $default;
192:
193: case 'memcache':
194: $value = $this->data_store->get($key);
195: if ($value === FALSE) {
196: return $default;
197: }
198:
199: return $value;
200: }
201: }
202:
203: public function save()
204: {
205: if ($this->type != 'file') {
206: return;
207: }
208:
209:
210: if (rand(0, 99) == 50) {
211: $clear_before = time();
212:
213: foreach ($this->cache as $key => $value) {
214: if ($value['expire'] && $value['expire'] < $clear_before) {
215: unset($this->cache[$key]);
216: $this->state = 'dirty';
217: }
218: }
219: }
220:
221: if ($this->state == 'clean') {
222: return;
223: }
224:
225: file_put_contents($this->data_store, serialize($this->cache));
226: $this->state = 'clean';
227: }
228:
229: public function set($key, $value, $ttl = 0)
230: {
231:
232: if (!joosConfig::get2('cache', 'enable')) {
233: return;
234: }
235:
236: switch ($this->type) {
237: case 'apc':
238: apc_store($key, serialize($value), $ttl);
239:
240: return;
241:
242: case 'file':
243: $this->cache[$key] = array('value' => $value, 'expire' => (!$ttl) ? 0 : time() + $ttl);
244: $this->state = 'dirty';
245:
246: return;
247:
248: case 'memcache':
249: if ($ttl > 2592000) {
250: $ttl = time() + 2592000;
251: }
252: $this->data_store->set($key, $value, 0, $ttl);
253:
254: return;
255: }
256: }
257:
258: }
259:
260: 261: 262:
263: class joosCacheException extends joosException
264: {
265: }
266: