1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Caching\Storages;
9:
10: use Nette;
11: use Nette\Caching\Cache;
12:
13:
14: 15: 16:
17: class NewMemcachedStorage implements Nette\Caching\IStorage, Nette\Caching\IBulkReader
18: {
19: use Nette\SmartObject;
20:
21:
22: const META_CALLBACKS = 'callbacks',
23: META_DATA = 'data',
24: META_DELTA = 'delta';
25:
26:
27: private $memcached;
28:
29:
30: private $prefix;
31:
32:
33: private $journal;
34:
35:
36: 37: 38: 39:
40: public static function isAvailable()
41: {
42: return extension_loaded('memcached');
43: }
44:
45:
46: public function __construct($host = 'localhost', $port = 11211, $prefix = '', IJournal $journal = null)
47: {
48: if (!static::isAvailable()) {
49: throw new Nette\NotSupportedException("PHP extension 'memcached' is not loaded.");
50: }
51:
52: $this->prefix = $prefix;
53: $this->journal = $journal;
54: $this->memcached = new \Memcached;
55: if ($host) {
56: $this->addServer($host, $port);
57: }
58: }
59:
60:
61: public function addServer($host = 'localhost', $port = 11211)
62: {
63: if ($this->memcached->addServer($host, $port, 1) === false) {
64: $error = error_get_last();
65: throw new Nette\InvalidStateException("Memcached::addServer(): $error[message].");
66: }
67: }
68:
69:
70: 71: 72:
73: public function getConnection()
74: {
75: return $this->memcached;
76: }
77:
78:
79: 80: 81: 82: 83:
84: public function read($key)
85: {
86: $key = urlencode($this->prefix . $key);
87: $meta = $this->memcached->get($key);
88: if (!$meta) {
89: return null;
90: }
91:
92:
93:
94:
95:
96:
97:
98:
99:
100: if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
101: $this->memcached->delete($key, 0);
102: return null;
103: }
104:
105: if (!empty($meta[self::META_DELTA])) {
106: $this->memcached->replace($key, $meta, $meta[self::META_DELTA] + time());
107: }
108:
109: return $meta[self::META_DATA];
110: }
111:
112:
113: 114: 115: 116: 117:
118: public function bulkRead(array $keys)
119: {
120: $prefixedKeys = array_map(function ($key) {
121: return urlencode($this->prefix . $key);
122: }, $keys);
123: $keys = array_combine($prefixedKeys, $keys);
124: $metas = $this->memcached->getMulti($prefixedKeys);
125: $result = [];
126: $deleteKeys = [];
127: foreach ($metas as $prefixedKey => $meta) {
128: if (!empty($meta[self::META_CALLBACKS]) && !Cache::checkCallbacks($meta[self::META_CALLBACKS])) {
129: $deleteKeys[] = $prefixedKey;
130: } else {
131: $result[$keys[$prefixedKey]] = $meta[self::META_DATA];
132: }
133:
134: if (!empty($meta[self::META_DELTA])) {
135: $this->memcached->replace($prefixedKey, $meta, $meta[self::META_DELTA] + time());
136: }
137: }
138: if (!empty($deleteKeys)) {
139: $this->memcached->deleteMulti($deleteKeys, 0);
140: }
141:
142: return $result;
143: }
144:
145:
146: 147: 148: 149: 150:
151: public function lock($key)
152: {
153: }
154:
155:
156: 157: 158: 159: 160: 161:
162: public function write($key, $data, array $dp)
163: {
164: if (isset($dp[Cache::ITEMS])) {
165: throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
166: }
167:
168: $key = urlencode($this->prefix . $key);
169: $meta = [
170: self::META_DATA => $data,
171: ];
172:
173: $expire = 0;
174: if (isset($dp[Cache::EXPIRATION])) {
175: $expire = (int) $dp[Cache::EXPIRATION];
176: if (!empty($dp[Cache::SLIDING])) {
177: $meta[self::META_DELTA] = $expire;
178: }
179: }
180:
181: if (isset($dp[Cache::CALLBACKS])) {
182: $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
183: }
184:
185: if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) {
186: if (!$this->journal) {
187: throw new Nette\InvalidStateException('CacheJournal has not been provided.');
188: }
189: $this->journal->write($key, $dp);
190: }
191:
192: $this->memcached->set($key, $meta, $expire);
193: }
194:
195:
196: 197: 198: 199: 200:
201: public function remove($key)
202: {
203: $this->memcached->delete(urlencode($this->prefix . $key), 0);
204: }
205:
206:
207: 208: 209: 210: 211:
212: public function clean(array $conditions)
213: {
214: if (!empty($conditions[Cache::ALL])) {
215: $this->memcached->flush();
216:
217: } elseif ($this->journal) {
218: foreach ($this->journal->clean($conditions) as $entry) {
219: $this->memcached->delete($entry, 0);
220: }
221: }
222: }
223: }
224: