1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11:
12:
13:
14:
15: 16: 17: 18: 19:
20: class MemcachedStorage extends Object implements ICacheStorage
21: {
22:
23: const META_CALLBACKS = 'callbacks',
24: META_DATA = 'data',
25: META_DELTA = 'delta';
26:
27:
28: private $memcache;
29:
30:
31: private $prefix;
32:
33:
34: private $journal;
35:
36:
37:
38: 39: 40: 41:
42: public static function isAvailable()
43: {
44: return extension_loaded('memcache');
45: }
46:
47:
48:
49: public function __construct($host = 'localhost', $port = 11211, $prefix = '', ICacheJournal $journal = NULL)
50: {
51: if (!self::isAvailable()) {
52: throw new NotSupportedException("PHP extension 'memcache' is not loaded.");
53: }
54:
55: $this->prefix = $prefix;
56: $this->journal = $journal;
57: $this->memcache = new Memcache;
58: Debug::tryError();
59: $this->memcache->connect($host, $port);
60: if (Debug::catchError($e)) {
61: throw new InvalidStateException('Memcache::connect(): ' . $e->getMessage(), 0, $e);
62: }
63: }
64:
65:
66:
67: 68: 69: 70: 71:
72: public function read($key)
73: {
74: $key = $this->prefix . $key;
75: $meta = $this->memcache->get($key);
76: if (!$meta) return NULL;
77:
78: 79: 80: 81: 82: 83: 84:
85: 86: if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
87: $this->memcache->delete($key, 0);
88: return NULL;
89: }
90:
91: if (!empty($meta[self::META_DELTA])) {
92: $this->memcache->replace($key, $meta, 0, $meta[self::META_DELTA] + time());
93: }
94:
95: return $meta[self::META_DATA];
96: }
97:
98:
99:
100: 101: 102: 103: 104: 105: 106:
107: public function write($key, $data, array $dp)
108: {
109: if (isset($dp[Cache::ITEMS])) {
110: throw new NotSupportedException('Dependent items are not supported by MemcachedStorage.');
111: }
112:
113: $key = $this->prefix . $key;
114: $meta = array(
115: self::META_DATA => $data,
116: );
117:
118: $expire = 0;
119: if (isset($dp[Cache::EXPIRATION])) {
120: $expire = (int) $dp[Cache::EXPIRATION];
121: if (!empty($dp[Cache::SLIDING])) {
122: $meta[self::META_DELTA] = $expire; 123: }
124: }
125:
126: if (isset($dp[Cache::CALLBACKS])) {
127: $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
128: }
129:
130: if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) {
131: if (!$this->journal) {
132: throw new InvalidStateException('CacheJournal has not been provided.');
133: }
134: $this->journal->write($key, $dp);
135: }
136:
137: $this->memcache->set($key, $meta, 0, $expire);
138: }
139:
140:
141:
142: 143: 144: 145: 146:
147: public function remove($key)
148: {
149: $this->memcache->delete($this->prefix . $key, 0);
150: }
151:
152:
153:
154: 155: 156: 157: 158:
159: public function clean(array $conds)
160: {
161: if (!empty($conds[Cache::ALL])) {
162: $this->memcache->flush();
163:
164: } elseif ($this->journal) {
165: foreach ($this->journal->clean($conds) as $entry) {
166: $this->memcache->delete($entry, 0);
167: }
168: }
169: }
170:
171: }
172: