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 MemcachedStorage extends Nette\Object implements ICacheStorage
24: {
25:
26: const META_CALLBACKS = 'callbacks',
27: META_DATA = 'data',
28: META_DELTA = 'delta';
29:
30:
31: private $memcache;
32:
33:
34: private $prefix;
35:
36:
37: private $journal;
38:
39:
40:
41: 42: 43: 44:
45: public static function isAvailable()
46: {
47: return extension_loaded('memcache');
48: }
49:
50:
51:
52: public function __construct($host = 'localhost', $port = 11211, $prefix = '', ICacheJournal $journal = NULL)
53: {
54: if (!self::isAvailable()) {
55: throw new \NotSupportedException("PHP extension 'memcache' is not loaded.");
56: }
57:
58: $this->prefix = $prefix;
59: $this->journal = $journal;
60: $this->memcache = new \Memcache;
61: Nette\Debug::tryError();
62: $this->memcache->connect($host, $port);
63: if (Nette\Debug::catchError($e)) {
64: throw new \InvalidStateException('Memcache::connect(): ' . $e->getMessage(), 0, $e);
65: }
66: }
67:
68:
69:
70: 71: 72: 73: 74:
75: public function read($key)
76: {
77: $key = $this->prefix . $key;
78: $meta = $this->memcache->get($key);
79: if (!$meta) return NULL;
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: