summaryrefslogtreecommitdiff
path: root/includes/objectcache
diff options
context:
space:
mode:
Diffstat (limited to 'includes/objectcache')
-rw-r--r--includes/objectcache/APCBagOStuff.php30
-rw-r--r--includes/objectcache/BagOStuff.php180
-rw-r--r--includes/objectcache/DBABagOStuff.php307
-rw-r--r--includes/objectcache/EhcacheBagOStuff.php324
-rw-r--r--includes/objectcache/EmptyBagOStuff.php37
-rw-r--r--includes/objectcache/HashBagOStuff.php31
-rw-r--r--includes/objectcache/MemcachedBagOStuff.php58
-rw-r--r--includes/objectcache/MemcachedClient.php274
-rw-r--r--includes/objectcache/MemcachedPeclBagOStuff.php87
-rw-r--r--includes/objectcache/MemcachedPhpBagOStuff.php28
-rw-r--r--includes/objectcache/MultiWriteBagOStuff.php83
-rw-r--r--includes/objectcache/ObjectCache.php28
-rw-r--r--includes/objectcache/ObjectCacheSessionHandler.php31
-rw-r--r--includes/objectcache/RedisBagOStuff.php215
-rw-r--r--includes/objectcache/SqlBagOStuff.php222
-rw-r--r--includes/objectcache/WinCacheBagOStuff.php22
-rw-r--r--includes/objectcache/XCacheBagOStuff.php30
17 files changed, 797 insertions, 1190 deletions
diff --git a/includes/objectcache/APCBagOStuff.php b/includes/objectcache/APCBagOStuff.php
index 3fb80835..4cbb32df 100644
--- a/includes/objectcache/APCBagOStuff.php
+++ b/includes/objectcache/APCBagOStuff.php
@@ -28,8 +28,8 @@
*/
class APCBagOStuff extends BagOStuff {
/**
- * @param $key string
- * @param $casToken[optional] int
+ * @param string $key
+ * @param int $casToken [optional]
* @return mixed
*/
public function get( $key, &$casToken = null ) {
@@ -49,9 +49,9 @@ class APCBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function set( $key, $value, $exptime = 0 ) {
@@ -65,10 +65,10 @@ class APCBagOStuff extends BagOStuff {
}
/**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -77,8 +77,8 @@ class APCBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
public function delete( $key, $time = 0 ) {
@@ -88,13 +88,13 @@ class APCBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
return $this->mergeViaLock( $key, $callback, $exptime, $attempts );
}
diff --git a/includes/objectcache/BagOStuff.php b/includes/objectcache/BagOStuff.php
index 857943ee..1978c3ea 100644
--- a/includes/objectcache/BagOStuff.php
+++ b/includes/objectcache/BagOStuff.php
@@ -3,7 +3,7 @@
* Classes to cache objects in PHP accelerators, SQL database or DBA files
*
* Copyright © 2003-2004 Brion Vibber <brion@pobox.com>
- * http://www.mediawiki.org/
+ * https://www.mediawiki.org/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,8 +43,16 @@
abstract class BagOStuff {
private $debugMode = false;
+ protected $lastError = self::ERR_NONE;
+
+ /** Possible values for getLastError() */
+ const ERR_NONE = 0; // no error
+ const ERR_NO_RESPONSE = 1; // no response
+ const ERR_UNREACHABLE = 2; // can't connect
+ const ERR_UNEXPECTED = 3; // response gave some error
+
/**
- * @param $bool bool
+ * @param bool $bool
*/
public function setDebug( $bool ) {
$this->debugMode = $bool;
@@ -55,34 +63,34 @@ abstract class BagOStuff {
/**
* Get an item with the given key. Returns false if it does not exist.
- * @param $key string
- * @param $casToken[optional] mixed
+ * @param string $key
+ * @param mixed $casToken [optional]
* @return mixed Returns false on failure
*/
abstract public function get( $key, &$casToken = null );
/**
* Set an item.
- * @param $key string
- * @param $value mixed
+ * @param string $key
+ * @param mixed $value
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
- * @return bool success
+ * @return bool Success
*/
abstract public function set( $key, $value, $exptime = 0 );
/**
* Check and set an item.
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
- * @return bool success
+ * @return bool Success
*/
abstract public function cas( $casToken, $key, $value, $exptime = 0 );
/**
* Delete an item.
- * @param $key string
+ * @param string $key
* @param int $time Amount of time to delay the operation (mostly memcached-specific)
* @return bool True if the item was deleted or not found, false on failure
*/
@@ -93,26 +101,26 @@ abstract class BagOStuff {
* The callback function returns the new value given the current value (possibly false),
* and takes the arguments: (this BagOStuff object, cache key, current value).
*
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
return $this->mergeViaCas( $key, $callback, $exptime, $attempts );
}
/**
* @see BagOStuff::merge()
*
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- protected function mergeViaCas( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
+ protected function mergeViaCas( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
do {
$casToken = null; // passed by reference
$currentValue = $this->get( $key, $casToken ); // get the old value
@@ -135,14 +143,14 @@ abstract class BagOStuff {
/**
* @see BagOStuff::merge()
*
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- protected function mergeViaLock( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
- if ( !$this->lock( $key, 60 ) ) {
+ protected function mergeViaLock( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
+ if ( !$this->lock( $key, 6 ) ) {
return false;
}
@@ -164,14 +172,17 @@ abstract class BagOStuff {
}
/**
- * @param $key string
- * @param $timeout integer [optional]
- * @return bool success
+ * @param string $key
+ * @param int $timeout [optional]
+ * @return bool Success
*/
- public function lock( $key, $timeout = 60 ) {
+ public function lock( $key, $timeout = 6 ) {
+ $this->clearLastError();
$timestamp = microtime( true ); // starting UNIX timestamp
if ( $this->add( "{$key}:lock", 1, $timeout ) ) {
return true;
+ } elseif ( $this->getLastError() ) {
+ return false;
}
$uRTT = ceil( 1e6 * ( microtime( true ) - $timestamp ) ); // estimate RTT (us)
@@ -186,15 +197,19 @@ abstract class BagOStuff {
$sleep *= 2;
}
usleep( $sleep ); // back off
+ $this->clearLastError();
$locked = $this->add( "{$key}:lock", 1, $timeout );
+ if ( $this->getLastError() ) {
+ return false;
+ }
} while ( !$locked );
return $locked;
}
/**
- * @param $key string
- * @return bool success
+ * @param string $key
+ * @return bool Success
*/
public function unlock( $key ) {
return $this->delete( "{$key}:lock" );
@@ -203,11 +218,11 @@ abstract class BagOStuff {
/**
* Delete all objects expiring before a certain date.
* @param string $date The reference date in MW format
- * @param $progressCallback callback|bool Optional, a function which will be called
+ * @param callable|bool $progressCallback Optional, a function which will be called
* regularly during long-running operations with the percentage progress
* as the first parameter.
*
- * @return bool on success, false if unimplemented
+ * @return bool Success, false if unimplemented
*/
public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) {
// stub
@@ -219,7 +234,7 @@ abstract class BagOStuff {
/**
* Get an associative array containing the item for each of the keys that have items.
* @param array $keys List of strings
- * @return Array
+ * @return array
*/
public function getMulti( array $keys ) {
$res = array();
@@ -233,36 +248,40 @@ abstract class BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime integer
- * @return bool success
+ * Batch insertion
+ * @param array $data $key => $value assoc array
+ * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+ * @return bool Success
+ * @since 1.24
*/
- public function add( $key, $value, $exptime = 0 ) {
- if ( $this->get( $key ) === false ) {
- return $this->set( $key, $value, $exptime );
+ public function setMulti( array $data, $exptime = 0 ) {
+ $res = true;
+ foreach ( $data as $key => $value ) {
+ if ( !$this->set( $key, $value, $exptime ) ) {
+ $res = false;
+ }
}
- return false; // key already set
+ return $res;
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
- * @return bool success
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
+ * @return bool Success
*/
- public function replace( $key, $value, $exptime = 0 ) {
- if ( $this->get( $key ) !== false ) {
+ public function add( $key, $value, $exptime = 0 ) {
+ if ( $this->get( $key ) === false ) {
return $this->set( $key, $value, $exptime );
}
- return false; // key not already set
+ return false; // key already set
}
/**
* Increase stored value of $key by $value while preserving its TTL
* @param string $key Key to increase
- * @param $value Integer: Value to add to $key (Default 1)
- * @return integer|bool New value or false on failure
+ * @param int $value Value to add to $key (Default 1)
+ * @return int|bool New value or false on failure
*/
public function incr( $key, $value = 1 ) {
if ( !$this->lock( $key ) ) {
@@ -282,16 +301,59 @@ abstract class BagOStuff {
/**
* Decrease stored value of $key by $value while preserving its TTL
- * @param $key String
- * @param $value Integer
- * @return integer
+ * @param string $key
+ * @param int $value
+ * @return int
*/
public function decr( $key, $value = 1 ) {
return $this->incr( $key, - $value );
}
/**
- * @param $text string
+ * Increase stored value of $key by $value while preserving its TTL
+ *
+ * This will create the key with value $init and TTL $ttl if not present
+ *
+ * @param string $key
+ * @param int $ttl
+ * @param int $value
+ * @param int $init
+ * @return bool
+ * @since 1.24
+ */
+ public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+ return $this->incr( $key, $value ) ||
+ $this->add( $key, (int)$init, $ttl ) || $this->incr( $key, $value );
+ }
+
+ /**
+ * Get the "last error" registered; clearLastError() should be called manually
+ * @return int ERR_* constant for the "last error" registry
+ * @since 1.23
+ */
+ public function getLastError() {
+ return $this->lastError;
+ }
+
+ /**
+ * Clear the "last error" registry
+ * @since 1.23
+ */
+ public function clearLastError() {
+ $this->lastError = self::ERR_NONE;
+ }
+
+ /**
+ * Set the "last error" registry
+ * @param int $err ERR_* constant
+ * @since 1.23
+ */
+ protected function setLastError( $err ) {
+ $this->lastError = $err;
+ }
+
+ /**
+ * @param string $text
*/
public function debug( $text ) {
if ( $this->debugMode ) {
@@ -302,7 +364,7 @@ abstract class BagOStuff {
/**
* Convert an optionally relative time to an absolute time
- * @param $exptime integer
+ * @param int $exptime
* @return int
*/
protected function convertExpiry( $exptime ) {
@@ -317,8 +379,8 @@ abstract class BagOStuff {
* Convert an optionally absolute expiry time to a relative time. If an
* absolute time is specified which is in the past, use a short expiry time.
*
- * @param $exptime integer
- * @return integer
+ * @param int $exptime
+ * @return int
*/
protected function convertToRelative( $exptime ) {
if ( $exptime >= 86400 * 3650 /* 10 years */ ) {
@@ -335,7 +397,7 @@ abstract class BagOStuff {
/**
* Check if a value is an integer
*
- * @param $value mixed
+ * @param mixed $value
* @return bool
*/
protected function isInteger( $value ) {
diff --git a/includes/objectcache/DBABagOStuff.php b/includes/objectcache/DBABagOStuff.php
deleted file mode 100644
index c82b3aa4..00000000
--- a/includes/objectcache/DBABagOStuff.php
+++ /dev/null
@@ -1,307 +0,0 @@
-<?php
-/**
- * Object caching using DBA backend.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Cache that uses DBA as a backend.
- * Slow due to the need to constantly open and close the file to avoid holding
- * writer locks. Intended for development use only, as a memcached workalike
- * for systems that don't have it.
- *
- * On construction you can pass array( 'dir' => '/some/path' ); as a parameter
- * to override the default DBA files directory (wfTempDir()).
- *
- * @ingroup Cache
- */
-class DBABagOStuff extends BagOStuff {
- var $mHandler, $mFile, $mReader, $mWriter, $mDisabled;
-
- /**
- * @param $params array
- */
- public function __construct( $params ) {
- global $wgDBAhandler;
-
- if ( !isset( $params['dir'] ) ) {
- $params['dir'] = wfTempDir();
- }
-
- $this->mFile = $params['dir'] . '/mw-cache-' . wfWikiID() . '.db';
- wfDebug( __CLASS__ . ": using cache file {$this->mFile}\n" );
- $this->mHandler = $wgDBAhandler;
- }
-
- /**
- * Encode value and expiry for storage
- * @param $value
- * @param $expiry
- *
- * @return string
- */
- protected function encode( $value, $expiry ) {
- # Convert to absolute time
- $expiry = $this->convertExpiry( $expiry );
-
- return sprintf( '%010u', intval( $expiry ) ) . ' ' . serialize( $value );
- }
-
- /**
- * @param $blob string
- * @return array list containing value first and expiry second
- */
- protected function decode( $blob ) {
- if ( !is_string( $blob ) ) {
- return array( false, 0 );
- } else {
- return array(
- unserialize( substr( $blob, 11 ) ),
- intval( substr( $blob, 0, 10 ) )
- );
- }
- }
-
- /**
- * @return resource
- */
- protected function getReader() {
- if ( file_exists( $this->mFile ) ) {
- $handle = dba_open( $this->mFile, 'rl', $this->mHandler );
- } else {
- $handle = $this->getWriter();
- }
-
- if ( !$handle ) {
- wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
- }
-
- return $handle;
- }
-
- /**
- * @return resource
- */
- protected function getWriter() {
- $handle = dba_open( $this->mFile, 'cl', $this->mHandler );
-
- if ( !$handle ) {
- wfDebug( "Unable to open DBA cache file {$this->mFile}\n" );
- }
-
- return $handle;
- }
-
- /**
- * @param $key string
- * @param $casToken[optional] mixed
- * @return mixed
- */
- public function get( $key, &$casToken = null ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $handle = $this->getReader();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $val = dba_fetch( $key, $handle );
- list( $val, $expiry ) = $this->decode( $val );
-
- # Must close ASAP because locks are held
- dba_close( $handle );
-
- if ( $val !== false && $expiry && $expiry < time() ) {
- # Key is expired, delete it
- $handle = $this->getWriter();
- dba_delete( $key, $handle );
- dba_close( $handle );
- wfDebug( __METHOD__ . ": $key expired\n" );
- $val = false;
- }
-
- $casToken = $val;
-
- wfProfileOut( __METHOD__ );
-
- return $val;
- }
-
- /**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
- * @return bool
- */
- public function set( $key, $value, $exptime = 0 ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $blob = $this->encode( $value, $exptime );
-
- $handle = $this->getWriter();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = dba_replace( $key, $blob, $handle );
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- /**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
- * @return bool
- */
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $blob = $this->encode( $value, $exptime );
-
- $handle = $this->getWriter();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- // DBA is locked to any other write connection, so we can safely
- // compare the current & previous value before saving new value
- $val = dba_fetch( $key, $handle );
- list( $val, $exptime ) = $this->decode( $val );
- if ( $casToken !== $val ) {
- dba_close( $handle );
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = dba_replace( $key, $blob, $handle );
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- /**
- * @param $key string
- * @param $time int
- * @return bool
- */
- public function delete( $key, $time = 0 ) {
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__ . "($key)\n" );
-
- $handle = $this->getWriter();
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = !dba_exists( $key, $handle ) || dba_delete( $key, $handle );
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- /**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
- * @return bool
- */
- public function add( $key, $value, $exptime = 0 ) {
- wfProfileIn( __METHOD__ );
-
- $blob = $this->encode( $value, $exptime );
-
- $handle = $this->getWriter();
-
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $ret = dba_insert( $key, $blob, $handle );
-
- # Insert failed, check to see if it failed due to an expired key
- if ( !$ret ) {
- list( , $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
-
- if ( $expiry && $expiry < time() ) {
- # Yes expired, delete and try again
- dba_delete( $key, $handle );
- $ret = dba_insert( $key, $blob, $handle );
- # This time if it failed then it will be handled by the caller like any other race
- }
- }
-
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
- return $ret;
- }
-
- /**
- * @param $key string
- * @param $step integer
- * @return integer|bool
- */
- public function incr( $key, $step = 1 ) {
- wfProfileIn( __METHOD__ );
-
- $handle = $this->getWriter();
-
- if ( !$handle ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- list( $value, $expiry ) = $this->decode( dba_fetch( $key, $handle ) );
- if ( $value !== false ) {
- if ( $expiry && $expiry < time() ) {
- # Key is expired, delete it
- dba_delete( $key, $handle );
- wfDebug( __METHOD__ . ": $key expired\n" );
- $value = false;
- } else {
- $value += $step;
- $blob = $this->encode( $value, $expiry );
-
- $ret = dba_replace( $key, $blob, $handle );
- $value = $ret ? $value : false;
- }
- }
-
- dba_close( $handle );
-
- wfProfileOut( __METHOD__ );
-
- return ( $value === false ) ? false : (int)$value;
- }
-}
diff --git a/includes/objectcache/EhcacheBagOStuff.php b/includes/objectcache/EhcacheBagOStuff.php
deleted file mode 100644
index 960668f5..00000000
--- a/includes/objectcache/EhcacheBagOStuff.php
+++ /dev/null
@@ -1,324 +0,0 @@
-<?php
-/**
- * Object caching using the Ehcache RESTful web service.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- * @file
- * @ingroup Cache
- */
-
-/**
- * Client for the Ehcache RESTful web service - http://ehcache.org/documentation/cache_server.html
- * TODO: Simplify configuration and add to the installer.
- *
- * @ingroup Cache
- */
-class EhcacheBagOStuff extends BagOStuff {
- var $servers, $cacheName, $connectTimeout, $timeout, $curlOptions,
- $requestData, $requestDataPos;
-
- var $curls = array();
-
- /**
- * @param $params array
- * @throws MWException
- */
- function __construct( $params ) {
- if ( !defined( 'CURLOPT_TIMEOUT_MS' ) ) {
- throw new MWException( __CLASS__ . ' requires curl version 7.16.2 or later.' );
- }
- if ( !extension_loaded( 'zlib' ) ) {
- throw new MWException( __CLASS__ . ' requires the zlib extension' );
- }
- if ( !isset( $params['servers'] ) ) {
- throw new MWException( __METHOD__ . ': servers parameter is required' );
- }
- $this->servers = $params['servers'];
- $this->cacheName = isset( $params['cache'] ) ? $params['cache'] : 'mw';
- $this->connectTimeout = isset( $params['connectTimeout'] )
- ? $params['connectTimeout'] : 1;
- $this->timeout = isset( $params['timeout'] ) ? $params['timeout'] : 1;
- $this->curlOptions = array(
- CURLOPT_CONNECTTIMEOUT_MS => intval( $this->connectTimeout * 1000 ),
- CURLOPT_TIMEOUT_MS => intval( $this->timeout * 1000 ),
- CURLOPT_RETURNTRANSFER => 1,
- CURLOPT_CUSTOMREQUEST => 'GET',
- CURLOPT_POST => 0,
- CURLOPT_POSTFIELDS => '',
- CURLOPT_HTTPHEADER => array(),
- );
- }
-
- /**
- * @param $key string
- * @param $casToken[optional] mixed
- * @return bool|mixed
- */
- public function get( $key, &$casToken = null ) {
- wfProfileIn( __METHOD__ );
- $response = $this->doItemRequest( $key );
- if ( !$response || $response['http_code'] == 404 ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
- if ( $response['http_code'] >= 300 ) {
- wfDebug( __METHOD__ . ": GET failure, got HTTP {$response['http_code']}\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
- $body = $response['body'];
- $type = $response['content_type'];
- if ( $type == 'application/vnd.php.serialized+deflate' ) {
- $body = gzinflate( $body );
- if ( !$body ) {
- wfDebug( __METHOD__ . ": error inflating $key\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
- $data = unserialize( $body );
- } elseif ( $type == 'application/vnd.php.serialized' ) {
- $data = unserialize( $body );
- } else {
- wfDebug( __METHOD__ . ": unknown content type \"$type\"\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
-
- $casToken = $body;
-
- wfProfileOut( __METHOD__ );
- return $data;
- }
-
- /**
- * @param $key string
- * @param $value mixed
- * @param $expiry int
- * @return bool
- */
- public function set( $key, $value, $expiry = 0 ) {
- wfProfileIn( __METHOD__ );
- $expiry = $this->convertExpiry( $expiry );
- $ttl = $expiry ? $expiry - time() : 2147483647;
- $blob = serialize( $value );
- if ( strlen( $blob ) > 100 ) {
- $blob = gzdeflate( $blob );
- $contentType = 'application/vnd.php.serialized+deflate';
- } else {
- $contentType = 'application/vnd.php.serialized';
- }
-
- $code = $this->attemptPut( $key, $blob, $contentType, $ttl );
-
- if ( $code == 404 ) {
- // Maybe the cache does not exist yet, let's try creating it
- if ( !$this->createCache( $key ) ) {
- wfDebug( __METHOD__ . ": cache creation failed\n" );
- wfProfileOut( __METHOD__ );
- return false;
- }
- $code = $this->attemptPut( $key, $blob, $contentType, $ttl );
- }
-
- $result = false;
- if ( !$code ) {
- wfDebug( __METHOD__ . ": PUT failure for key $key\n" );
- } elseif ( $code >= 300 ) {
- wfDebug( __METHOD__ . ": PUT failure for key $key: HTTP $code\n" );
- } else {
- $result = true;
- }
-
- wfProfileOut( __METHOD__ );
- return $result;
- }
-
- /**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
- * @return bool
- */
- public function cas( $casToken, $key, $value, $exptime = 0 ) {
- // Not sure if we can implement CAS for ehcache. There appears to be CAS-support per
- // http://ehcache.org/documentation/get-started/consistency-options#cas-cache-operations,
- // but I can't find any docs for our current implementation.
- throw new MWException( "CAS is not implemented in " . __CLASS__ );
- }
-
- /**
- * @param $key string
- * @param $time int
- * @return bool
- */
- public function delete( $key, $time = 0 ) {
- wfProfileIn( __METHOD__ );
- $response = $this->doItemRequest( $key,
- array( CURLOPT_CUSTOMREQUEST => 'DELETE' ) );
- $code = isset( $response['http_code'] ) ? $response['http_code'] : 0;
- if ( !$response || ( $code != 404 && $code >= 300 ) ) {
- wfDebug( __METHOD__ . ": DELETE failure for key $key\n" );
- $result = false;
- } else {
- $result = true;
- }
- wfProfileOut( __METHOD__ );
- return $result;
- }
-
- /**
- * @see BagOStuff::merge()
- * @return bool success
- */
- public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
- return $this->mergeViaLock( $key, $callback, $exptime, $attempts );
- }
-
- /**
- * @param $key string
- * @return string
- */
- protected function getCacheUrl( $key ) {
- if ( count( $this->servers ) == 1 ) {
- $server = reset( $this->servers );
- } else {
- // Use consistent hashing
- $hashes = array();
- foreach ( $this->servers as $server ) {
- $hashes[$server] = md5( $server . '/' . $key );
- }
- asort( $hashes );
- reset( $hashes );
- $server = key( $hashes );
- }
- return "http://$server/ehcache/rest/{$this->cacheName}";
- }
-
- /**
- * Get a cURL handle for the given cache URL.
- * We cache the handles to allow keepalive.
- */
- protected function getCurl( $cacheUrl ) {
- if ( !isset( $this->curls[$cacheUrl] ) ) {
- $this->curls[$cacheUrl] = curl_init();
- }
- return $this->curls[$cacheUrl];
- }
-
- /**
- * @param $key string
- * @param $data
- * @param $type
- * @param $ttl
- * @return int
- */
- protected function attemptPut( $key, $data, $type, $ttl ) {
- // In initial benchmarking, it was 30 times faster to use CURLOPT_POST
- // than CURLOPT_UPLOAD with CURLOPT_READFUNCTION. This was because
- // CURLOPT_UPLOAD was pushing the request headers first, then waiting
- // for an ACK packet, then sending the data, whereas CURLOPT_POST just
- // sends the headers and the data in a single send().
- $response = $this->doItemRequest( $key,
- array(
- CURLOPT_POST => 1,
- CURLOPT_CUSTOMREQUEST => 'PUT',
- CURLOPT_POSTFIELDS => $data,
- CURLOPT_HTTPHEADER => array(
- 'Content-Type: ' . $type,
- 'ehcacheTimeToLiveSeconds: ' . $ttl
- )
- )
- );
- if ( !$response ) {
- return 0;
- } else {
- return $response['http_code'];
- }
- }
-
- /**
- * @param $key string
- * @return bool
- */
- protected function createCache( $key ) {
- wfDebug( __METHOD__ . ": creating cache for $key\n" );
- $response = $this->doCacheRequest( $key,
- array(
- CURLOPT_POST => 1,
- CURLOPT_CUSTOMREQUEST => 'PUT',
- CURLOPT_POSTFIELDS => '',
- ) );
- if ( !$response ) {
- wfDebug( __CLASS__ . ": failed to create cache for $key\n" );
- return false;
- }
- return ( $response['http_code'] == 201 /* created */
- || $response['http_code'] == 409 /* already there */ );
- }
-
- /**
- * @param $key string
- * @param $curlOptions array
- * @return array|bool|mixed
- */
- protected function doCacheRequest( $key, $curlOptions = array() ) {
- $cacheUrl = $this->getCacheUrl( $key );
- $curl = $this->getCurl( $cacheUrl );
- return $this->doRequest( $curl, $cacheUrl, $curlOptions );
- }
-
- /**
- * @param $key string
- * @param $curlOptions array
- * @return array|bool|mixed
- */
- protected function doItemRequest( $key, $curlOptions = array() ) {
- $cacheUrl = $this->getCacheUrl( $key );
- $curl = $this->getCurl( $cacheUrl );
- $url = $cacheUrl . '/' . rawurlencode( $key );
- return $this->doRequest( $curl, $url, $curlOptions );
- }
-
- /**
- * @param $curl
- * @param $url string
- * @param $curlOptions array
- * @return array|bool|mixed
- * @throws MWException
- */
- protected function doRequest( $curl, $url, $curlOptions = array() ) {
- if ( array_diff_key( $curlOptions, $this->curlOptions ) ) {
- // var_dump( array_diff_key( $curlOptions, $this->curlOptions ) );
- throw new MWException( __METHOD__ . ": to prevent options set in one doRequest() " .
- "call from affecting subsequent doRequest() calls, only options listed " .
- "in \$this->curlOptions may be specified in the \$curlOptions parameter." );
- }
- $curlOptions += $this->curlOptions;
- $curlOptions[CURLOPT_URL] = $url;
-
- curl_setopt_array( $curl, $curlOptions );
- $result = curl_exec( $curl );
- if ( $result === false ) {
- wfDebug( __CLASS__ . ": curl error: " . curl_error( $curl ) . "\n" );
- return false;
- }
- $info = curl_getinfo( $curl );
- $info['body'] = $result;
- return $info;
- }
-}
diff --git a/includes/objectcache/EmptyBagOStuff.php b/includes/objectcache/EmptyBagOStuff.php
index 62060579..9595b83c 100644
--- a/includes/objectcache/EmptyBagOStuff.php
+++ b/includes/objectcache/EmptyBagOStuff.php
@@ -29,8 +29,8 @@
class EmptyBagOStuff extends BagOStuff {
/**
- * @param $key string
- * @param $casToken[optional] mixed
+ * @param string $key
+ * @param mixed $casToken [optional]
* @return bool
*/
function get( $key, &$casToken = null ) {
@@ -38,9 +38,9 @@ class EmptyBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exp int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exp
* @return bool
*/
function set( $key, $value, $exp = 0 ) {
@@ -48,10 +48,10 @@ class EmptyBagOStuff extends BagOStuff {
}
/**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exp int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exp
* @return bool
*/
function cas( $casToken, $key, $value, $exp = 0 ) {
@@ -59,8 +59,8 @@ class EmptyBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
function delete( $key, $time = 0 ) {
@@ -68,20 +68,13 @@ class EmptyBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
return true;
}
}
-
-/**
- * Backwards compatibility alias for EmptyBagOStuff
- * @deprecated since 1.18
- */
-class FakeMemCachedClient extends EmptyBagOStuff {
-}
diff --git a/includes/objectcache/HashBagOStuff.php b/includes/objectcache/HashBagOStuff.php
index d061eff0..6e50a8c3 100644
--- a/includes/objectcache/HashBagOStuff.php
+++ b/includes/objectcache/HashBagOStuff.php
@@ -28,14 +28,15 @@
* @ingroup Cache
*/
class HashBagOStuff extends BagOStuff {
- var $bag;
+ /** @var array */
+ protected $bag;
function __construct() {
$this->bag = array();
}
/**
- * @param $key string
+ * @param string $key
* @return bool
*/
protected function expire( $key ) {
@@ -51,8 +52,8 @@ class HashBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $casToken[optional] mixed
+ * @param string $key
+ * @param mixed $casToken [optional]
* @return bool|mixed
*/
function get( $key, &$casToken = null ) {
@@ -64,15 +65,15 @@ class HashBagOStuff extends BagOStuff {
return false;
}
- $casToken = $this->bag[$key][0];
+ $casToken = serialize( $this->bag[$key][0] );
return $this->bag[$key][0];
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
function set( $key, $value, $exptime = 0 ) {
@@ -81,14 +82,14 @@ class HashBagOStuff extends BagOStuff {
}
/**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
function cas( $casToken, $key, $value, $exptime = 0 ) {
- if ( $this->get( $key ) === $casToken ) {
+ if ( serialize( $this->get( $key ) ) === $casToken ) {
return $this->set( $key, $value, $exptime );
}
@@ -96,8 +97,8 @@ class HashBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
function delete( $key, $time = 0 ) {
diff --git a/includes/objectcache/MemcachedBagOStuff.php b/includes/objectcache/MemcachedBagOStuff.php
index f1644edb..53edcdde 100644
--- a/includes/objectcache/MemcachedBagOStuff.php
+++ b/includes/objectcache/MemcachedBagOStuff.php
@@ -32,6 +32,8 @@ class MemcachedBagOStuff extends BagOStuff {
/**
* Fill in the defaults for any parameters missing from $params, using the
* backwards-compatible global variables
+ * @param array $params
+ * @return array
*/
protected function applyDefaultParams( $params ) {
if ( !isset( $params['servers'] ) ) {
@@ -56,18 +58,18 @@ class MemcachedBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $casToken[optional] mixed
- * @return Mixed
+ * @param string $key
+ * @param mixed $casToken [optional]
+ * @return mixed
*/
public function get( $key, &$casToken = null ) {
return $this->client->get( $this->encodeKey( $key ), $casToken );
}
/**
- * @param $key string
- * @param $value
- * @param $exptime int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function set( $key, $value, $exptime = 0 ) {
@@ -76,10 +78,10 @@ class MemcachedBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $casToken mixed
- * @param $value
- * @param $exptime int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -88,8 +90,8 @@ class MemcachedBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
public function delete( $key, $time = 0 ) {
@@ -97,10 +99,10 @@ class MemcachedBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value int
+ * @param string $key
+ * @param int $value
* @param int $exptime (default 0)
- * @return Mixed
+ * @return mixed
*/
public function add( $key, $value, $exptime = 0 ) {
return $this->client->add( $this->encodeKey( $key ), $value,
@@ -108,19 +110,9 @@ class MemcachedBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value int
- * @param $exptime
- * @return Mixed
- */
- public function replace( $key, $value, $exptime = 0 ) {
- return $this->client->replace( $this->encodeKey( $key ), $value,
- $this->fixExpiry( $exptime ) );
- }
-
- /**
* Get the underlying client object. This is provided for debugging
* purposes.
+ * @return BagOStuff
*/
public function getClient() {
return $this->client;
@@ -133,7 +125,7 @@ class MemcachedBagOStuff extends BagOStuff {
* the other control characters for compatibility with libmemcached
* verify_key. We leave other punctuation alone, to maximise backwards
* compatibility.
- * @param $key string
+ * @param string $key
* @return string
*/
public function encodeKey( $key ) {
@@ -142,7 +134,7 @@ class MemcachedBagOStuff extends BagOStuff {
}
/**
- * @param $m array
+ * @param array $m
* @return string
*/
protected function encodeKeyCallback( $m ) {
@@ -155,19 +147,21 @@ class MemcachedBagOStuff extends BagOStuff {
* discarded immediately because the expiry is in the past.
* Clamp expiries >30d at 30d, unless they're >=1e9 in which
* case they are likely to really be absolute (1e9 = 2011-09-09)
+ * @param int $expiry
+ * @return int
*/
function fixExpiry( $expiry ) {
if ( $expiry > 2592000 && $expiry < 1000000000 ) {
$expiry = 2592000;
}
- return $expiry;
+ return (int)$expiry;
}
/**
* Decode a key encoded with encodeKey(). This is provided as a convenience
* function for debugging.
*
- * @param $key string
+ * @param string $key
*
* @return string
*/
@@ -177,11 +171,9 @@ class MemcachedBagOStuff extends BagOStuff {
/**
* Send a debug message to the log
+ * @param string $text
*/
protected function debugLog( $text ) {
- if ( substr( $text, -1 ) !== "\n" ) {
- $text .= "\n";
- }
wfDebugLog( 'memcached', $text );
}
}
diff --git a/includes/objectcache/MemcachedClient.php b/includes/objectcache/MemcachedClient.php
index e5f60b55..41eebfb5 100644
--- a/includes/objectcache/MemcachedClient.php
+++ b/includes/objectcache/MemcachedClient.php
@@ -1,4 +1,5 @@
<?php
+// @codingStandardsIgnoreFile It's an external lib and it isn't. Let's not bother.
/**
* Memcached client for PHP.
*
@@ -102,10 +103,10 @@ class MWMemcached {
/**
* Command statistics
*
- * @var array
- * @access public
+ * @var array
+ * @access public
*/
- var $stats;
+ public $stats;
// }}}
// {{{ private
@@ -113,124 +114,124 @@ class MWMemcached {
/**
* Cached Sockets that are connected
*
- * @var array
- * @access private
+ * @var array
+ * @access private
*/
- var $_cache_sock;
+ public $_cache_sock;
/**
* Current debug status; 0 - none to 9 - profiling
*
- * @var boolean
- * @access private
+ * @var bool
+ * @access private
*/
- var $_debug;
+ public $_debug;
/**
* Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
*
- * @var array
- * @access private
+ * @var array
+ * @access private
*/
- var $_host_dead;
+ public $_host_dead;
/**
* Is compression available?
*
- * @var boolean
- * @access private
+ * @var bool
+ * @access private
*/
- var $_have_zlib;
+ public $_have_zlib;
/**
* Do we want to use compression?
*
- * @var boolean
- * @access private
+ * @var bool
+ * @access private
*/
- var $_compress_enable;
+ public $_compress_enable;
/**
* At how many bytes should we compress?
*
- * @var integer
- * @access private
+ * @var int
+ * @access private
*/
- var $_compress_threshold;
+ public $_compress_threshold;
/**
* Are we using persistent links?
*
- * @var boolean
- * @access private
+ * @var bool
+ * @access private
*/
- var $_persistent;
+ public $_persistent;
/**
* If only using one server; contains ip:port to connect to
*
- * @var string
- * @access private
+ * @var string
+ * @access private
*/
- var $_single_sock;
+ public $_single_sock;
/**
* Array containing ip:port or array(ip:port, weight)
*
- * @var array
- * @access private
+ * @var array
+ * @access private
*/
- var $_servers;
+ public $_servers;
/**
* Our bit buckets
*
- * @var array
- * @access private
+ * @var array
+ * @access private
*/
- var $_buckets;
+ public $_buckets;
/**
* Total # of bit buckets we have
*
- * @var integer
- * @access private
+ * @var int
+ * @access private
*/
- var $_bucketcount;
+ public $_bucketcount;
/**
* # of total servers we have
*
- * @var integer
- * @access private
+ * @var int
+ * @access private
*/
- var $_active;
+ public $_active;
/**
* Stream timeout in seconds. Applies for example to fread()
*
- * @var integer
- * @access private
+ * @var int
+ * @access private
*/
- var $_timeout_seconds;
+ public $_timeout_seconds;
/**
* Stream timeout in microseconds
*
- * @var integer
- * @access private
+ * @var int
+ * @access private
*/
- var $_timeout_microseconds;
+ public $_timeout_microseconds;
/**
* Connect timeout in seconds
*/
- var $_connect_timeout;
+ public $_connect_timeout;
/**
* Number of connection attempts for each server
*/
- var $_connect_attempts;
+ public $_connect_attempts;
// }}}
// }}}
@@ -243,7 +244,7 @@ class MWMemcached {
*
* @param array $args Associative array of settings
*
- * @return mixed
+ * @return mixed
*/
public function __construct( $args ) {
$this->set_servers( isset( $args['servers'] ) ? $args['servers'] : array() );
@@ -271,15 +272,15 @@ class MWMemcached {
* Adds a key/value to the memcache server if one isn't already set with
* that key
*
- * @param string $key key to set with data
- * @param $val Mixed: value to store
- * @param $exp Integer: (optional) Expiration time. This can be a number of seconds
+ * @param string $key Key to set with data
+ * @param mixed $val Value to store
+ * @param int $exp (optional) Expiration time. This can be a number of seconds
* to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or
* longer must be the timestamp of the time at which the mapping should expire. It
* is safe to use timestamps in all cases, regardless of expiration
* eg: strtotime("+3 hour")
*
- * @return Boolean
+ * @return bool
*/
public function add( $key, $val, $exp = 0 ) {
return $this->_set( 'add', $key, $val, $exp );
@@ -291,10 +292,10 @@ class MWMemcached {
/**
* Decrease a value stored on the memcache server
*
- * @param string $key key to decrease
- * @param $amt Integer: (optional) amount to decrease
+ * @param string $key Key to decrease
+ * @param int $amt (optional) amount to decrease
*
- * @return Mixed: FALSE on failure, value on success
+ * @return mixed False on failure, value on success
*/
public function decr( $key, $amt = 1 ) {
return $this->_incrdecr( 'decr', $key, $amt );
@@ -306,10 +307,10 @@ class MWMemcached {
/**
* Deletes a key from the server, optionally after $time
*
- * @param string $key key to delete
- * @param $time Integer: (optional) how long to wait before deleting
+ * @param string $key Key to delete
+ * @param int $time (optional) how long to wait before deleting
*
- * @return Boolean: TRUE on success, FALSE on failure
+ * @return bool True on success, false on failure
*/
public function delete( $key, $time = 0 ) {
if ( !$this->_active ) {
@@ -346,8 +347,8 @@ class MWMemcached {
}
/**
- * @param $key
- * @param $timeout int
+ * @param string $key
+ * @param int $timeout
* @return bool
*/
public function lock( $key, $timeout = 0 ) {
@@ -356,7 +357,7 @@ class MWMemcached {
}
/**
- * @param $key
+ * @param string $key
* @return bool
*/
public function unlock( $key ) {
@@ -384,7 +385,7 @@ class MWMemcached {
/**
* Enable / Disable compression
*
- * @param $enable Boolean: TRUE to enable, FALSE to disable
+ * @param bool $enable True to enable, false to disable
*/
public function enable_compress( $enable ) {
$this->_compress_enable = $enable;
@@ -407,9 +408,9 @@ class MWMemcached {
* Retrieves the value associated with the key from the memcache server
*
* @param array|string $key key to retrieve
- * @param $casToken[optional] Float
+ * @param float $casToken [optional]
*
- * @return Mixed
+ * @return mixed
*/
public function get( $key, &$casToken = null ) {
wfProfileIn( __METHOD__ );
@@ -418,6 +419,12 @@ class MWMemcached {
$this->_debugprint( "get($key)\n" );
}
+ if ( !is_array( $key ) && strval( $key ) === '' ) {
+ $this->_debugprint( "Skipping key which equals to an empty string" );
+ wfProfileOut( __METHOD__ );
+ return false;
+ }
+
if ( !$this->_active ) {
wfProfileOut( __METHOD__ );
return false;
@@ -466,9 +473,9 @@ class MWMemcached {
/**
* Get multiple keys from the server(s)
*
- * @param array $keys keys to retrieve
+ * @param array $keys Keys to retrieve
*
- * @return Array
+ * @return array
*/
public function get_multi( $keys ) {
if ( !$this->_active ) {
@@ -530,10 +537,10 @@ class MWMemcached {
/**
* Increments $key (optionally) by $amt
*
- * @param string $key key to increment
- * @param $amt Integer: (optional) amount to increment
+ * @param string $key Key to increment
+ * @param int $amt (optional) amount to increment
*
- * @return Integer: null if the key does not exist yet (this does NOT
+ * @return int|null Null if the key does not exist yet (this does NOT
* create new mappings if the key does not exist). If the key does
* exist, this returns the new value for that key.
*/
@@ -547,15 +554,15 @@ class MWMemcached {
/**
* Overwrites an existing value for key; only works if key is already set
*
- * @param string $key key to set value as
- * @param $value Mixed: value to store
- * @param $exp Integer: (optional) Expiration time. This can be a number of seconds
+ * @param string $key Key to set value as
+ * @param mixed $value Value to store
+ * @param int $exp (optional) Expiration time. This can be a number of seconds
* to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or
* longer must be the timestamp of the time at which the mapping should expire. It
* is safe to use timestamps in all cases, regardless of exipration
* eg: strtotime("+3 hour")
*
- * @return Boolean
+ * @return bool
*/
public function replace( $key, $value, $exp = 0 ) {
return $this->_set( 'replace', $key, $value, $exp );
@@ -568,10 +575,10 @@ class MWMemcached {
* Passes through $cmd to the memcache server connected by $sock; returns
* output as an array (null array if no output)
*
- * @param $sock Resource: socket to send command on
- * @param string $cmd command to run
+ * @param Resource $sock Socket to send command on
+ * @param string $cmd Command to run
*
- * @return Array: output array
+ * @return array Output array
*/
public function run_command( $sock, $cmd ) {
if ( !is_resource( $sock ) ) {
@@ -603,15 +610,15 @@ class MWMemcached {
* Unconditionally sets a key to a given value in the memcache. Returns true
* if set successfully.
*
- * @param string $key key to set value as
- * @param $value Mixed: value to set
- * @param $exp Integer: (optional) Expiration time. This can be a number of seconds
+ * @param string $key Key to set value as
+ * @param mixed $value Value to set
+ * @param int $exp (optional) Expiration time. This can be a number of seconds
* to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or
* longer must be the timestamp of the time at which the mapping should expire. It
* is safe to use timestamps in all cases, regardless of exipration
* eg: strtotime("+3 hour")
*
- * @return Boolean: TRUE on success
+ * @return bool True on success
*/
public function set( $key, $value, $exp = 0 ) {
return $this->_set( 'set', $key, $value, $exp );
@@ -624,16 +631,16 @@ class MWMemcached {
* Sets a key to a given value in the memcache if the current value still corresponds
* to a known, given value. Returns true if set successfully.
*
- * @param $casToken Float: current known value
- * @param string $key key to set value as
- * @param $value Mixed: value to set
- * @param $exp Integer: (optional) Expiration time. This can be a number of seconds
+ * @param float $casToken Current known value
+ * @param string $key Key to set value as
+ * @param mixed $value Value to set
+ * @param int $exp (optional) Expiration time. This can be a number of seconds
* to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or
* longer must be the timestamp of the time at which the mapping should expire. It
* is safe to use timestamps in all cases, regardless of exipration
* eg: strtotime("+3 hour")
*
- * @return Boolean: TRUE on success
+ * @return bool True on success
*/
public function cas( $casToken, $key, $value, $exp = 0 ) {
return $this->_set( 'cas', $key, $value, $exp, $casToken );
@@ -645,7 +652,7 @@ class MWMemcached {
/**
* Sets the compression threshold
*
- * @param $thresh Integer: threshold to compress if larger than
+ * @param int $thresh Threshold to compress if larger than
*/
public function set_compress_threshold( $thresh ) {
$this->_compress_threshold = $thresh;
@@ -657,9 +664,9 @@ class MWMemcached {
/**
* Sets the debug flag
*
- * @param $dbg Boolean: TRUE for debugging, FALSE otherwise
+ * @param bool $dbg True for debugging, false otherwise
*
- * @see MWMemcached::__construct
+ * @see MWMemcached::__construct
*/
public function set_debug( $dbg ) {
$this->_debug = $dbg;
@@ -671,9 +678,9 @@ class MWMemcached {
/**
* Sets the server list to distribute key gets and puts between
*
- * @param array $list of servers to connect to
+ * @param array $list Array of servers to connect to
*
- * @see MWMemcached::__construct()
+ * @see MWMemcached::__construct()
*/
public function set_servers( $list ) {
$this->_servers = $list;
@@ -690,8 +697,8 @@ class MWMemcached {
/**
* Sets the timeout for new connections
*
- * @param $seconds Integer: number of seconds
- * @param $microseconds Integer: number of microseconds
+ * @param int $seconds Number of seconds
+ * @param int $microseconds Number of microseconds
*/
public function set_timeout( $seconds, $microseconds ) {
$this->_timeout_seconds = $seconds;
@@ -706,9 +713,9 @@ class MWMemcached {
/**
* Close the specified socket
*
- * @param string $sock socket to close
+ * @param string $sock Socket to close
*
- * @access private
+ * @access private
*/
function _close_sock( $sock ) {
$host = array_search( $sock, $this->_cache_sock );
@@ -722,14 +729,14 @@ class MWMemcached {
/**
* Connects $sock to $host, timing out after $timeout
*
- * @param $sock Integer: socket to connect
+ * @param int $sock Socket to connect
* @param string $host Host:IP to connect to
*
- * @return boolean
- * @access private
+ * @return bool
+ * @access private
*/
function _connect_sock( &$sock, $host ) {
- list( $ip, $port ) = explode( ':', $host );
+ list( $ip, $port ) = preg_split( '/:(?=\d)/', $host );
$sock = false;
$timeout = $this->_connect_timeout;
$errno = $errstr = null;
@@ -765,9 +772,9 @@ class MWMemcached {
/**
* Marks a host as dead until 30-40 seconds in the future
*
- * @param string $sock socket to mark as dead
+ * @param string $sock Socket to mark as dead
*
- * @access private
+ * @access private
*/
function _dead_sock( $sock ) {
$host = array_search( $sock, $this->_cache_sock );
@@ -775,7 +782,7 @@ class MWMemcached {
}
/**
- * @param $host
+ * @param string $host
*/
function _dead_host( $host ) {
$parts = explode( ':', $host );
@@ -791,9 +798,9 @@ class MWMemcached {
/**
* get_sock
*
- * @param string $key key to retrieve value for;
+ * @param string $key Key to retrieve value for;
*
- * @return Mixed: resource on success, false on failure
+ * @return Resource|bool Resource on success, false on failure
* @access private
*/
function get_sock( $key ) {
@@ -840,9 +847,9 @@ class MWMemcached {
/**
* Creates a hash integer based on the $key
*
- * @param string $key key to hash
+ * @param string $key Key to hash
*
- * @return Integer: hash value
+ * @return int Hash value
* @access private
*/
function _hashfunc( $key ) {
@@ -858,11 +865,11 @@ class MWMemcached {
/**
* Perform increment/decriment on $key
*
- * @param string $cmd command to perform
- * @param string|array $key key to perform it on
- * @param $amt Integer amount to adjust
+ * @param string $cmd Command to perform
+ * @param string|array $key Key to perform it on
+ * @param int $amt Amount to adjust
*
- * @return Integer: new value of $key
+ * @return int New value of $key
* @access private
*/
function _incrdecr( $cmd, $key, $amt = 1 ) {
@@ -899,10 +906,10 @@ class MWMemcached {
/**
* Load items into $ret from $sock
*
- * @param $sock Resource: socket to read from
+ * @param Resource $sock Socket to read from
* @param array $ret returned values
- * @param $casToken[optional] Float
- * @return boolean True for success, false for failure
+ * @param float $casToken [optional]
+ * @return bool True for success, false for failure
*
* @access private
*/
@@ -985,17 +992,17 @@ class MWMemcached {
/**
* Performs the requested storage operation to the memcache server
*
- * @param string $cmd command to perform
- * @param string $key key to act on
- * @param $val Mixed: what we need to store
- * @param $exp Integer: (optional) Expiration time. This can be a number of seconds
+ * @param string $cmd Command to perform
+ * @param string $key Key to act on
+ * @param mixed $val What we need to store
+ * @param int $exp (optional) Expiration time. This can be a number of seconds
* to cache for (up to 30 days inclusive). Any timespans of 30 days + 1 second or
* longer must be the timestamp of the time at which the mapping should expire. It
* is safe to use timestamps in all cases, regardless of exipration
* eg: strtotime("+3 hour")
- * @param $casToken[optional] Float
+ * @param float $casToken [optional]
*
- * @return Boolean
+ * @return bool
* @access private
*/
function _set( $cmd, $key, $val, $exp, $casToken = null ) {
@@ -1026,9 +1033,9 @@ class MWMemcached {
$len = strlen( $val );
- if ( $this->_have_zlib && $this->_compress_enable &&
- $this->_compress_threshold && $len >= $this->_compress_threshold )
- {
+ if ( $this->_have_zlib && $this->_compress_enable
+ && $this->_compress_threshold && $len >= $this->_compress_threshold
+ ) {
$c_val = gzcompress( $val, 9 );
$c_len = strlen( $c_val );
@@ -1070,7 +1077,7 @@ class MWMemcached {
*
* @param string $host Host:IP to get socket for
*
- * @return Mixed: IO Stream or false
+ * @return Resource|bool IO Stream or false
* @access private
*/
function sock_to_host( $host ) {
@@ -1100,14 +1107,14 @@ class MWMemcached {
}
/**
- * @param $text string
+ * @param string $text
*/
function _debugprint( $text ) {
wfDebugLog( 'memcached', $text );
}
/**
- * @param $text string
+ * @param string $text
*/
function _error_log( $text ) {
wfDebugLog( 'memcached-serious', "Memcached error: $text" );
@@ -1116,8 +1123,8 @@ class MWMemcached {
/**
* Write to a stream. If there is an error, mark the socket dead.
*
- * @param $sock The socket
- * @param $buf The string to write
+ * @param Resource $sock The socket
+ * @param string $buf The string to write
* @return bool True on success, false on failure
*/
function _fwrite( $sock, $buf ) {
@@ -1143,6 +1150,9 @@ class MWMemcached {
/**
* Handle an I/O error. Mark the socket dead and log an error.
+ *
+ * @param Resource $sock
+ * @param string $msg
*/
function _handle_error( $sock, $msg ) {
$peer = stream_socket_get_name( $sock, true /** remote **/ );
@@ -1161,9 +1171,9 @@ class MWMemcached {
* Read the specified number of bytes from a stream. If there is an error,
* mark the socket dead.
*
- * @param $sock The socket
- * @param $len The number of bytes to read
- * @return The string on success, false on failure.
+ * @param Resource $sock The socket
+ * @param int $len The number of bytes to read
+ * @return string|bool The string on success, false on failure.
*/
function _fread( $sock, $len ) {
$buf = '';
@@ -1193,8 +1203,8 @@ class MWMemcached {
* Read a line from a stream. If there is an error, mark the socket dead.
* The \r\n line ending is stripped from the response.
*
- * @param $sock The socket
- * @return The string on success, false on failure
+ * @param Resource $sock The socket
+ * @return string|bool The string on success, false on failure
*/
function _fgets( $sock ) {
$result = fgets( $sock );
@@ -1223,7 +1233,7 @@ class MWMemcached {
/**
* Flush the read buffer of a stream
- * @param $f Resource
+ * @param Resource $f
*/
function _flush_read_buffer( $f ) {
if ( !is_resource( $f ) ) {
diff --git a/includes/objectcache/MemcachedPeclBagOStuff.php b/includes/objectcache/MemcachedPeclBagOStuff.php
index 0c3b228f..c853bcf4 100644
--- a/includes/objectcache/MemcachedPeclBagOStuff.php
+++ b/includes/objectcache/MemcachedPeclBagOStuff.php
@@ -42,6 +42,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
* - serializer: May be either "php" or "igbinary". Igbinary produces more compact
* values, but serialization is much slower unless the php.ini option
* igbinary.compact_strings is off.
+ * @param array $params
*/
function __construct( $params ) {
$params = $this->applyDefaultParams( $params );
@@ -113,9 +114,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @param $casToken[optional] float
- * @return Mixed
+ * @param string $key
+ * @param float $casToken [optional]
+ * @return mixed
*/
public function get( $key, &$casToken = null ) {
wfProfileIn( __METHOD__ );
@@ -127,9 +128,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @param $value
- * @param $exptime int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function set( $key, $value, $exptime = 0 ) {
@@ -138,10 +139,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $casToken float
- * @param $key string
- * @param $value
- * @param $exptime int
+ * @param float $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -150,8 +151,8 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
public function delete( $key, $time = 0 ) {
@@ -166,10 +167,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @param $value int
- * @param $exptime int
- * @return Mixed
+ * @param string $key
+ * @param int $value
+ * @param int $exptime
+ * @return mixed
*/
public function add( $key, $value, $exptime = 0 ) {
$this->debugLog( "add($key)" );
@@ -177,20 +178,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @param $value int
- * @param $exptime
- * @return Mixed
- */
- public function replace( $key, $value, $exptime = 0 ) {
- $this->debugLog( "replace($key)" );
- return $this->checkResult( $key, parent::replace( $key, $value, $exptime ) );
- }
-
- /**
- * @param $key string
- * @param $value int
- * @return Mixed
+ * @param string $key
+ * @param int $value
+ * @return mixed
*/
public function incr( $key, $value = 1 ) {
$this->debugLog( "incr($key)" );
@@ -199,9 +189,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @param $value int
- * @return Mixed
+ * @param string $key
+ * @param int $value
+ * @return mixed
*/
public function decr( $key, $value = 1 ) {
$this->debugLog( "decr($key)" );
@@ -217,8 +207,8 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
* different.
*
* @param string $key The key used by the caller, or false if there wasn't one.
- * @param $result Mixed The return value
- * @return Mixed
+ * @param mixed $result The return value
+ * @return mixed
*/
protected function checkResult( $key, $result ) {
if ( $result !== false ) {
@@ -242,13 +232,14 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
$msg = "Memcached error: $msg";
}
wfDebugLog( 'memcached-serious', $msg );
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
}
return $result;
}
/**
- * @param $keys Array
- * @return Array
+ * @param array $keys
+ * @return array
*/
public function getMulti( array $keys ) {
wfProfileIn( __METHOD__ );
@@ -256,11 +247,27 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
$callback = array( $this, 'encodeKey' );
$result = $this->client->getMulti( array_map( $callback, $keys ) );
wfProfileOut( __METHOD__ );
+ $result = $result ?: array(); // must be an array
return $this->checkResult( false, $result );
}
- /* NOTE: there is no cas() method here because it is currently not supported
- * by the BagOStuff interface and other BagOStuff subclasses, such as
- * SqlBagOStuff.
+ /**
+ * @param array $data
+ * @param int $exptime
+ * @return bool
*/
+ public function setMulti( array $data, $exptime = 0 ) {
+ wfProfileIn( __METHOD__ );
+ foreach ( $data as $key => $value ) {
+ $encKey = $this->encodeKey( $key );
+ if ( $encKey !== $key ) {
+ $data[$encKey] = $value;
+ unset( $data[$key] );
+ }
+ }
+ $this->debugLog( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' );
+ $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
+ wfProfileOut( __METHOD__ );
+ return $this->checkResult( false, $result );
+ }
}
diff --git a/includes/objectcache/MemcachedPhpBagOStuff.php b/includes/objectcache/MemcachedPhpBagOStuff.php
index 33a134c7..330d2b52 100644
--- a/includes/objectcache/MemcachedPhpBagOStuff.php
+++ b/includes/objectcache/MemcachedPhpBagOStuff.php
@@ -39,7 +39,7 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
* - timeout: The read timeout in microseconds
* - connect_timeout: The connect timeout in seconds
*
- * @param $params array
+ * @param array $params
*/
function __construct( $params ) {
$params = $this->applyDefaultParams( $params );
@@ -50,15 +50,15 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $debug bool
+ * @param bool $debug
*/
public function setDebug( $debug ) {
$this->client->set_debug( $debug );
}
/**
- * @param $keys Array
- * @return Array
+ * @param array $keys
+ * @return array
*/
public function getMulti( array $keys ) {
$callback = array( $this, 'encodeKey' );
@@ -66,8 +66,8 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key
- * @param $timeout int
+ * @param string $key
+ * @param int $timeout
* @return bool
*/
public function lock( $key, $timeout = 0 ) {
@@ -75,26 +75,26 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
}
/**
- * @param $key string
- * @return Mixed
+ * @param string $key
+ * @return mixed
*/
public function unlock( $key ) {
return $this->client->unlock( $this->encodeKey( $key ) );
}
/**
- * @param $key string
- * @param $value int
- * @return Mixed
+ * @param string $key
+ * @param int $value
+ * @return mixed
*/
public function incr( $key, $value = 1 ) {
return $this->client->incr( $this->encodeKey( $key ), $value );
}
/**
- * @param $key string
- * @param $value int
- * @return Mixed
+ * @param string $key
+ * @param int $value
+ * @return mixed
*/
public function decr( $key, $value = 1 ) {
return $this->client->decr( $this->encodeKey( $key ), $value );
diff --git a/includes/objectcache/MultiWriteBagOStuff.php b/includes/objectcache/MultiWriteBagOStuff.php
index e550c0d0..6a691379 100644
--- a/includes/objectcache/MultiWriteBagOStuff.php
+++ b/includes/objectcache/MultiWriteBagOStuff.php
@@ -29,7 +29,8 @@
* @ingroup Cache
*/
class MultiWriteBagOStuff extends BagOStuff {
- var $caches;
+ /** @var array BagOStuff[] */
+ protected $caches;
/**
* Constructor. Parameters are:
@@ -38,7 +39,7 @@ class MultiWriteBagOStuff extends BagOStuff {
* structures, in the style required by $wgObjectCaches. See
* the documentation of $wgObjectCaches for more detail.
*
- * @param $params array
+ * @param array $params
* @throws MWException
*/
public function __construct( $params ) {
@@ -53,15 +54,15 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $debug bool
+ * @param bool $debug
*/
public function setDebug( $debug ) {
$this->doWrite( 'setDebug', $debug );
}
/**
- * @param $key string
- * @param $casToken[optional] mixed
+ * @param string $key
+ * @param mixed $casToken [optional]
* @return bool|mixed
*/
public function get( $key, &$casToken = null ) {
@@ -75,10 +76,10 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param mixed $exptime
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -86,9 +87,9 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function set( $key, $value, $exptime = 0 ) {
@@ -96,8 +97,8 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
public function delete( $key, $time = 0 ) {
@@ -105,9 +106,9 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function add( $key, $value, $exptime = 0 ) {
@@ -115,18 +116,8 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
- * @return bool
- */
- public function replace( $key, $value, $exptime = 0 ) {
- return $this->doWrite( 'replace', $key, $value, $exptime );
- }
-
- /**
- * @param $key string
- * @param $value int
+ * @param string $key
+ * @param int $value
* @return bool|null
*/
public function incr( $key, $value = 1 ) {
@@ -134,8 +125,8 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value int
+ * @param string $key
+ * @param int $value
* @return bool
*/
public function decr( $key, $value = 1 ) {
@@ -143,8 +134,8 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $timeout int
+ * @param string $key
+ * @param int $timeout
* @return bool
*/
public function lock( $key, $timeout = 0 ) {
@@ -157,7 +148,7 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
+ * @param string $key
* @return bool
*/
public function unlock( $key ) {
@@ -169,18 +160,28 @@ class MultiWriteBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
return $this->doWrite( 'merge', $key, $callback, $exptime );
}
+ public function getLastError() {
+ return isset( $this->caches[0] ) ? $this->caches[0]->getLastError() : self::ERR_NONE;
+ }
+
+ public function clearLastError() {
+ if ( isset( $this->caches[0] ) ) {
+ $this->caches[0]->clearLastError();
+ }
+ }
+
/**
- * @param $method string
+ * @param string $method
* @return bool
*/
protected function doWrite( $method /*, ... */ ) {
@@ -200,8 +201,8 @@ class MultiWriteBagOStuff extends BagOStuff {
* Delete objects expiring before a certain date.
*
* Succeed if any of the child caches succeed.
- * @param $date string
- * @param $progressCallback bool|callback
+ * @param string $date
+ * @param bool|callable $progressCallback
* @return bool
*/
public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) {
diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php
index 6c1433a9..633b34a2 100644
--- a/includes/objectcache/ObjectCache.php
+++ b/includes/objectcache/ObjectCache.php
@@ -27,12 +27,12 @@
* @ingroup Cache
*/
class ObjectCache {
- static $instances = array();
+ public static $instances = array();
/**
* Get a cached instance of the specified type of cache object.
*
- * @param $id string
+ * @param string $id
*
* @return BagOStuff
*/
@@ -56,7 +56,7 @@ class ObjectCache {
/**
* Create a new cache object of the specified type.
*
- * @param $id string
+ * @param string $id
*
* @throws MWException
* @return BagOStuff
@@ -75,7 +75,7 @@ class ObjectCache {
/**
* Create a new cache object from parameters
*
- * @param $params array
+ * @param array $params
*
* @throws MWException
* @return BagOStuff
@@ -87,8 +87,9 @@ class ObjectCache {
$class = $params['class'];
return new $class( $params );
} else {
- throw new MWException( "The definition of cache type \"" . print_r( $params, true ) . "\" lacks both " .
- "factory and class parameters." );
+ throw new MWException( "The definition of cache type \""
+ . print_r( $params, true ) . "\" lacks both "
+ . "factory and class parameters." );
}
}
@@ -101,7 +102,7 @@ class ObjectCache {
* be an alias to the configured cache choice for that.
* If no cache choice is configured (by default $wgMainCacheType is CACHE_NONE),
* then CACHE_ANYTHING will forward to CACHE_DB.
- * @param $params array
+ * @param array $params
* @return BagOStuff
*/
static function newAnything( $params ) {
@@ -118,11 +119,15 @@ class ObjectCache {
/**
* Factory function referenced from DefaultSettings.php for CACHE_ACCEL.
*
- * @param $params array
+ * This will look for any APC style server-local cache.
+ * A fallback cache can be specified if none is found.
+ *
+ * @param array $params
+ * @param int|string $fallback Fallback cache, e.g. (CACHE_NONE, "hash") (since 1.24)
* @throws MWException
* @return BagOStuff
*/
- static function newAccelerator( $params ) {
+ static function newAccelerator( $params, $fallback = null ) {
if ( function_exists( 'apc_fetch' ) ) {
$id = 'apc';
} elseif ( function_exists( 'xcache_get' ) && wfIniGetBool( 'xcache.var_size' ) ) {
@@ -130,6 +135,9 @@ class ObjectCache {
} elseif ( function_exists( 'wincache_ucache_get' ) ) {
$id = 'wincache';
} else {
+ if ( $fallback ) {
+ return self::newFromId( $fallback );
+ }
throw new MWException( "CACHE_ACCEL requested but no suitable object " .
"cache is present. You may want to install APC." );
}
@@ -143,7 +151,7 @@ class ObjectCache {
* hashing scheme and a different interpretation of the flags bitfield, so
* switching between the two clients randomly would be disastrous.
*
- * @param $params array
+ * @param array $params
*
* @return MemcachedPhpBagOStuff
*/
diff --git a/includes/objectcache/ObjectCacheSessionHandler.php b/includes/objectcache/ObjectCacheSessionHandler.php
index 7cf960e7..cdf8da1e 100644
--- a/includes/objectcache/ObjectCacheSessionHandler.php
+++ b/includes/objectcache/ObjectCacheSessionHandler.php
@@ -49,6 +49,7 @@ class ObjectCacheSessionHandler {
/**
* Get the cache storage object to use for session storage
+ * @return ObjectCache
*/
static function getCache() {
global $wgSessionCacheType;
@@ -58,8 +59,8 @@ class ObjectCacheSessionHandler {
/**
* Get a cache key for the given session id.
*
- * @param string $id session id
- * @return String: cache key
+ * @param string $id Session id
+ * @return string Cache key
*/
static function getKey( $id ) {
return wfMemcKey( 'session', $id );
@@ -68,9 +69,9 @@ class ObjectCacheSessionHandler {
/**
* Callback when opening a session.
*
- * @param $save_path String: path used to store session files, unused
- * @param $session_name String: session name
- * @return Boolean: success
+ * @param string $save_path Path used to store session files, unused
+ * @param string $session_name Session name
+ * @return bool Success
*/
static function open( $save_path, $session_name ) {
return true;
@@ -80,7 +81,7 @@ class ObjectCacheSessionHandler {
* Callback when closing a session.
* NOP.
*
- * @return Boolean: success
+ * @return bool Success
*/
static function close() {
return true;
@@ -89,8 +90,8 @@ class ObjectCacheSessionHandler {
/**
* Callback when reading session data.
*
- * @param string $id session id
- * @return Mixed: session data
+ * @param string $id Session id
+ * @return mixed Session data
*/
static function read( $id ) {
$data = self::getCache()->get( self::getKey( $id ) );
@@ -103,9 +104,9 @@ class ObjectCacheSessionHandler {
/**
* Callback when writing session data.
*
- * @param string $id session id
- * @param $data Mixed: session data
- * @return Boolean: success
+ * @param string $id Session id
+ * @param mixed $data Session data
+ * @return bool Success
*/
static function write( $id, $data ) {
global $wgObjectCacheSessionExpiry;
@@ -116,8 +117,8 @@ class ObjectCacheSessionHandler {
/**
* Callback to destroy a session when calling session_destroy().
*
- * @param string $id session id
- * @return Boolean: success
+ * @param string $id Session id
+ * @return bool Success
*/
static function destroy( $id ) {
self::getCache()->delete( self::getKey( $id ) );
@@ -128,8 +129,8 @@ class ObjectCacheSessionHandler {
* Callback to execute garbage collection.
* NOP: Object caches perform garbage collection implicitly
*
- * @param $maxlifetime Integer: maximum session life time
- * @return Boolean: success
+ * @param int $maxlifetime Maximum session life time
+ * @return bool Success
*/
static function gc( $maxlifetime ) {
return true;
diff --git a/includes/objectcache/RedisBagOStuff.php b/includes/objectcache/RedisBagOStuff.php
index 135e0e83..ae8cc5b7 100644
--- a/includes/objectcache/RedisBagOStuff.php
+++ b/includes/objectcache/RedisBagOStuff.php
@@ -23,7 +23,7 @@
class RedisBagOStuff extends BagOStuff {
/** @var RedisConnectionPool */
protected $redisPool;
- /** @var Array List of server names */
+ /** @var array List of server names */
protected $servers;
/** @var bool */
protected $automaticFailover;
@@ -53,9 +53,10 @@ class RedisBagOStuff extends BagOStuff {
* consistent hashing algorithm). True by default. This has the
* potential to create consistency issues if a server is slow enough to
* flap, for example if it is in swap death.
+ * @param array $params
*/
function __construct( $params ) {
- $redisConf = array( 'serializer' => 'php' );
+ $redisConf = array( 'serializer' => 'none' ); // manage that in this class
foreach ( array( 'connectTimeout', 'persistent', 'password' ) as $opt ) {
if ( isset( $params[$opt] ) ) {
$redisConf[$opt] = $params[$opt];
@@ -72,96 +73,88 @@ class RedisBagOStuff extends BagOStuff {
}
public function get( $key, &$casToken = null ) {
- wfProfileIn( __METHOD__ );
+ $section = new ProfileSection( __METHOD__ );
+
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
- wfProfileOut( __METHOD__ );
return false;
}
try {
- $result = $conn->get( $key );
+ $value = $conn->get( $key );
+ $casToken = $value;
+ $result = $this->unserialize( $value );
} catch ( RedisException $e ) {
$result = false;
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
- $casToken = $result;
+
$this->logRequest( 'get', $key, $server, $result );
- wfProfileOut( __METHOD__ );
return $result;
}
public function set( $key, $value, $expiry = 0 ) {
- wfProfileIn( __METHOD__ );
+ $section = new ProfileSection( __METHOD__ );
+
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
- wfProfileOut( __METHOD__ );
return false;
}
$expiry = $this->convertToRelative( $expiry );
try {
- if ( !$expiry ) {
- // No expiry, that is very different from zero expiry in Redis
- $result = $conn->set( $key, $value );
+ if ( $expiry ) {
+ $result = $conn->setex( $key, $expiry, $this->serialize( $value ) );
} else {
- $result = $conn->setex( $key, $expiry, $value );
+ // No expiry, that is very different from zero expiry in Redis
+ $result = $conn->set( $key, $this->serialize( $value ) );
}
} catch ( RedisException $e ) {
$result = false;
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
$this->logRequest( 'set', $key, $server, $result );
- wfProfileOut( __METHOD__ );
return $result;
}
public function cas( $casToken, $key, $value, $expiry = 0 ) {
- wfProfileIn( __METHOD__ );
+ $section = new ProfileSection( __METHOD__ );
+
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
- wfProfileOut( __METHOD__ );
return false;
}
$expiry = $this->convertToRelative( $expiry );
try {
$conn->watch( $key );
- if ( $this->get( $key ) !== $casToken ) {
- wfProfileOut( __METHOD__ );
+ if ( $this->serialize( $this->get( $key ) ) !== $casToken ) {
+ $conn->unwatch();
return false;
}
+ // multi()/exec() will fail atomically if the key changed since watch()
$conn->multi();
-
- if ( !$expiry ) {
- // No expiry, that is very different from zero expiry in Redis
- $conn->set( $key, $value );
+ if ( $expiry ) {
+ $conn->setex( $key, $expiry, $this->serialize( $value ) );
} else {
- $conn->setex( $key, $expiry, $value );
+ // No expiry, that is very different from zero expiry in Redis
+ $conn->set( $key, $this->serialize( $value ) );
}
-
- /*
- * multi()/exec() (transactional mode) allows multiple values to
- * be set/get at once and will return an array of results, in
- * the order they were set/get. In this case, we only set 1
- * value, which should (in case of success) result in true.
- */
$result = ( $conn->exec() == array( true ) );
} catch ( RedisException $e ) {
$result = false;
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
$this->logRequest( 'cas', $key, $server, $result );
- wfProfileOut( __METHOD__ );
return $result;
}
public function delete( $key, $time = 0 ) {
- wfProfileIn( __METHOD__ );
+ $section = new ProfileSection( __METHOD__ );
+
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
- wfProfileOut( __METHOD__ );
return false;
}
try {
@@ -170,15 +163,16 @@ class RedisBagOStuff extends BagOStuff {
$result = true;
} catch ( RedisException $e ) {
$result = false;
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
+
$this->logRequest( 'delete', $key, $server, $result );
- wfProfileOut( __METHOD__ );
return $result;
}
public function getMulti( array $keys ) {
- wfProfileIn( __METHOD__ );
+ $section = new ProfileSection( __METHOD__ );
+
$batches = array();
$conns = array();
foreach ( $keys as $key ) {
@@ -204,78 +198,152 @@ class RedisBagOStuff extends BagOStuff {
}
foreach ( $batchResult as $i => $value ) {
if ( $value !== false ) {
- $result[$batchKeys[$i]] = $value;
+ $result[$batchKeys[$i]] = $this->unserialize( $value );
}
}
} catch ( RedisException $e ) {
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
}
$this->debug( "getMulti for " . count( $keys ) . " keys " .
"returned " . count( $result ) . " results" );
- wfProfileOut( __METHOD__ );
return $result;
}
+ /**
+ * @param array $data
+ * @param int $expiry
+ * @return bool
+ */
+ public function setMulti( array $data, $expiry = 0 ) {
+ $section = new ProfileSection( __METHOD__ );
+
+ $batches = array();
+ $conns = array();
+ foreach ( $data as $key => $value ) {
+ list( $server, $conn ) = $this->getConnection( $key );
+ if ( !$conn ) {
+ continue;
+ }
+ $conns[$server] = $conn;
+ $batches[$server][] = $key;
+ }
+
+ $expiry = $this->convertToRelative( $expiry );
+ $result = true;
+ foreach ( $batches as $server => $batchKeys ) {
+ $conn = $conns[$server];
+ try {
+ $conn->multi( Redis::PIPELINE );
+ foreach ( $batchKeys as $key ) {
+ if ( $expiry ) {
+ $conn->setex( $key, $expiry, $this->serialize( $data[$key] ) );
+ } else {
+ $conn->set( $key, $this->serialize( $data[$key] ) );
+ }
+ }
+ $batchResult = $conn->exec();
+ if ( $batchResult === false ) {
+ $this->debug( "setMulti request to $server failed" );
+ continue;
+ }
+ foreach ( $batchResult as $value ) {
+ if ( $value === false ) {
+ $result = false;
+ }
+ }
+ } catch ( RedisException $e ) {
+ $this->handleException( $server, $conn, $e );
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+
+
public function add( $key, $value, $expiry = 0 ) {
- wfProfileIn( __METHOD__ );
+ $section = new ProfileSection( __METHOD__ );
+
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
- wfProfileOut( __METHOD__ );
return false;
}
$expiry = $this->convertToRelative( $expiry );
try {
- $result = $conn->setnx( $key, $value );
- if ( $result && $expiry ) {
+ if ( $expiry ) {
+ $conn->multi();
+ $conn->setnx( $key, $this->serialize( $value ) );
$conn->expire( $key, $expiry );
+ $result = ( $conn->exec() == array( true, true ) );
+ } else {
+ $result = $conn->setnx( $key, $this->serialize( $value ) );
}
} catch ( RedisException $e ) {
$result = false;
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
+
$this->logRequest( 'add', $key, $server, $result );
- wfProfileOut( __METHOD__ );
return $result;
}
/**
- * Non-atomic implementation of replace(). Could perhaps be done atomically
- * with WATCH or scripting, but this function is rarely used.
+ * Non-atomic implementation of incr().
+ *
+ * Probably all callers actually want incr() to atomically initialise
+ * values to zero if they don't exist, as provided by the Redis INCR
+ * command. But we are constrained by the memcached-like interface to
+ * return null in that case. Once the key exists, further increments are
+ * atomic.
+ * @param string $key Key to increase
+ * @param int $value Value to add to $key (Default 1)
+ * @return int|bool New value or false on failure
*/
- public function replace( $key, $value, $expiry = 0 ) {
- wfProfileIn( __METHOD__ );
+ public function incr( $key, $value = 1 ) {
+ $section = new ProfileSection( __METHOD__ );
+
list( $server, $conn ) = $this->getConnection( $key );
if ( !$conn ) {
- wfProfileOut( __METHOD__ );
return false;
}
if ( !$conn->exists( $key ) ) {
- wfProfileOut( __METHOD__ );
- return false;
+ return null;
}
-
- $expiry = $this->convertToRelative( $expiry );
try {
- if ( !$expiry ) {
- $result = $conn->set( $key, $value );
- } else {
- $result = $conn->setex( $key, $expiry, $value );
- }
+ $result = $conn->incrBy( $key, $value );
} catch ( RedisException $e ) {
$result = false;
- $this->handleException( $server, $conn, $e );
+ $this->handleException( $conn, $e );
}
- $this->logRequest( 'replace', $key, $server, $result );
- wfProfileOut( __METHOD__ );
+ $this->logRequest( 'incr', $key, $server, $result );
return $result;
}
+ /**
+ * @param mixed $data
+ * @return string
+ */
+ protected function serialize( $data ) {
+ // Serialize anything but integers so INCR/DECR work
+ // Do not store integer-like strings as integers to avoid type confusion (bug 60563)
+ return is_int( $data ) ? $data : serialize( $data );
+ }
+
+ /**
+ * @param string $data
+ * @return mixed
+ */
+ protected function unserialize( $data ) {
+ return ctype_digit( $data ) ? intval( $data ) : unserialize( $data );
+ }
/**
* Get a Redis object with a connection suitable for fetching the specified key
- * @return Array (server, RedisConnRef) or (false, false)
+ * @param string $key
+ * @return array (server, RedisConnRef) or (false, false)
*/
protected function getConnection( $key ) {
if ( count( $this->servers ) === 1 ) {
@@ -294,14 +362,16 @@ class RedisBagOStuff extends BagOStuff {
return array( $server, $conn );
}
}
+ $this->setLastError( BagOStuff::ERR_UNREACHABLE );
return array( false, false );
}
/**
* Log a fatal error
+ * @param string $msg
*/
protected function logError( $msg ) {
- wfDebugLog( 'redis', "Redis error: $msg\n" );
+ wfDebugLog( 'redis', "Redis error: $msg" );
}
/**
@@ -309,13 +379,20 @@ class RedisBagOStuff extends BagOStuff {
* and protocol errors. Sometimes it also closes the connection, sometimes
* not. The safest response for us is to explicitly destroy the connection
* object and let it be reopened during the next request.
+ * @param RedisConnRef $conn
+ * @param Exception $e
*/
- protected function handleException( $server, RedisConnRef $conn, $e ) {
- $this->redisPool->handleException( $server, $conn, $e );
+ protected function handleException( RedisConnRef $conn, $e ) {
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
+ $this->redisPool->handleError( $conn, $e );
}
/**
* Send information about a single request to the debug log
+ * @param string $method
+ * @param string $key
+ * @param string $server
+ * @param bool $result
*/
public function logRequest( $method, $key, $server, $result ) {
$this->debug( "$method $key on $server: " .
diff --git a/includes/objectcache/SqlBagOStuff.php b/includes/objectcache/SqlBagOStuff.php
index acf27036..58720790 100644
--- a/includes/objectcache/SqlBagOStuff.php
+++ b/includes/objectcache/SqlBagOStuff.php
@@ -27,22 +27,37 @@
* @ingroup Cache
*/
class SqlBagOStuff extends BagOStuff {
- /**
- * @var LoadBalancer
- */
- var $lb;
+ /** @var LoadBalancer */
+ protected $lb;
+
+ protected $serverInfos;
+
+ /** @var array */
+ protected $serverNames;
+
+ /** @var int */
+ protected $numServers;
+
+ /** @var array */
+ protected $conns;
+
+ /** @var int */
+ protected $lastExpireAll = 0;
+
+ /** @var int */
+ protected $purgePeriod = 100;
- var $serverInfos;
- var $serverNames;
- var $numServers;
- var $conns;
- var $lastExpireAll = 0;
- var $purgePeriod = 100;
- var $shards = 1;
- var $tableName = 'objectcache';
+ /** @var int */
+ protected $shards = 1;
- protected $connFailureTimes = array(); // UNIX timestamps
- protected $connFailureErrors = array(); // exceptions
+ /** @var string */
+ protected $tableName = 'objectcache';
+
+ /** @var array UNIX timestamps */
+ protected $connFailureTimes = array();
+
+ /** @var array Exceptions */
+ protected $connFailureErrors = array();
/**
* Constructor. Parameters are:
@@ -70,7 +85,7 @@ class SqlBagOStuff extends BagOStuff {
* distributed across all tables by key hash. This is for
* MySQL bugs 61735 and 61736.
*
- * @param $params array
+ * @param array $params
*/
public function __construct( $params ) {
if ( isset( $params['servers'] ) ) {
@@ -101,7 +116,7 @@ class SqlBagOStuff extends BagOStuff {
/**
* Get a connection to the specified database
*
- * @param $serverIndex integer
+ * @param int $serverIndex
* @return DatabaseBase
*/
protected function getDB( $serverIndex ) {
@@ -114,8 +129,8 @@ class SqlBagOStuff extends BagOStuff {
# Don't keep timing out trying to connect for each call if the DB is down
if ( isset( $this->connFailureErrors[$serverIndex] )
- && ( time() - $this->connFailureTimes[$serverIndex] ) < 60 )
- {
+ && ( time() - $this->connFailureTimes[$serverIndex] ) < 60
+ ) {
throw $this->connFailureErrors[$serverIndex];
}
@@ -155,8 +170,8 @@ class SqlBagOStuff extends BagOStuff {
/**
* Get the server index and table name for a given key
- * @param $key string
- * @return Array: server index and table name
+ * @param string $key
+ * @return array Server index and table name
*/
protected function getTableByKey( $key ) {
if ( $this->shards > 1 ) {
@@ -178,7 +193,7 @@ class SqlBagOStuff extends BagOStuff {
/**
* Get the table name for a given shard index
- * @param $index int
+ * @param int $index
* @return string
*/
protected function getTableNameByShard( $index ) {
@@ -192,8 +207,8 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $casToken[optional] mixed
+ * @param string $key
+ * @param mixed $casToken [optional]
* @return mixed
*/
public function get( $key, &$casToken = null ) {
@@ -206,8 +221,8 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $keys array
- * @return Array
+ * @param array $keys
+ * @return array
*/
public function getMulti( array $keys ) {
$values = array(); // array of (key => value)
@@ -228,7 +243,15 @@ class SqlBagOStuff extends BagOStuff {
$res = $db->select( $tableName,
array( 'keyname', 'value', 'exptime' ),
array( 'keyname' => $tableKeys ),
- __METHOD__ );
+ __METHOD__,
+ // Approximate write-on-the-fly BagOStuff API via blocking.
+ // This approximation fails if a ROLLBACK happens (which is rare).
+ // We do not want to flush the TRX as that can break callers.
+ $db->trxLevel() ? array( 'LOCK IN SHARE MODE' ) : array()
+ );
+ if ( $res === false ) {
+ continue;
+ }
foreach ( $res as $row ) {
$row->serverIndex = $serverIndex;
$row->tableName = $tableName;
@@ -248,14 +271,11 @@ class SqlBagOStuff extends BagOStuff {
$db = $this->getDB( $row->serverIndex );
if ( $this->isExpired( $db, $row->exptime ) ) { // MISS
$this->debug( "get: key has expired, deleting" );
- $db->begin( __METHOD__ );
# Put the expiry time in the WHERE condition to avoid deleting a
# newly-inserted value
$db->delete( $row->tableName,
array( 'keyname' => $key, 'exptime' => $row->exptime ),
__METHOD__ );
- $db->commit( __METHOD__ );
- $values[$key] = false;
} else { // HIT
$values[$key] = $this->unserialize( $db->decodeBlob( $row->value ) );
}
@@ -263,7 +283,6 @@ class SqlBagOStuff extends BagOStuff {
$this->handleWriteError( $e, $row->serverIndex );
}
} else { // MISS
- $values[$key] = false;
$this->debug( 'get: no matching rows' );
}
}
@@ -272,9 +291,77 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param array $data
+ * @param int $expiry
+ * @return bool
+ */
+ public function setMulti( array $data, $expiry = 0 ) {
+ $keysByTable = array();
+ foreach ( $data as $key => $value ) {
+ list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
+ $keysByTable[$serverIndex][$tableName][] = $key;
+ }
+
+ $this->garbageCollect(); // expire old entries if any
+
+ $result = true;
+ $exptime = (int)$expiry;
+ foreach ( $keysByTable as $serverIndex => $serverKeys ) {
+ try {
+ $db = $this->getDB( $serverIndex );
+ } catch ( DBError $e ) {
+ $this->handleWriteError( $e, $serverIndex );
+ $result = false;
+ continue;
+ }
+
+ if ( $exptime < 0 ) {
+ $exptime = 0;
+ }
+
+ if ( $exptime == 0 ) {
+ $encExpiry = $this->getMaxDateTime( $db );
+ } else {
+ if ( $exptime < 3.16e8 ) { # ~10 years
+ $exptime += time();
+ }
+ $encExpiry = $db->timestamp( $exptime );
+ }
+ foreach ( $serverKeys as $tableName => $tableKeys ) {
+ $rows = array();
+ foreach ( $tableKeys as $key ) {
+ $rows[] = array(
+ 'keyname' => $key,
+ 'value' => $db->encodeBlob( $this->serialize( $data[$key] ) ),
+ 'exptime' => $encExpiry,
+ );
+ }
+
+ try {
+ $db->replace(
+ $tableName,
+ array( 'keyname' ),
+ $rows,
+ __METHOD__
+ );
+ } catch ( DBError $e ) {
+ $this->handleWriteError( $e, $serverIndex );
+ $result = false;
+ }
+
+ }
+
+ }
+
+ return $result;
+ }
+
+
+
+ /**
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function set( $key, $value, $exptime = 0 ) {
@@ -296,7 +383,6 @@ class SqlBagOStuff extends BagOStuff {
$encExpiry = $db->timestamp( $exptime );
}
- $db->begin( __METHOD__ );
// (bug 24425) use a replace if the db supports it instead of
// delete/insert to avoid clashes with conflicting keynames
$db->replace(
@@ -307,7 +393,6 @@ class SqlBagOStuff extends BagOStuff {
'value' => $db->encodeBlob( $this->serialize( $value ) ),
'exptime' => $encExpiry
), __METHOD__ );
- $db->commit( __METHOD__ );
} catch ( DBError $e ) {
$this->handleWriteError( $e, $serverIndex );
return false;
@@ -317,10 +402,10 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -341,7 +426,6 @@ class SqlBagOStuff extends BagOStuff {
}
$encExpiry = $db->timestamp( $exptime );
}
- $db->begin( __METHOD__ );
// (bug 24425) use a replace if the db supports it instead of
// delete/insert to avoid clashes with conflicting keynames
$db->update(
@@ -357,7 +441,6 @@ class SqlBagOStuff extends BagOStuff {
),
__METHOD__
);
- $db->commit( __METHOD__ );
} catch ( DBQueryError $e ) {
$this->handleWriteError( $e, $serverIndex );
@@ -368,20 +451,18 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $time int
+ * @param string $key
+ * @param int $time
* @return bool
*/
public function delete( $key, $time = 0 ) {
list( $serverIndex, $tableName ) = $this->getTableByKey( $key );
try {
$db = $this->getDB( $serverIndex );
- $db->begin( __METHOD__ );
$db->delete(
$tableName,
array( 'keyname' => $key ),
__METHOD__ );
- $db->commit( __METHOD__ );
} catch ( DBError $e ) {
$this->handleWriteError( $e, $serverIndex );
return false;
@@ -391,8 +472,8 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $key string
- * @param $step int
+ * @param string $key
+ * @param int $step
* @return int|null
*/
public function incr( $key, $step = 1 ) {
@@ -400,7 +481,6 @@ class SqlBagOStuff extends BagOStuff {
try {
$db = $this->getDB( $serverIndex );
$step = intval( $step );
- $db->begin( __METHOD__ );
$row = $db->selectRow(
$tableName,
array( 'value', 'exptime' ),
@@ -409,14 +489,12 @@ class SqlBagOStuff extends BagOStuff {
array( 'FOR UPDATE' ) );
if ( $row === false ) {
// Missing
- $db->commit( __METHOD__ );
return null;
}
$db->delete( $tableName, array( 'keyname' => $key ), __METHOD__ );
if ( $this->isExpired( $db, $row->exptime ) ) {
// Expired, do not reinsert
- $db->commit( __METHOD__ );
return null;
}
@@ -434,7 +512,6 @@ class SqlBagOStuff extends BagOStuff {
// Race condition. See bug 28611
$newValue = null;
}
- $db->commit( __METHOD__ );
} catch ( DBError $e ) {
$this->handleWriteError( $e, $serverIndex );
return null;
@@ -444,7 +521,8 @@ class SqlBagOStuff extends BagOStuff {
}
/**
- * @param $exptime string
+ * @param DatabaseBase $db
+ * @param string $exptime
* @return bool
*/
protected function isExpired( $db, $exptime ) {
@@ -452,6 +530,7 @@ class SqlBagOStuff extends BagOStuff {
}
/**
+ * @param DatabaseBase $db
* @return string
*/
protected function getMaxDateTime( $db ) {
@@ -485,8 +564,8 @@ class SqlBagOStuff extends BagOStuff {
/**
* Delete objects from the database which expire before a certain date.
- * @param $timestamp string
- * @param $progressCallback bool|callback
+ * @param string $timestamp
+ * @param bool|callable $progressCallback
* @return bool
*/
public function deleteObjectsExpiringBefore( $timestamp, $progressCallback = false ) {
@@ -509,7 +588,7 @@ class SqlBagOStuff extends BagOStuff {
$conds,
__METHOD__,
array( 'LIMIT' => 100, 'ORDER BY' => 'exptime' ) );
- if ( !$rows->numRows() ) {
+ if ( $rows === false || !$rows->numRows() ) {
break;
}
$keys = array();
@@ -524,7 +603,6 @@ class SqlBagOStuff extends BagOStuff {
$maxExpTime = $row->exptime;
}
- $db->begin( __METHOD__ );
$db->delete(
$this->getTableNameByShard( $i ),
array(
@@ -533,7 +611,6 @@ class SqlBagOStuff extends BagOStuff {
'keyname' => $keys
),
__METHOD__ );
- $db->commit( __METHOD__ );
if ( $progressCallback ) {
if ( intval( $totalSeconds ) === 0 ) {
@@ -566,9 +643,7 @@ class SqlBagOStuff extends BagOStuff {
try {
$db = $this->getDB( $serverIndex );
for ( $i = 0; $i < $this->shards; $i++ ) {
- $db->begin( __METHOD__ );
$db->delete( $this->getTableNameByShard( $i ), '*', __METHOD__ );
- $db->commit( __METHOD__ );
}
} catch ( DBError $e ) {
$this->handleWriteError( $e, $serverIndex );
@@ -583,7 +658,7 @@ class SqlBagOStuff extends BagOStuff {
* On typical message and page data, this can provide a 3X decrease
* in storage requirements.
*
- * @param $data mixed
+ * @param mixed $data
* @return string
*/
protected function serialize( &$data ) {
@@ -598,7 +673,7 @@ class SqlBagOStuff extends BagOStuff {
/**
* Unserialize and, if necessary, decompress an object.
- * @param $serial string
+ * @param string $serial
* @return mixed
*/
protected function unserialize( $serial ) {
@@ -619,6 +694,9 @@ class SqlBagOStuff extends BagOStuff {
/**
* Handle a DBError which occurred during a read operation.
+ *
+ * @param DBError $exception
+ * @param int $serverIndex
*/
protected function handleReadError( DBError $exception, $serverIndex ) {
if ( $exception instanceof DBConnectionError ) {
@@ -626,14 +704,19 @@ class SqlBagOStuff extends BagOStuff {
}
wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" );
if ( $exception instanceof DBConnectionError ) {
+ $this->setLastError( BagOStuff::ERR_UNREACHABLE );
wfDebug( __METHOD__ . ": ignoring connection error\n" );
} else {
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
wfDebug( __METHOD__ . ": ignoring query error\n" );
}
}
/**
* Handle a DBQueryError which occurred during a write operation.
+ *
+ * @param DBError $exception
+ * @param int $serverIndex
*/
protected function handleWriteError( DBError $exception, $serverIndex ) {
if ( $exception instanceof DBConnectionError ) {
@@ -642,18 +725,24 @@ class SqlBagOStuff extends BagOStuff {
if ( $exception->db && $exception->db->wasReadOnlyError() ) {
try {
$exception->db->rollback( __METHOD__ );
- } catch ( DBError $e ) {}
+ } catch ( DBError $e ) {
+ }
}
wfDebugLog( 'SQLBagOStuff', "DBError: {$exception->getMessage()}" );
if ( $exception instanceof DBConnectionError ) {
+ $this->setLastError( BagOStuff::ERR_UNREACHABLE );
wfDebug( __METHOD__ . ": ignoring connection error\n" );
} else {
+ $this->setLastError( BagOStuff::ERR_UNEXPECTED );
wfDebug( __METHOD__ . ": ignoring query error\n" );
}
}
/**
* Mark a server down due to a DBConnectionError exception
+ *
+ * @param DBError $exception
+ * @param int $serverIndex
*/
protected function markServerDown( $exception, $serverIndex ) {
if ( isset( $this->connFailureTimes[$serverIndex] ) ) {
@@ -677,19 +766,15 @@ class SqlBagOStuff extends BagOStuff {
public function createTables() {
for ( $serverIndex = 0; $serverIndex < $this->numServers; $serverIndex++ ) {
$db = $this->getDB( $serverIndex );
- if ( $db->getType() !== 'mysql'
- || version_compare( $db->getServerVersion(), '4.1.0', '<' ) )
- {
+ if ( $db->getType() !== 'mysql' ) {
throw new MWException( __METHOD__ . ' is not supported on this DB server' );
}
for ( $i = 0; $i < $this->shards; $i++ ) {
- $db->begin( __METHOD__ );
$db->query(
'CREATE TABLE ' . $db->tableName( $this->getTableNameByShard( $i ) ) .
' LIKE ' . $db->tableName( 'objectcache' ),
__METHOD__ );
- $db->commit( __METHOD__ );
}
}
}
@@ -698,4 +783,5 @@ class SqlBagOStuff extends BagOStuff {
/**
* Backwards compatibility alias
*/
-class MediaWikiBagOStuff extends SqlBagOStuff { }
+class MediaWikiBagOStuff extends SqlBagOStuff {
+}
diff --git a/includes/objectcache/WinCacheBagOStuff.php b/includes/objectcache/WinCacheBagOStuff.php
index 6d9b47ad..78a512ce 100644
--- a/includes/objectcache/WinCacheBagOStuff.php
+++ b/includes/objectcache/WinCacheBagOStuff.php
@@ -32,8 +32,8 @@ class WinCacheBagOStuff extends BagOStuff {
/**
* Get a value from the WinCache object cache
*
- * @param string $key cache key
- * @param $casToken[optional] int: cas token
+ * @param string $key Cache key
+ * @param int $casToken [optional] Cas token
* @return mixed
*/
public function get( $key, &$casToken = null ) {
@@ -51,9 +51,9 @@ class WinCacheBagOStuff extends BagOStuff {
/**
* Store a value in the WinCache object cache
*
- * @param string $key cache key
- * @param $value Mixed: object to store
- * @param int $expire expiration time
+ * @param string $key Cache key
+ * @param mixed $value Value to store
+ * @param int $expire Expiration time
* @return bool
*/
public function set( $key, $value, $expire = 0 ) {
@@ -67,10 +67,10 @@ class WinCacheBagOStuff extends BagOStuff {
/**
* Store a value in the WinCache object cache, race condition-safe
*
- * @param int $casToken cas token
- * @param string $key cache key
- * @param int $value object to store
- * @param int $exptime expiration time
+ * @param int $casToken Cas token
+ * @param string $key Cache key
+ * @param int $value Object to store
+ * @param int $exptime Expiration time
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -80,8 +80,8 @@ class WinCacheBagOStuff extends BagOStuff {
/**
* Remove a value from the WinCache object cache
*
- * @param string $key cache key
- * @param int $time not used in this implementation
+ * @param string $key Cache key
+ * @param int $time Not used in this implementation
* @return bool
*/
public function delete( $key, $time = 0 ) {
diff --git a/includes/objectcache/XCacheBagOStuff.php b/includes/objectcache/XCacheBagOStuff.php
index 0f45db73..8e2a160d 100644
--- a/includes/objectcache/XCacheBagOStuff.php
+++ b/includes/objectcache/XCacheBagOStuff.php
@@ -31,8 +31,8 @@ class XCacheBagOStuff extends BagOStuff {
/**
* Get a value from the XCache object cache
*
- * @param string $key cache key
- * @param $casToken mixed: cas token
+ * @param string $key Cache key
+ * @param mixed $casToken Cas token
* @return mixed
*/
public function get( $key, &$casToken = null ) {
@@ -54,9 +54,9 @@ class XCacheBagOStuff extends BagOStuff {
/**
* Store a value in the XCache object cache
*
- * @param string $key cache key
- * @param $value Mixed: object to store
- * @param int $expire expiration time
+ * @param string $key Cache key
+ * @param mixed $value Object to store
+ * @param int $expire Expiration time
* @return bool
*/
public function set( $key, $value, $expire = 0 ) {
@@ -69,10 +69,10 @@ class XCacheBagOStuff extends BagOStuff {
}
/**
- * @param $casToken mixed
- * @param $key string
- * @param $value mixed
- * @param $exptime int
+ * @param mixed $casToken
+ * @param string $key
+ * @param mixed $value
+ * @param int $exptime
* @return bool
*/
public function cas( $casToken, $key, $value, $exptime = 0 ) {
@@ -83,8 +83,8 @@ class XCacheBagOStuff extends BagOStuff {
/**
* Remove a value from the XCache object cache
*
- * @param string $key cache key
- * @param int $time not used in this implementation
+ * @param string $key Cache key
+ * @param int $time Not used in this implementation
* @return bool
*/
public function delete( $key, $time = 0 ) {
@@ -97,13 +97,13 @@ class XCacheBagOStuff extends BagOStuff {
* XCache does not seem to support any way of performing CAS - this however will
* provide a way to perform CAS-like functionality.
*
- * @param $key string
- * @param $callback closure Callback method to be executed
+ * @param string $key
+ * @param Closure $callback Callback method to be executed
* @param int $exptime Either an interval in seconds or a unix timestamp for expiry
* @param int $attempts The amount of times to attempt a merge in case of failure
- * @return bool success
+ * @return bool Success
*/
- public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) {
+ public function merge( $key, Closure $callback, $exptime = 0, $attempts = 10 ) {
return $this->mergeViaLock( $key, $callback, $exptime, $attempts );
}