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