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:
163: public function write($key, $data, array $dp)
164: {
165: if (isset($dp[Cache::ITEMS])) {
166: throw new Nette\NotSupportedException('Dependent items are not supported by MemcachedStorage.');
167: }
168:
169: $key = urlencode($this->prefix . $key);
170: $meta = [
171: self::META_DATA => $data,
172: ];
173:
174: $expire = 0;
175: if (isset($dp[Cache::EXPIRATION])) {
176: $expire = (int) $dp[Cache::EXPIRATION];
177: if (!empty($dp[Cache::SLIDING])) {
178: $meta[self::META_DELTA] = $expire;
179: }
180: }
181:
182: if (isset($dp[Cache::CALLBACKS])) {
183: $meta[self::META_CALLBACKS] = $dp[Cache::CALLBACKS];
184: }
185:
186: if (isset($dp[Cache::TAGS]) || isset($dp[Cache::PRIORITY])) {
187: if (!$this->journal) {
188: throw new Nette\InvalidStateException('CacheJournal has not been provided.');
189: }
190: $this->journal->write($key, $dp);
191: }
192:
193: $this->memcached->set($key, $meta, $expire);
194: }
195:
196:
197: 198: 199: 200: 201:
202: public function remove($key)
203: {
204: $this->memcached->delete(urlencode($this->prefix . $key), 0);
205: }
206:
207:
208: 209: 210: 211: 212:
213: public function clean(array $conditions)
214: {
215: if (!empty($conditions[Cache::ALL])) {
216: $this->memcached->flush();
217:
218: } elseif ($this->journal) {
219: foreach ($this->journal->clean($conditions) as $entry) {
220: $this->memcached->delete($entry, 0);
221: }
222: }
223: }
224:
225: }
226: