* @ingroup Cache
*/
class MemcachedBagOStuff extends BagOStuff {
+ /** @var MWMemcached|Memcached */
protected $client;
/**
return $params;
}
- /**
- * @param string $key
- * @param mixed $casToken [optional]
- * @return mixed
- */
- public function get( $key, &$casToken = null ) {
+ protected function doGet( $key, $flags = 0 ) {
+ $casToken = null;
+
+ return $this->getWithToken( $key, $casToken, $flags );
+ }
+
+ protected function getWithToken( $key, &$casToken, $flags = 0 ) {
return $this->client->get( $this->encodeKey( $key ), $casToken );
}
- /**
- * @param string $key
- * @param mixed $value
- * @param int $exptime
- * @return bool
- */
- public function set( $key, $value, $exptime = 0 ) {
+ public function set( $key, $value, $exptime = 0, $flags = 0 ) {
return $this->client->set( $this->encodeKey( $key ), $value,
$this->fixExpiry( $exptime ) );
}
- /**
- * @param mixed $casToken
- * @param string $key
- * @param mixed $value
- * @param int $exptime
- * @return bool
- */
protected function cas( $casToken, $key, $value, $exptime = 0 ) {
return $this->client->cas( $casToken, $this->encodeKey( $key ),
$value, $this->fixExpiry( $exptime ) );
}
- /**
- * @param string $key
- * @return bool
- */
public function delete( $key ) {
return $this->client->delete( $this->encodeKey( $key ) );
}
- /**
- * @param string $key
- * @param int $value
- * @param int $exptime (default 0)
- * @return mixed
- */
public function add( $key, $value, $exptime = 0 ) {
return $this->client->add( $this->encodeKey( $key ), $value,
$this->fixExpiry( $exptime ) );
}
- public function merge( $key, $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
if ( !is_callable( $callback ) ) {
throw new Exception( "Got invalid callback." );
}
return $this->client;
}
+ /**
+ * Construct a cache key.
+ *
+ * @since 1.27
+ * @param string $keyspace
+ * @param array $args
+ * @return string
+ */
+ public function makeKeyInternal( $keyspace, $args ) {
+ // Memcached keys have a maximum length of 255 characters. From that,
+ // subtract the number of characters we need for the keyspace and for
+ // the separator character needed for each argument.
+ $charsLeft = 255 - strlen( $keyspace ) - count( $args );
+
+ $that = $this;
+ $args = array_map(
+ function ( $arg ) use ( $that, &$charsLeft ) {
+ // Because MemcachedBagOStuff::encodeKey() will be called again
+ // with this input once the key is actually used, we have to
+ // encode pound signs here rather than in encodeKey().
+ $arg = $that->encodeKey( str_replace( '#', '%23', $arg ) );
+
+ // 33 = 32 characters for the MD5 + 1 for the '#' prefix.
+ if ( $charsLeft > 33 && strlen( $arg ) > $charsLeft ) {
+ $arg = '#' . md5( $arg );
+ }
+
+ $charsLeft -= strlen( $arg );
+ return $arg;
+ },
+ $args
+ );
+
+ if ( $charsLeft < 0 ) {
+ $args = array( '##' . md5( implode( ':', $args ) ) );
+ }
+
+ return parent::makeKeyInternal( $keyspace, $args );
+ }
+
/**
* Encode a key for use on the wire inside the memcached protocol.
*
* @return string
*/
public function encodeKey( $key ) {
- return preg_replace_callback( '/[\x00-\x20\x25\x7f]+/',
+ return preg_replace_callback( '/[^\x21-\x7e]+/',
array( $this, 'encodeKeyCallback' ), $key );
}
* @return string
*/
public function decodeKey( $key ) {
- return urldecode( $key );
+ // matches %00-%20, %25, %7F (=decoded alternatives for those encoded in encodeKey)
+ return preg_replace_callback( '/%([0-1][0-9]|20|25|7F)/i', function ( $match ) {
+ return urldecode( $match[0] );
+ }, $key );
}
/**