1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* |
5
|
|
|
* This file is part of Phpfastcache. |
6
|
|
|
* |
7
|
|
|
* @license MIT License (MIT) |
8
|
|
|
* |
9
|
|
|
* For full copyright and license information, please see the docs/CREDITS.txt and LICENCE files. |
10
|
|
|
* |
11
|
|
|
* @author Georges.L (Geolim4) <[email protected]> |
12
|
|
|
* @author Contributors https://github.com/PHPSocialNetwork/phpfastcache/graphs/contributors |
13
|
|
|
*/ |
14
|
|
|
|
15
|
|
|
declare(strict_types=1); |
16
|
|
|
|
17
|
|
|
namespace Phpfastcache\Drivers\Memcached; |
18
|
|
|
|
19
|
|
|
use DateTime; |
20
|
|
|
use Memcached as MemcachedSoftware; |
21
|
|
|
use Phpfastcache\Cluster\AggregatablePoolInterface; |
22
|
|
|
use Phpfastcache\Config\ConfigurationOption; |
23
|
|
|
use Phpfastcache\Core\Pool\ExtendedCacheItemPoolInterface; |
24
|
|
|
use Phpfastcache\Core\Pool\TaggableCacheItemPoolTrait; |
25
|
|
|
use Phpfastcache\Core\Item\ExtendedCacheItemInterface; |
26
|
|
|
use Phpfastcache\Entities\DriverStatistic; |
27
|
|
|
use Phpfastcache\Event\EventManagerInterface; |
28
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheCoreException; |
29
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheDriverCheckException; |
30
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheDriverConnectException; |
31
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheDriverException; |
32
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheInvalidArgumentException; |
33
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheInvalidTypeException; |
34
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheIOException; |
35
|
|
|
use Phpfastcache\Exceptions\PhpfastcacheLogicException; |
36
|
|
|
use Phpfastcache\Util\MemcacheDriverCollisionDetectorTrait; |
37
|
|
|
use Psr\Cache\CacheItemInterface; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @property MemcachedSoftware $instance |
41
|
|
|
* @method Config getConfig() |
42
|
|
|
*/ |
43
|
|
|
class Driver implements AggregatablePoolInterface |
44
|
|
|
{ |
45
|
|
|
use TaggableCacheItemPoolTrait { |
46
|
|
|
__construct as protected __parentConstruct; |
47
|
|
|
} |
48
|
|
|
use MemcacheDriverCollisionDetectorTrait; |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* Driver constructor. |
52
|
|
|
* @param ConfigurationOption $config |
53
|
|
|
* @param string $instanceId |
54
|
|
|
* @param EventManagerInterface $em |
55
|
|
|
* @throws PhpfastcacheDriverConnectException |
56
|
|
|
* @throws PhpfastcacheInvalidArgumentException |
57
|
|
|
* @throws PhpfastcacheCoreException |
58
|
|
|
* @throws PhpfastcacheDriverCheckException |
59
|
|
|
* @throws PhpfastcacheIOException |
60
|
|
|
*/ |
61
|
|
|
public function __construct(ConfigurationOption $config, string $instanceId, EventManagerInterface $em) |
62
|
|
|
{ |
63
|
|
|
self::checkCollision('Memcached'); |
64
|
|
|
$this->__parentConstruct($config, $instanceId, $em); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* @return bool |
69
|
|
|
*/ |
70
|
|
|
public function driverCheck(): bool |
71
|
|
|
{ |
72
|
|
|
return class_exists('Memcached'); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* @return DriverStatistic |
77
|
|
|
*/ |
78
|
|
|
public function getStats(): DriverStatistic |
79
|
|
|
{ |
80
|
|
|
$stats = current($this->instance->getStats()); |
81
|
|
|
$stats['uptime'] = $stats['uptime'] ?? 0; |
82
|
|
|
$stats['bytes'] = $stats['bytes'] ?? 0; |
83
|
|
|
$stats['version'] = $stats['version'] ?? $this->instance->getVersion(); |
84
|
|
|
|
85
|
|
|
$date = (new DateTime())->setTimestamp(time() - $stats['uptime']); |
86
|
|
|
|
87
|
|
|
return (new DriverStatistic()) |
88
|
|
|
->setData(implode(', ', array_keys($this->itemInstances))) |
89
|
|
|
->setInfo(sprintf("The memcache daemon v%s is up since %s.\n For more information see RawData.", $stats['version'], $date->format(DATE_RFC2822))) |
90
|
|
|
->setRawData($stats) |
91
|
|
|
->setSize((int)$stats['bytes']); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @return bool |
96
|
|
|
* @throws PhpfastcacheDriverException |
97
|
|
|
*/ |
98
|
|
|
protected function driverConnect(): bool |
99
|
|
|
{ |
100
|
|
|
$this->instance = new MemcachedSoftware(); |
101
|
|
|
$optPrefix = $this->getConfig()->getOptPrefix(); |
102
|
|
|
$this->instance->setOption(MemcachedSoftware::OPT_BINARY_PROTOCOL, true); |
103
|
|
|
|
104
|
|
|
if ($optPrefix) { |
105
|
|
|
$this->instance->setOption(MemcachedSoftware::OPT_PREFIX_KEY, $optPrefix); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
foreach ($this->getConfig()->getServers() as $server) { |
109
|
|
|
$connected = false; |
110
|
|
|
/** |
111
|
|
|
* If path is provided we consider it as an UNIX Socket |
112
|
|
|
*/ |
113
|
|
|
if (!empty($server['path'])) { |
114
|
|
|
$connected = $this->instance->addServer($server['path'], 0); |
115
|
|
|
} elseif (!empty($server['host'])) { |
116
|
|
|
$connected = $this->instance->addServer($server['host'], $server['port']); |
117
|
|
|
} |
118
|
|
|
if (!empty($server['saslUser']) && !empty($server['saslPassword'])) { |
119
|
|
|
$this->instance->setSaslAuthData($server['saslUser'], $server['saslPassword']); |
120
|
|
|
} |
121
|
|
|
if (!$connected) { |
122
|
|
|
throw new PhpfastcacheDriverConnectException( |
123
|
|
|
sprintf( |
124
|
|
|
'Failed to connect to memcache host/path "%s".', |
125
|
|
|
$server['host'] ?: $server['path'], |
126
|
|
|
) |
127
|
|
|
); |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Since Memcached does not throw |
133
|
|
|
* any error if not connected ... |
134
|
|
|
*/ |
135
|
|
|
$version = $this->instance->getVersion(); |
136
|
|
|
if (!$version || $this->instance->getResultCode() !== MemcachedSoftware::RES_SUCCESS) { |
|
|
|
|
137
|
|
|
throw new PhpfastcacheDriverException('Memcached seems to not be connected'); |
138
|
|
|
} |
139
|
|
|
return true; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @param ExtendedCacheItemInterface $item |
144
|
|
|
* @return ?array<string, mixed> |
145
|
|
|
*/ |
146
|
|
|
protected function driverRead(ExtendedCacheItemInterface $item): ?array |
147
|
|
|
{ |
148
|
|
|
$val = $this->instance->get($item->getKey()); |
149
|
|
|
|
150
|
|
|
if (empty($val) || !\is_array($val)) { |
151
|
|
|
return null; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
return $val; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @param ExtendedCacheItemInterface ...$items |
159
|
|
|
* @return array<array<string, mixed>> |
160
|
|
|
*/ |
161
|
|
|
protected function driverReadMultiple(ExtendedCacheItemInterface ...$items): array |
162
|
|
|
{ |
163
|
|
|
$keys = $this->getKeys($items); |
164
|
|
|
|
165
|
|
|
$val = $this->instance->getMulti($keys); |
166
|
|
|
|
167
|
|
|
if (empty($val) || !\is_array($val)) { |
168
|
|
|
return []; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return $val; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @return array<string, mixed> |
177
|
|
|
* @throws PhpfastcacheInvalidArgumentException |
178
|
|
|
*/ |
179
|
|
|
protected function driverReadAllKeys(string $pattern = ''): iterable |
180
|
|
|
{ |
181
|
|
|
if ($pattern !== '') { |
182
|
|
|
$this->throwUnsupportedDriverReadAllPattern('https://www.php.net/manual/en/memcached.getallkeys.php'); |
183
|
|
|
} |
184
|
|
|
$keys = $this->instance->getAllKeys(); |
185
|
|
|
|
186
|
|
|
if (is_iterable($keys)) { |
187
|
|
|
return $keys; |
188
|
|
|
} else { |
189
|
|
|
return []; |
190
|
|
|
} |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @param ExtendedCacheItemInterface $item |
195
|
|
|
* @return bool |
196
|
|
|
* @throws PhpfastcacheInvalidArgumentException |
197
|
|
|
* @throws PhpfastcacheLogicException |
198
|
|
|
*/ |
199
|
|
|
protected function driverWrite(ExtendedCacheItemInterface $item): bool |
200
|
|
|
{ |
201
|
|
|
|
202
|
|
|
$ttl = $item->getExpirationDate()->getTimestamp() - time(); |
203
|
|
|
|
204
|
|
|
// Memcache will only allow a expiration timer less than 2592000 seconds, |
205
|
|
|
// otherwise, it will assume you're giving it a UNIX timestamp. |
206
|
|
|
if ($ttl > 2592000) { |
207
|
|
|
$ttl = time() + $ttl; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
return $this->instance->set($item->getKey(), $this->driverPreWrap($item), $ttl); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* @param string $key |
215
|
|
|
* @param string $encodedKey |
216
|
|
|
* @return bool |
217
|
|
|
*/ |
218
|
|
|
protected function driverDelete(string $key, string $encodedKey): bool |
219
|
|
|
{ |
220
|
|
|
return $this->instance->delete($key); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* @return bool |
225
|
|
|
*/ |
226
|
|
|
protected function driverClear(): bool |
227
|
|
|
{ |
228
|
|
|
return $this->instance->flush(); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.