42 Memcached::OPT_NO_BLOCK =>
false,
43 Memcached::OPT_BUFFER_WRITES =>
false
47 Memcached::OPT_NO_BLOCK =>
true,
48 Memcached::OPT_BUFFER_WRITES =>
true
68 parent::__construct( $params );
72 'compress_threshold' => 1500,
73 'connect_timeout' => 0.5,
74 'serializer' =>
'php',
75 'use_binary_protocol' =>
false,
76 'allow_tcp_nagle_delay' =>
true
79 if ( $params[
'persistent'] ) {
83 $connectionPoolId = md5(
serialize( $params ) );
84 $syncClient =
new Memcached(
"$connectionPoolId-sync" );
86 $asyncClient =
new Memcached(
"$connectionPoolId-async" );
103 ini_set(
'memcached.compression_threshold', $params[
'compress_threshold'] );
116 if ( $client->getServerList() ) {
117 $this->logger->debug( __METHOD__ .
": pre-initialized client instance." );
122 $this->logger->debug( __METHOD__ .
": initializing new client instance." );
125 Memcached::OPT_NO_BLOCK =>
false,
126 Memcached::OPT_BUFFER_WRITES =>
false,
128 Memcached::OPT_BINARY_PROTOCOL => $params[
'use_binary_protocol'],
130 Memcached::OPT_CONNECT_TIMEOUT => $params[
'connect_timeout'] * 1000,
131 Memcached::OPT_SEND_TIMEOUT => $params[
'timeout'],
132 Memcached::OPT_RECV_TIMEOUT => $params[
'timeout'],
133 Memcached::OPT_POLL_TIMEOUT => $params[
'timeout'] / 1000,
135 Memcached::OPT_TCP_NODELAY => !$params[
'allow_tcp_nagle_delay'],
137 Memcached::OPT_LIBKETAMA_COMPATIBLE =>
true
139 if ( isset( $params[
'retry_timeout'] ) ) {
140 $options[Memcached::OPT_RETRY_TIMEOUT] = $params[
'retry_timeout'];
142 if ( isset( $params[
'server_failure_limit'] ) ) {
143 $options[Memcached::OPT_SERVER_FAILURE_LIMIT] = $params[
'server_failure_limit'];
145 if ( $params[
'serializer'] ===
'php' ) {
146 $options[Memcached::OPT_SERIALIZER] = Memcached::SERIALIZER_PHP;
147 } elseif ( $params[
'serializer'] ===
'igbinary' ) {
149 if ( !Memcached::HAVE_IGBINARY ) {
150 throw new RuntimeException(
151 __CLASS__ .
': the igbinary extension is not available ' .
152 'but igbinary serialization was requested.'
155 $options[Memcached::OPT_SERIALIZER] = Memcached::SERIALIZER_IGBINARY;
158 if ( !$client->setOptions( $options ) ) {
159 throw new RuntimeException(
160 "Invalid options: " . json_encode( $options, JSON_PRETTY_PRINT )
165 foreach ( $params[
'servers'] as $host ) {
166 if ( preg_match(
'/^\[(.+)\]:(\d+)$/', $host, $m ) ) {
167 $servers[] = [ $m[1], (int)$m[2] ];
168 } elseif ( preg_match(
'/^([^:]+):(\d+)$/', $host, $m ) ) {
169 $servers[] = [ $m[1], (int)$m[2] ];
171 $servers[] = [ $host, false ];
175 if ( !$client->addServers( $servers ) ) {
176 throw new RuntimeException(
"Failed to inject server address list" );
180 protected function doGet( $key, $flags = 0, &$casToken =
null ) {
181 $this->
debug(
"get($key)" );
184 if ( defined( Memcached::class .
'::GET_EXTENDED' ) ) {
186 $flags = Memcached::GET_EXTENDED;
188 if ( is_array(
$res ) ) {
189 $result =
$res[
'value'];
190 $casToken =
$res[
'cas'];
202 protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
203 $this->
debug(
"set($key)" );
206 $result = $client->set(
212 return ( $result ===
false && $client->getResultCode() === Memcached::RES_NOTSTORED )
218 protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
219 $this->
debug(
"cas($key)" );
231 $this->
debug(
"delete($key)" );
236 return ( $result ===
false && $client->getResultCode() === Memcached::RES_NOTFOUND )
242 protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
243 $this->
debug(
"add($key)" );
254 public function incr( $key, $value = 1, $flags = 0 ) {
255 $this->
debug(
"incr($key)" );
262 public function decr( $key, $value = 1, $flags = 0 ) {
263 $this->
debug(
"decr($key)" );
273 foreach ( $valueByKey as $value ) {
292 if ( $result !==
false ) {
297 switch ( $client->getResultCode() ) {
298 case Memcached::RES_SUCCESS:
300 case Memcached::RES_DATA_EXISTS:
301 case Memcached::RES_NOTSTORED:
302 case Memcached::RES_NOTFOUND:
303 $this->
debug(
"result: " . $client->getResultMessage() );
306 $msg = $client->getResultMessage();
308 if ( $key !==
false ) {
309 $server = $client->getServerByKey( $key );
310 $logCtx[
'memcached-server'] =
"{$server['host']}:{$server['port']}";
311 $logCtx[
'memcached-key'] = $key;
312 $msg =
"Memcached error for key \"{memcached-key}\" " .
313 "on server \"{memcached-server}\": $msg";
315 $msg =
"Memcached error: $msg";
317 $this->logger->error( $msg, $logCtx );
324 $this->
debug(
'getMulti(' . implode(
', ',
$keys ) .
')' );
326 foreach (
$keys as $key ) {
336 protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
337 $this->
debug(
'setMulti(' . implode(
', ', array_keys( $data ) ) .
')' );
340 foreach ( array_keys( $data ) as $key ) {
346 if ( $this->
fieldHasFlags( $flags, self::WRITE_BACKGROUND ) ) {
348 $result = $client->setMulti( $data, $exptime );
358 $this->
debug(
'deleteMulti(' . implode(
', ',
$keys ) .
')' );
360 foreach (
$keys as $key ) {
366 if ( $this->
fieldHasFlags( $flags, self::WRITE_BACKGROUND ) ) {
368 $resultArray = $client->deleteMulti(
$keys ) ?: [];
375 foreach ( $resultArray as $code ) {
376 if ( !in_array( $code, [
true, Memcached::RES_NOTFOUND ],
true ) ) {
386 $this->
debug(
"touch($key)" );
394 if ( is_int( $value ) ) {
398 $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
399 if ( $serializer === Memcached::SERIALIZER_PHP ) {
401 } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
402 return igbinary_serialize( $value );
405 throw new UnexpectedValueException( __METHOD__ .
": got serializer '$serializer'." );
413 $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
414 if ( $serializer === Memcached::SERIALIZER_PHP ) {
416 } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
417 return igbinary_unserialize( $value );
420 throw new UnexpectedValueException( __METHOD__ .
": got serializer '$serializer'." );
427 if ( $this->syncClientIsBuffering ) {
428 throw new RuntimeException(
"The main (unbuffered I/O) client is locked" );
431 if ( $this->hasUnflushedChanges ) {
433 $this->syncClient->fetch();
434 if ( $this->asyncClient ) {
435 $this->asyncClient->fetch();
437 $this->hasUnflushedChanges =
false;
447 if ( $this->asyncClient ) {
452 $this->syncClientIsBuffering =
true;
453 $this->syncClient->setOptions( self::$OPTS_ASYNC_WRITES );
462 $this->hasUnflushedChanges =
true;
464 if ( !$this->asyncClient ) {
466 $client->setOptions( self::$OPTS_SYNC_WRITES );
467 $this->syncClientIsBuffering =
false;
fieldHasFlags( $field, $flags)
guessSerialValueSize( $value, $depth=0, &$loops=0)
Estimate the size of a variable once serialized.
isInteger( $value)
Check if a value is an integer.
setLastError( $err)
Set the "last error" registry.
Base class for memcached clients.
validateKeyEncoding( $key)
Ensure that a key is safe to use (contains no control characters and no characters above the ASCII ra...
A wrapper class for the PECL memcached client.
doAdd( $key, $value, $exptime=0, $flags=0)
Insert an item if it does not already exist.
bool $syncClientIsBuffering
Whether the non-buffering client is locked from use.
doGet( $key, $flags=0, &$casToken=null)
doGetMulti(array $keys, $flags=0)
Get an associative array containing the item for each of the keys that have items.
doCas( $casToken, $key, $value, $exptime=0, $flags=0)
Check and set an item.
doSet( $key, $value, $exptime=0, $flags=0)
Set an item.
incr( $key, $value=1, $flags=0)
Increase stored value of $key by $value while preserving its TTL.
doChangeTTL( $key, $exptime, $flags)
doDeleteMulti(array $keys, $flags=0)
decr( $key, $value=1, $flags=0)
Decrease stored value of $key by $value while preserving its TTL.
releaseAsyncClient( $client)
setNewPreparedValues(array $valueByKey)
Prepare values for storage and get their serialized sizes, or, estimate those sizes.
initializeClient(Memcached $client, array $params, array $options)
Initialize the client only if needed and reuse it otherwise.
bool $hasUnflushedChanges
Whether the non-buffering client should be flushed before use.
static array $OPTS_SYNC_WRITES
Memcached options.
doSetMulti(array $data, $exptime=0, $flags=0)
static array $OPTS_ASYNC_WRITES
Memcached options.
checkResult( $key, $result)
Check the return value from a client method call and take any necessary action.
doDelete( $key, $flags=0)
Delete an item.
__construct( $params)
Available parameters are:
Memcached null $asyncClient