├── .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 | [![Build Status](https://travis-ci.org/avantarm/memcached-emulator.png?branch=master)](https://travis-ci.org/avantarm/memcached-emulator) 3 | [![Latest Stable Version](https://poser.pugx.org/avantarm/memcached-emulator/v/stable)](https://packagist.org/packages/avantarm/memcached-emulator) 4 | [![Total Downloads](https://poser.pugx.org/avantarm/memcached-emulator/downloads)](https://packagist.org/packages/avantarm/memcached-emulator) 5 | [![Latest Unstable Version](https://poser.pugx.org/avantarm/memcached-emulator/v/unstable)](https://packagist.org/packages/avantarm/memcached-emulator) 6 | [![License](https://poser.pugx.org/avantarm/memcached-emulator/license)](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 | *

473 | * @return bool TRUE on success or FALSE on failure. 474 | */ 475 | public function addServer($host, $port, $weight = 0) 476 | { 477 | // Create native server key. 478 | $key = $host . ':' . $port; 479 | 480 | if (isset($this->servers[$key])) { 481 | return $this->_return(false, self::RES_FAILURE, 'Server already exists.'); 482 | } 483 | 484 | $this->servers[$key] = [ 485 | 'host' => $host, 486 | 'port' => $port, 487 | 'weight' => $weight, 488 | ]; 489 | 490 | return true; 491 | } 492 | 493 | /** 494 | * (PECL memcached >= 0.1.1)
495 | * Add multiple servers to the server pool 496 | * 497 | * @link http://php.net/manual/en/memcached.addservers.php 498 | * @param array $servers 499 | * @return bool TRUE on success or FALSE on failure. 500 | */ 501 | public function addServers(array $servers) 502 | { 503 | foreach ($servers as $server) { 504 | if (\count($server) !== 3) { 505 | return false; 506 | } 507 | 508 | [$host, $port, $weight] = $server; 509 | 510 | if (!$this->addServer($host, $port, $weight)) { 511 | return false; 512 | } 513 | } 514 | 515 | return true; 516 | } 517 | 518 | /** 519 | * (PECL memcached >= 0.1.0)
520 | * Append data to an existing item 521 | * 522 | * @link http://php.net/manual/en/memcached.append.php 523 | * @param string $key

The key under which to store the value.

524 | * @param string $value

The string to append.

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 [