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' ) {
148 if ( !Memcached::HAVE_IGBINARY ) {
149 throw new RuntimeException(
150 __CLASS__ .
': the igbinary extension is not available ' .
151 'but igbinary serialization was requested.'
154 $options[Memcached::OPT_SERIALIZER] = Memcached::SERIALIZER_IGBINARY;
157 if ( !$client->setOptions( $options ) ) {
158 throw new RuntimeException(
159 "Invalid options: " . json_encode( $options, JSON_PRETTY_PRINT )
164 foreach ( $params[
'servers'] as $host ) {
165 if ( preg_match(
'/^\[(.+)\]:(\d+)$/', $host, $m ) ) {
166 $servers[] = [ $m[1], (int)$m[2] ];
167 } elseif ( preg_match(
'/^([^:]+):(\d+)$/', $host, $m ) ) {
168 $servers[] = [ $m[1], (int)$m[2] ];
170 $servers[] = [ $host, false ];
174 if ( !$client->addServers( $servers ) ) {
175 throw new RuntimeException(
"Failed to inject server address list" );
179 protected function doGet( $key, $flags = 0, &$casToken =
null ) {
180 $this->
debug(
"get($key)" );
183 if ( defined( Memcached::class .
'::GET_EXTENDED' ) ) {
185 $flags = Memcached::GET_EXTENDED;
187 if ( is_array(
$res ) ) {
188 $result =
$res[
'value'];
189 $casToken =
$res[
'cas'];
201 protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
202 $this->
debug(
"set($key)" );
205 $result = $client->set(
211 return ( $result ===
false && $client->getResultCode() === Memcached::RES_NOTSTORED )
217 protected function doCas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
218 $this->
debug(
"cas($key)" );
230 $this->
debug(
"delete($key)" );
235 return ( $result ===
false && $client->getResultCode() === Memcached::RES_NOTFOUND )
241 protected function doAdd( $key, $value, $exptime = 0, $flags = 0 ) {
242 $this->
debug(
"add($key)" );
253 public function incr( $key, $value = 1, $flags = 0 ) {
254 $this->
debug(
"incr($key)" );
261 public function decr( $key, $value = 1, $flags = 0 ) {
262 $this->
debug(
"decr($key)" );
281 if ( $result !==
false ) {
286 switch ( $client->getResultCode() ) {
287 case Memcached::RES_SUCCESS:
289 case Memcached::RES_DATA_EXISTS:
290 case Memcached::RES_NOTSTORED:
291 case Memcached::RES_NOTFOUND:
292 $this->
debug(
"result: " . $client->getResultMessage() );
295 $msg = $client->getResultMessage();
297 if ( $key !==
false ) {
298 $server = $client->getServerByKey( $key );
299 $logCtx[
'memcached-server'] =
"{$server['host']}:{$server['port']}";
300 $logCtx[
'memcached-key'] = $key;
301 $msg =
"Memcached error for key \"{memcached-key}\" " .
302 "on server \"{memcached-server}\": $msg";
304 $msg =
"Memcached error: $msg";
306 $this->logger->error( $msg, $logCtx );
313 $this->
debug(
'getMulti(' . implode(
', ',
$keys ) .
')' );
315 foreach (
$keys as $key ) {
325 protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
326 $this->
debug(
'setMulti(' . implode(
', ', array_keys( $data ) ) .
')' );
329 foreach ( array_keys( $data ) as $key ) {
335 if ( $this->
fieldHasFlags( $flags, self::WRITE_BACKGROUND ) ) {
337 $result = $client->setMulti( $data, $exptime );
347 $this->
debug(
'deleteMulti(' . implode(
', ',
$keys ) .
')' );
349 foreach (
$keys as $key ) {
355 if ( $this->
fieldHasFlags( $flags, self::WRITE_BACKGROUND ) ) {
357 $resultArray = $client->deleteMulti(
$keys ) ?: [];
364 foreach ( $resultArray as $code ) {
365 if ( !in_array( $code, [
true, Memcached::RES_NOTFOUND ],
true ) ) {
375 $this->
debug(
"touch($key)" );
383 if ( is_int( $value ) ) {
387 $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
388 if ( $serializer === Memcached::SERIALIZER_PHP ) {
390 } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
391 return igbinary_serialize( $value );
394 throw new UnexpectedValueException( __METHOD__ .
": got serializer '$serializer'." );
402 $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
403 if ( $serializer === Memcached::SERIALIZER_PHP ) {
405 } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
406 return igbinary_unserialize( $value );
409 throw new UnexpectedValueException( __METHOD__ .
": got serializer '$serializer'." );
416 if ( $this->syncClientIsBuffering ) {
417 throw new RuntimeException(
"The main (unbuffered I/O) client is locked" );
420 if ( $this->hasUnflushedChanges ) {
422 $this->syncClient->fetch();
423 if ( $this->asyncClient ) {
424 $this->asyncClient->fetch();
426 $this->hasUnflushedChanges =
false;
436 if ( $this->asyncClient ) {
441 $this->syncClientIsBuffering =
true;
442 $this->syncClient->setOptions( self::$OPTS_ASYNC_WRITES );
451 $this->hasUnflushedChanges =
true;
453 if ( !$this->asyncClient ) {
455 $client->setOptions( self::$OPTS_SYNC_WRITES );
456 $this->syncClientIsBuffering =
false;
fieldHasFlags( $field, $flags)
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)
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