├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── phpunit.xml.dist
├── src
├── Memcached.php
├── MemcachedEmulator.php
└── MemcachedException.php
└── tests
└── MemcachedEmulatorTest.php
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE
2 | .idea
3 |
4 | # Misc
5 | vendor/
6 | composer.lock
7 | phpunit.xml
8 | .phpunit.result.cache
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 | sudo: false
3 | dist: xenial
4 |
5 | services:
6 | - memcached
7 |
8 | matrix:
9 | fast_finish: true
10 | include:
11 | - php: 7.4
12 | - php: 8.0
13 | - php: 8.1
14 | - php: 8.2
15 | - php: nightly
16 | allow_failures:
17 | - php: nightly
18 |
19 | before_script:
20 | - composer self-update
21 | - composer install --prefer-source --no-interaction --dev
22 |
23 | script:
24 | - vendor/bin/phpunit
25 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | CHANGELOG
2 | =========
3 |
4 | 1.0.7
5 | -----
6 |
7 | * PHP 8.1 and 8.2 support
8 |
9 | 1.0.6
10 | -----
11 |
12 | * PHP 7.4 as minimum.
13 | * PHPUnit 9.5.
14 | * PHP 7.4 and PHP 8 tests.
15 | * xenial dist for Travis.
16 |
17 | 1.0.5
18 | -----
19 |
20 | * PHP 7.2 as minimum.
21 | * PHPUnit 8.
22 | * Changed `getAllKeys()` retrieve logic.
23 | * Performance improvements.
24 |
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Avantarm
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Memcached emulator
2 | [](https://travis-ci.org/avantarm/memcached-emulator)
3 | [](https://packagist.org/packages/avantarm/memcached-emulator)
4 | [](https://packagist.org/packages/avantarm/memcached-emulator)
5 | [](https://packagist.org/packages/avantarm/memcached-emulator)
6 | [](https://packagist.org/packages/avantarm/memcached-emulator)
7 |
8 | Memcached PHP extension emulator for Windows environment.
9 |
10 | Emulates Memcached 3.0.4 extension for PHP.
11 |
12 | ## Installation via Composer
13 |
14 | Add `"avantarm/memcached-emulator": "~1.0"` to the require block in your composer.json and then run `composer install`.
15 |
16 | ```json
17 | {
18 | "require": {
19 | "avantarm/memcached-emulator": "~1.0"
20 | }
21 | }
22 | ```
23 |
24 | Alternatively, you can simply run the following from the command line:
25 |
26 | ```sh
27 | composer require avantarm/memcached-emulator
28 | ```
29 |
30 | Unsupported methods:
31 |
32 | ```php
33 | Memcached::setSaslAuthData()
34 | Memcached::fetch()
35 | Memcached::fetchAll()
36 | Memcached::getDelayed()
37 | Memcached::getDelayedByKey()
38 | ```
39 |
40 | `$time` parameter is not supported for `Memcached::delete()` and `Memcached::deleteByKey()`.
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "avantarm/memcached-emulator",
3 | "type": "library",
4 | "description": "Memcached PHP extension emulator for Windows environments.",
5 | "keywords": [
6 | "memcache",
7 | "memcached",
8 | "caching",
9 | "emulator",
10 | "windows",
11 | "cache"
12 | ],
13 | "time": "2021-01-27",
14 | "license": "MIT",
15 | "homepage": "https://github.com/avantarm/memcached-emulator",
16 | "support": {
17 | "issues": "https://github.com/avantarm/memcached-emulator/issues",
18 | "source": "https://github.com/avantarm/memcached-emulator"
19 | },
20 | "authors": [
21 | {
22 | "name": "Avantarm",
23 | "email": "avantarm@gmail.com"
24 | }
25 | ],
26 | "require": {
27 | "php": ">=7.4",
28 | "ext-json": "*"
29 | },
30 | "require-dev": {
31 | "phpunit/phpunit": "^9.5"
32 | },
33 | "suggest": {
34 | "ext-igbinary": "*",
35 | "ext-zlib": "*"
36 | },
37 | "autoload": {
38 | "psr-0": {
39 | "Memcached": "src/",
40 | "MemcachedException": "src/"
41 | },
42 | "psr-4": {
43 | "Avantarm\\MemcachedEmulator\\": "src/"
44 | }
45 | },
46 | "minimum-stability": "stable",
47 | "extra": {
48 | "branch-alias": {
49 | "dev-master": "1.0.x-dev"
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 | ./
13 |
14 |
15 | ./tests
16 | ./vendor
17 |
18 |
19 |
20 |
21 | tests
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/Memcached.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | use Avantarm\MemcachedEmulator\MemcachedEmulator;
11 |
12 | /**
13 | * Memcached emulating class.
14 | */
15 | class Memcached extends MemcachedEmulator
16 | {
17 | }
18 |
--------------------------------------------------------------------------------
/src/MemcachedEmulator.php:
--------------------------------------------------------------------------------
1 |
5 | *
6 | * For the full copyright and license information, please view the LICENSE
7 | * file that was distributed with this source code.
8 | */
9 |
10 | namespace Avantarm\MemcachedEmulator;
11 |
12 | /**
13 | * Memcached emulator class, Memcached 3.0.4 compatible.
14 | * @noinspection PhpUnused
15 | */
16 | class MemcachedEmulator
17 | {
18 | /**
19 | * Predefined Constants
20 | *
21 | * @see http://php.net/manual/en/memcached.constants.php
22 | */
23 |
24 | /**
25 | * Libmemcached behavior options.
26 | */
27 | public const OPT_HASH = 2;
28 | public const HASH_DEFAULT = 0;
29 | public const HASH_MD5 = 1;
30 | public const HASH_CRC = 2;
31 | public const HASH_FNV1_64 = 3;
32 | public const HASH_FNV1A_64 = 4;
33 | public const HASH_FNV1_32 = 5;
34 | public const HASH_FNV1A_32 = 6;
35 | public const HASH_HSIEH = 7;
36 | public const HASH_MURMUR = 8;
37 |
38 | public const OPT_DISTRIBUTION = 9;
39 | public const DISTRIBUTION_MODULA = 0;
40 | public const DISTRIBUTION_CONSISTENT = 1;
41 | public const DISTRIBUTION_VIRTUAL_BUCKET = 6;
42 |
43 | public const OPT_LIBKETAMA_COMPATIBLE = 16;
44 | public const OPT_LIBKETAMA_HASH = 17;
45 | public const OPT_TCP_KEEPALIVE = 32;
46 | public const OPT_BUFFER_WRITES = 10;
47 | public const OPT_BINARY_PROTOCOL = 18;
48 | public const OPT_NO_BLOCK = 0;
49 | public const OPT_TCP_NODELAY = 1;
50 | public const OPT_SOCKET_SEND_SIZE = 4;
51 | public const OPT_SOCKET_RECV_SIZE = 5;
52 | public const OPT_CONNECT_TIMEOUT = 14;
53 | public const OPT_RETRY_TIMEOUT = 15;
54 | public const OPT_DEAD_TIMEOUT = 36;
55 | public const OPT_SEND_TIMEOUT = 19;
56 | public const OPT_RECV_TIMEOUT = 20;
57 | public const OPT_POLL_TIMEOUT = 8;
58 | public const OPT_SERVER_FAILURE_LIMIT = 21;
59 | public const OPT_SERVER_TIMEOUT_LIMIT = 37;
60 | public const OPT_CACHE_LOOKUPS = 6;
61 | public const OPT_AUTO_EJECT_HOSTS = 28;
62 | public const OPT_HASH_WITH_PREFIX_KEY = 25;
63 | public const OPT_NOREPLY = 26;
64 | public const OPT_SORT_HOSTS = 12;
65 | public const OPT_VERIFY_KEY = 13;
66 | public const OPT_USE_UDP = 27;
67 | public const OPT_NUMBER_OF_REPLICAS = 29;
68 | public const OPT_RANDOMIZE_REPLICA_READ = 30;
69 | public const OPT_REMOVE_FAILED_SERVERS = 35;
70 |
71 | public const LIBMEMCACHED_VERSION_HEX = 16777240;
72 | public const OPT_SERIALIZER = -1003;
73 | public const OPT_STORE_RETRY_COUNT = -1005;
74 |
75 | /**
76 | * Supported serializers
77 | */
78 | public const HAVE_IGBINARY = 0;
79 | public const HAVE_JSON = 0;
80 | public const HAVE_MSGPACK = 0;
81 |
82 | /**
83 | * Feature support
84 | */
85 | public const HAVE_SESSION = 1;
86 | public const HAVE_SASL = 1;
87 |
88 | /**
89 | * Class options.
90 | */
91 | public const OPT_COMPRESSION = -1001;
92 | public const OPT_COMPRESSION_TYPE = -1004;
93 | public const OPT_PREFIX_KEY = -1002;
94 |
95 | /*
96 | * Own emulated options.
97 | */
98 | public const OPT_COMPRESSION_THRESHOLD = 'compression_threshold';
99 | public const OPT_COMPRESSION_FACTOR = 'compression_factor';
100 |
101 | /**
102 | * Serializer constants
103 | */
104 | public const SERIALIZER_PHP = 1;
105 | public const SERIALIZER_IGBINARY = 2;
106 | public const SERIALIZER_JSON = 3;
107 | public const SERIALIZER_JSON_ARRAY = 4;
108 | public const SERIALIZER_MSGPACK = 5;
109 |
110 | /**
111 | * Compression types
112 | */
113 | public const COMPRESSION_ZLIB = 1;
114 | public const COMPRESSION_FASTLZ = 2;
115 |
116 | /**
117 | * Flags for get and getMulti operations.
118 | */
119 | public const GET_PRESERVE_ORDER = 1;
120 | public const GET_EXTENDED = 2;
121 |
122 | /**
123 | * Return values
124 | */
125 | public const GET_ERROR_RETURN_VALUE = false;
126 | public const RES_PAYLOAD_FAILURE = -1001;
127 | public const RES_SUCCESS = 0;
128 | public const RES_FAILURE = 1;
129 | public const RES_HOST_LOOKUP_FAILURE = 2;
130 | public const RES_UNKNOWN_READ_FAILURE = 7;
131 | public const RES_PROTOCOL_ERROR = 8;
132 | public const RES_CLIENT_ERROR = 9;
133 | public const RES_SERVER_ERROR = 10;
134 | public const RES_WRITE_FAILURE = 5;
135 | public const RES_DATA_EXISTS = 12;
136 | public const RES_NOTSTORED = 14;
137 | public const RES_NOTFOUND = 16;
138 | public const RES_PARTIAL_READ = 18;
139 | public const RES_SOME_ERRORS = 19;
140 | public const RES_NO_SERVERS = 20;
141 | public const RES_END = 21;
142 | public const RES_ERRNO = 26;
143 | public const RES_BUFFERED = 32;
144 | public const RES_TIMEOUT = 31;
145 | public const RES_BAD_KEY_PROVIDED = 33;
146 | public const RES_STORED = 15;
147 | public const RES_DELETED = 22;
148 | public const RES_STAT = 24;
149 | public const RES_ITEM = 25;
150 | public const RES_NOT_SUPPORTED = 28;
151 | public const RES_FETCH_NOTFINISHED = 30;
152 | public const RES_SERVER_MARKED_DEAD = 35;
153 | public const RES_UNKNOWN_STAT_KEY = 36;
154 | public const RES_INVALID_HOST_PROTOCOL = 34;
155 | public const RES_MEMORY_ALLOCATION_FAILURE = 17;
156 | public const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11;
157 | public const RES_E2BIG = 37;
158 | public const RES_INVALID_ARGUMENTS = 38; // Emulated, doesn't exist in real Memcached.
159 | public const RES_KEY_TOO_BIG = 39;
160 | public const RES_SERVER_TEMPORARILY_DISABLED = 47;
161 | public const RES_SERVER_MEMORY_ALLOCATION_FAILURE = 48;
162 | public const RES_AUTH_PROBLEM = 40;
163 | public const RES_AUTH_FAILURE = 41;
164 | public const RES_AUTH_CONTINUE = 42;
165 |
166 | /**
167 | * Flags for PHP variables types.
168 | */
169 | public const MEMC_VAL_IS_STRING = 0;
170 | public const MEMC_VAL_IS_LONG = 1;
171 | public const MEMC_VAL_IS_DOUBLE = 2;
172 | public const MEMC_VAL_IS_BOOL = 3;
173 | public const MEMC_VAL_IS_SERIALIZED = 4;
174 | public const MEMC_VAL_IS_IGBINARY = 5;
175 | public const MEMC_VAL_IS_JSON = 6;
176 | public const MEMC_VAL_IS_MSGPACK = 7;
177 |
178 | public const MEMC_VAL_COMPRESSED = 0x1; // (1 << 0);
179 | public const MEMC_VAL_COMPRESSION_ZLIB = 0x2; // (1 << 1);
180 | public const MEMC_VAL_COMPRESSION_FASTLZ = 0x4; // (1 << 2);
181 |
182 | public const MEMC_MASK_TYPE = 0xf; // MEMC_CREATE_MASK(0, 4)
183 | public const MEMC_MASK_INTERNAL = 0xfff0; // MEMC_CREATE_MASK(4, 12)
184 | public const MEMC_MASK_USER = 0xffff0000; // MEMC_CREATE_MASK(16, 16)
185 |
186 | /**
187 | * Response options.
188 | */
189 | public const RESPONSE_VALUE = 'VALUE';
190 | public const RESPONSE_STAT = 'STAT';
191 | public const RESPONSE_ITEM = 'ITEM';
192 | public const RESPONSE_END = 'END';
193 | public const RESPONSE_DELETED = 'DELETED';
194 | public const RESPONSE_NOT_FOUND = 'NOT_FOUND';
195 | public const RESPONSE_OK = 'OK';
196 | public const RESPONSE_EXISTS = 'EXISTS';
197 | public const RESPONSE_ERROR = 'ERROR';
198 | public const RESPONSE_RESET = 'RESET';
199 | public const RESPONSE_STORED = 'STORED';
200 | public const RESPONSE_NOT_STORED = 'NOT_STORED';
201 | public const RESPONSE_TOUCHED = 'TOUCHED';
202 | public const RESPONSE_VERSION = 'VERSION';
203 |
204 | public const RESPONSE_CLIENT_ERROR = 'CLIENT_ERROR';
205 | public const RESPONSE_SERVER_ERROR = 'SERVER_ERROR';
206 |
207 | public const _STORE_SIGNALS = [
208 | self::RESPONSE_STORED => self::RES_SUCCESS,
209 | self::RESPONSE_NOT_STORED => self::RES_NOTSTORED,
210 | self::RESPONSE_EXISTS => self::RES_DATA_EXISTS,
211 | self::RESPONSE_NOT_FOUND => self::RES_NOTFOUND,
212 | ];
213 |
214 | public const _RETRIEVE_SIGNALS = [
215 | self::RESPONSE_END => self::RES_SUCCESS,
216 | ];
217 |
218 | public const _DELETE_SIGNALS = [
219 | self::RESPONSE_DELETED => self::RES_SUCCESS,
220 | self::RESPONSE_NOT_FOUND => self::RES_NOTFOUND,
221 | ];
222 |
223 | public const _INCR_SIGNALS = [
224 | self::RESPONSE_NOT_FOUND => self::RES_NOTFOUND,
225 | self::RESPONSE_STORED => self::RES_SUCCESS,
226 | ];
227 |
228 | public const _DECR_SIGNALS = [
229 | self::RESPONSE_NOT_FOUND => self::RES_NOTFOUND,
230 | self::RESPONSE_STORED => self::RES_SUCCESS,
231 | ];
232 |
233 | public const _TOUCH_SIGNALS = [
234 | self::RESPONSE_TOUCHED => self::RES_SUCCESS,
235 | self::RESPONSE_NOT_FOUND => self::RES_NOTFOUND,
236 | ];
237 |
238 | public const _STAT_SIGNALS = [
239 | self::RESPONSE_END => self::RES_SUCCESS,
240 | ];
241 |
242 | public const _FLUSH_ALL_SIGNALS = [
243 | self::RESPONSE_OK => self::RES_SUCCESS,
244 | ];
245 |
246 | public const _SIGNALS = [
247 | 'add' => self::_STORE_SIGNALS,
248 | 'append' => self::_STORE_SIGNALS,
249 | 'prepend' => self::_STORE_SIGNALS,
250 | 'replace' => self::_STORE_SIGNALS,
251 | 'set' => self::_STORE_SIGNALS,
252 | 'stat' => self::_STAT_SIGNALS,
253 | 'stats' => self::_STAT_SIGNALS,
254 | 'decr' => self::_DECR_SIGNALS,
255 | 'incr' => self::_INCR_SIGNALS,
256 | 'delete' => self::_DELETE_SIGNALS,
257 | 'flush_all' => self::_FLUSH_ALL_SIGNALS,
258 | 'get' => self::_RETRIEVE_SIGNALS,
259 | 'gets' => self::_RETRIEVE_SIGNALS,
260 | 'touch' => self::_TOUCH_SIGNALS,
261 | ];
262 |
263 | /**
264 | * Unique instance ID.
265 | *
266 | * @var string
267 | */
268 | protected $persistent_id;
269 |
270 | /**
271 | * Pristine status.
272 | *
273 | * @var bool
274 | * @see isPristine()
275 | */
276 | protected $is_pristine;
277 |
278 | /**
279 | * Last result code.
280 | *
281 | * @var int
282 | */
283 | protected $result_code = self::RES_SUCCESS;
284 |
285 | /**
286 | * Last result message.
287 | *
288 | * @var string
289 | */
290 | protected $result_message = '';
291 |
292 | /**
293 | * Servers list array.
294 | *
295 | * @var array
296 | */
297 | protected $servers = [];
298 |
299 | /**
300 | * The key of currently used server.
301 | *
302 | * @var string
303 | */
304 | protected $current_server_key;
305 |
306 | /**
307 | * Socket connection handles per server key.
308 | *
309 | * @var resource[]
310 | */
311 | protected $sockets = [];
312 |
313 | /**
314 | * Options.
315 | *
316 | * @var array
317 | */
318 | protected $options = [];
319 |
320 | /**
321 | * @var string
322 | */
323 | protected $options_id;
324 |
325 | /** @var array Dummy option array */
326 | protected static $default_options = [
327 | self::OPT_COMPRESSION => false,
328 | self::OPT_COMPRESSION_TYPE => 'fastlz',
329 | self::OPT_SERIALIZER => self::SERIALIZER_PHP,
330 | self::OPT_PREFIX_KEY => '',
331 | self::OPT_HASH => self::HASH_DEFAULT,
332 | self::OPT_DISTRIBUTION => self::DISTRIBUTION_MODULA,
333 | self::OPT_LIBKETAMA_COMPATIBLE => false,
334 | self::OPT_BUFFER_WRITES => false,
335 | self::OPT_BINARY_PROTOCOL => false,
336 | self::OPT_NO_BLOCK => false,
337 | self::OPT_TCP_NODELAY => false,
338 |
339 | // This two is a value by guess
340 | self::OPT_SOCKET_SEND_SIZE => 32767,
341 | self::OPT_SOCKET_RECV_SIZE => 65535,
342 |
343 | self::OPT_CONNECT_TIMEOUT => 1000,
344 | self::OPT_RETRY_TIMEOUT => 0,
345 | self::OPT_SEND_TIMEOUT => 0,
346 | self::OPT_RECV_TIMEOUT => 0,
347 | self::OPT_POLL_TIMEOUT => 1000,
348 | self::OPT_CACHE_LOOKUPS => false,
349 | self::OPT_SERVER_FAILURE_LIMIT => 0,
350 |
351 | // Own emulated options.
352 | self::OPT_COMPRESSION_THRESHOLD => 2000,
353 | self::OPT_COMPRESSION_FACTOR => 1.3,
354 | ];
355 |
356 | /**
357 | * (PECL memcached >= 0.1.0)
358 | * Create a Memcached instance
359 | *
360 | * @link http://php.net/manual/en/memcached.construct.php
361 | * @param string $persistent_id [optional]
362 | */
363 | public function __construct($persistent_id = null)
364 | {
365 | // Store persistent_id.
366 | $this->persistent_id = $persistent_id;
367 |
368 | // Set pristine status: no persistent_id or persistent_id is firstly created.
369 | $this->is_pristine = $persistent_id === null || !isset($this->options[$persistent_id]);
370 |
371 | // Init options.
372 | $this->_initOptions();
373 | }
374 |
375 | /**
376 | * @return void
377 | */
378 | protected function _initOptions()
379 | {
380 | $this->options_id = $this->persistent_id ?? \spl_object_hash($this);
381 |
382 | if (!isset($this->options[$this->options_id])) {
383 | $this->options[$this->options_id] = static::$default_options;
384 |
385 | // Apply default INI options if real Memcached extension is installed.
386 | if (\extension_loaded('memcached')) {
387 | if ('' !== $serializer = (string)\ini_get('memcached.serializer')) {
388 | $this->setOption(self::OPT_SERIALIZER, $serializer);
389 | }
390 |
391 | if ('' !== $compression_type = (string)\ini_get('memcached.compression_type')) {
392 | $this->setOption(self::OPT_COMPRESSION_TYPE, $compression_type);
393 | }
394 |
395 | if ('' !== $compression_threshold = (string)\ini_get('memcached.compression_threshold')) {
396 | $this->setOption(self::OPT_COMPRESSION_THRESHOLD, $compression_threshold);
397 | }
398 |
399 | if ('' !== $compression_factor = (string)\ini_get('memcached.compression_factor')) {
400 | $this->setOption(self::OPT_COMPRESSION_FACTOR, $compression_factor);
401 | }
402 | }
403 | }
404 | }
405 |
406 | /**
407 | * (PECL memcached >= 0.1.0)
408 | * Add an item under a new key
409 | *
410 | * @link http://php.net/manual/en/memcached.add.php
411 | * @param string $key
The key under which to store the value.
412 | * @param mixed $value
The value to store.
413 | * @param int $expiration [optional]
The expiration time, defaults to 0. See Expiration Times for more info.
414 | * @return bool TRUE on success or FALSE on failure.
415 | * The Memcached::getResultCode will return
416 | * Memcached::RES_NOTSTORED if the key already exists.
417 | */
418 | public function add($key, $value, $expiration = 0)
419 | {
420 | return $this->addByKey(null, $key, $value, $expiration);
421 | }
422 |
423 | /**
424 | * (PECL memcached >= 0.1.0)
425 | * Add an item under a new key on a specific server
426 | *
427 | * @link http://php.net/manual/en/memcached.addbykey.php
428 | * @param string $server_key
The key identifying the server to store the value on or retrieve it from.
429 | * @param string $key
The key under which to store the value.
430 | * @param mixed $value
The value to store.
431 | * @param int $expiration [optional]
The expiration time, defaults to 0. See Expiration Times for more info.
432 | * @return bool TRUE on success or FALSE on failure.
433 | * The Memcached::getResultCode will return
434 | * Memcached::RES_NOTSTORED if the key already exists.
435 | */
436 | public function addByKey($server_key, $key, $value, $expiration = 0)
437 | {
438 | $real_key = $this->_getKey($key);
439 | $expiration = (int)$expiration;
440 |
441 | if (!$this->_serialize($value, $flags, $bytes)) {
442 | return $this->_return(false, self::RES_PAYLOAD_FAILURE);
443 | }
444 |
445 | // add [noreply]\r\n\r\n
446 | if ((false !== $response = $this->_query("add $real_key $flags $expiration $bytes\r\n$value", $server_key))
447 | &&
448 | // Valid response.
449 | isset(self::_STORE_SIGNALS[$response])
450 | ) {
451 | return $this->_return(self::_STORE_SIGNALS[$response] === self::RES_SUCCESS,
452 | self::_STORE_SIGNALS[$response]);
453 | }
454 |
455 | return $this->_return(false, self::RES_FAILURE, __METHOD__ . ' failed.');
456 | }
457 |
458 | /**
459 | * (PECL memcached >= 0.1.0)
460 | * Add a server to the server pool
461 | *
462 | * @link http://php.net/manual/en/memcached.addserver.php
463 | * @param string $host
The hostname of the memcache server. If the hostname is invalid, data-related
464 | * operations will set Memcached::RES_HOST_LOOKUP_FAILURE result code.
465 | * @param int $port
The port on which memcache is running. Usually, this is 11211.
466 | * @param int $weight [optional]
467 | * The weight of the server relative to the total weight of all the
468 | * servers in the pool. This controls the probability of the server being
469 | * selected for operations. This is used only with consistent distribution
470 | * option and usually corresponds to the amount of memory available to
471 | * memcache on that server.
472 | *
525 | * @return bool TRUE on success or FALSE on failure.
526 | * The Memcached::getResultCode will return
527 | * Memcached::RES_NOTSTORED if the key does not exist.
528 | */
529 | public function append($key, $value)
530 | {
531 | return $this->appendByKey(null, $key, $value);
532 | }
533 |
534 | /**
535 | * (PECL memcached >= 0.1.0)
536 | * Append data to an existing item on a specific server
537 | *
538 | * @link http://php.net/manual/en/memcached.appendbykey.php
539 | * @param string $server_key
The key identifying the server to store the value on or retrieve it from.
540 | * @param string $key
The key under which to store the value.
541 | * @param string $value
The string to append.
542 | * @return bool TRUE on success or FALSE on failure.
543 | * The Memcached::getResultCode will return
544 | * Memcached::RES_NOTSTORED if the key does not exist.
545 | */
546 | public function appendByKey($server_key, $key, $value)
547 | {
548 | if (!\is_scalar($value)) {
549 | return $this->_return(false, self::RES_PAYLOAD_FAILURE);
550 | }
551 |
552 | $real_key = $this->_getKey($key);
553 | $bytes = \strlen($value);
554 |
555 | // append [noreply]\r\n\r\n
556 | // flags and exptime are ignored.
557 | if ((false !== $response = $this->_query("append $real_key 0 0 $bytes\r\n$value", $server_key))
558 | &&
559 | // Valid response.
560 | isset(self::_STORE_SIGNALS[$response])
561 | ) {
562 | return $this->_return(self::_STORE_SIGNALS[$response] === self::RES_SUCCESS,
563 | self::_STORE_SIGNALS[$response]);
564 | }
565 |
566 | return $this->_return(false, self::RES_FAILURE, __METHOD__ . ' failed.');
567 | }
568 |
569 | /**
570 | * (PECL memcached >= 0.1.0)
571 | * Compare and swap an item
572 | *
573 | * @link http://php.net/manual/en/memcached.cas.php
574 | * @param float $cas_token
Unique value associated with the existing item. Generated by memcache.
575 | * @param string $key
The key under which to store the value.
576 | * @param mixed $value
The value to store.
577 | * @param int $expiration [optional]
The expiration time, defaults to 0. See Expiration Times for more info.
578 | * @return bool TRUE on success or FALSE on failure.
579 | * The Memcached::getResultCode will return
580 | * Memcached::RES_DATA_EXISTS if the item you are trying
581 | * to store has been modified since you last fetched it.
582 | */
583 | public function cas($cas_token, $key, $value, $expiration = 0)
584 | {
585 | return $this->casByKey($cas_token, null, $key, $value, $expiration);
586 | }
587 |
588 | /**
589 | * (PECL memcached >= 0.1.0)
590 | * Compare and swap an item on a specific server
591 | *
592 | * @link http://php.net/manual/en/memcached.casbykey.php
593 | * @param float $cas_token
Unique value associated with the existing item. Generated by memcache.
594 | * @param string $server_key
The key identifying the server to store the value on or retrieve it from.
595 | * @param string $key
The key under which to store the value.
596 | * @param mixed $value
The value to store.
597 | * @param int $expiration [optional]
The expiration time, defaults to 0. See Expiration Times for more info.
598 | * @return bool TRUE on success or FALSE on failure.
599 | * The Memcached::getResultCode will return
600 | * Memcached::RES_DATA_EXISTS if the item you are trying
601 | * to store has been modified since you last fetched it.
602 | */
603 | public function casByKey($cas_token, $server_key, $key, $value, $expiration = 0)
604 | {
605 | $real_key = $this->_getKey($key);
606 | $expiration = (int)$expiration;
607 |
608 | if (!$this->_serialize($value, $flags, $bytes)) {
609 | return $this->_return(false, self::RES_PAYLOAD_FAILURE);
610 | }
611 |
612 | // cas [noreply]\r\n
613 | if ((false !== $response = $this->_query("cas $real_key $flags $expiration $bytes $cas_token\r\n$value",
614 | $server_key))
615 | &&
616 | // Valid response.
617 | isset(self::_STORE_SIGNALS[$response])
618 | ) {
619 | return $this->_return(self::_STORE_SIGNALS[$response] === self::RES_SUCCESS,
620 | self::_STORE_SIGNALS[$response]);
621 | }
622 |
623 | return $this->_return(false, self::RES_FAILURE, __METHOD__ . ' failed.');
624 | }
625 |
626 | /**
627 | * (PECL memcached >= 0.1.0)
628 | * Decrement numeric item's value
629 | *
630 | * @link http://php.net/manual/en/memcached.decrement.php
631 | * @param string $key
The key of the item to decrement.
632 | * @param int $offset [optional]
The amount by which to decrement the item's value.
633 | * @param int $initial_value [optional]
The value to set the item to if it doesn't currently exist.
634 | * @param int $expiry [optional]
The expiry time to set on the item.
635 | * @return int item's new value on success or FALSE on failure.
636 | */
637 | public function decrement($key, $offset = 1, $initial_value = 0, $expiry = 0)
638 | {
639 | // Note: we use only single default server.
640 | return $this->decrementByKey(null, $key, $offset, $initial_value, $expiry);
641 | }
642 |
643 | /**
644 | * (PECL memcached >= 2.0.0)
645 | * Decrement numeric item's value, stored on a specific server
646 | *
647 | * @link http://php.net/manual/en/memcached.decrementbykey.php
648 | * @param string $server_key
The key identifying the server to store the value on or retrieve it from.
649 | * @param string $key
The key of the item to decrement.
650 | * @param int $offset [optional]
The amount by which to decrement the item's value.
651 | * @param int $initial_value [optional]
The value to set the item to if it doesn't currently exist.
652 | * @param int $expiry [optional]
The expiry time to set on the item.
653 | * @return int item's new value on success or FALSE on failure.
654 | */
655 | public function decrementByKey($server_key, $key, $offset = 1, $initial_value = 0, $expiry = 0)
656 | {
657 | $real_key = $this->_getKey($key);
658 | $expiry = (int)$expiry;
659 |
660 | if (!\is_scalar($offset)) {
661 | return $this->_return(false, self::RES_PAYLOAD_FAILURE);
662 | }
663 |
664 | // decr [noreply]\r\n
665 | if (false !== $response = $this->_query("decr $real_key $offset", $server_key)) {
666 | // Not found? Use initial value.
667 | if ($response === self::RESPONSE_NOT_FOUND) {
668 | // If the operation would decrease the value below 0, the new value will be 0.
669 | $value = \max(0, $initial_value - $offset);
670 |
671 | return $this->setByKey($server_key, $key, $initial_value, $expiry) ? $value : false;
672 | }
673 |
674 | // Another invalid response.
675 | if (isset(self::_STORE_SIGNALS[$response])) {
676 |
677 | // Another response
678 | return $this->_return(false, self::_STORE_SIGNALS[$response]);
679 | }
680 | }
681 |
682 | return (int)$response;
683 | }
684 |
685 | /**
686 | * (PECL memcached >= 0.1.0)
687 | * Delete an item
688 | *
689 | * @link http://php.net/manual/en/memcached.delete.php
690 | * @param string $key
The key to be deleted.
691 | * @param int $time [optional]
The amount of time the server will wait to delete the item.
692 | * @return bool TRUE on success or FALSE on failure.
693 | * The Memcached::getResultCode will return
694 | * Memcached::RES_NOTFOUND if the key does not exist.
695 | */
696 | public function delete($key, $time = 0)
697 | {
698 | return $this->deleteByKey(null, $key, $time);
699 | }
700 |
701 | /**
702 | * (PECL memcached >= 0.1.0)
703 | * Delete an item from a specific server
704 | *
705 | * @link http://php.net/manual/en/memcached.deletebykey.php
706 | * @param string $server_key
The key identifying the server to store the value on or retrieve it from.
707 | * @param string $key
The key to be deleted.
708 | * @param int $time [optional]
The amount of time the server will wait to delete the item.
709 | * @return bool TRUE on success or FALSE on failure.
710 | * The Memcached::getResultCode will return
711 | * Memcached::RES_NOTFOUND if the key does not exist.
712 | */
713 | public function deleteByKey($server_key, $key, $time = 0)
714 | {
715 | if ($time !== 0) {
716 | throw new \BadMethodCallException(\sprintf('%s does not emulate $time param.', __METHOD__));
717 | }
718 |
719 | $real_key = $this->_getKey($key);
720 |
721 | // delete [