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