* Avoid usage of this singleton by using TitleValue
* and the associated services when possible.
*
- * @return TitleParser
+ * @return MediaWikiTitleCodec
*/
- private static function getTitleParser() {
+ private static function getMediaWikiTitleCodec() {
global $wgContLang, $wgLocalInterwikis;
static $titleCodec = null;
* @return TitleFormatter
*/
private static function getTitleFormatter() {
- // NOTE: we know that getTitleParser() returns a MediaWikiTitleCodec,
+ // NOTE: we know that getMediaWikiTitleCodec() returns a MediaWikiTitleCodec,
// which implements TitleFormatter.
- return self::getTitleParser();
+ return self::getMediaWikiTitleCodec();
}
function __construct() {
// @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
// the parsing code with Title, while avoiding massive refactoring.
// @todo: get rid of secureAndSplit, refactor parsing code.
- $titleParser = self::getTitleParser();
+ $titleParser = self::getMediaWikiTitleCodec();
// MalformedTitleException can be thrown here
$parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
&& in_array( 'bot', $this->getUser()->getGroups() )
&& wfGetLB()->getServerCount() > 1
) {
- // Figure out how many servers have passed the lag threshold
- $numLagged = 0;
- $lagLimit = $this->getConfig()->get( 'APIMaxLagThreshold' );
- foreach ( wfGetLB()->getLagTimes() as $lag ) {
- if ( $lag > $lagLimit ) {
- ++$numLagged;
- }
- }
- // If a majority of slaves are too lagged then disallow writes
- $slaveCount = wfGetLB()->getServerCount() - 1;
- if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
- $parsed = $this->parseMsg( array( 'readonlytext' ) );
- $this->dieUsage(
- $parsed['info'],
- $parsed['code'],
- /* http error */
- 0,
- array( 'readonlyreason' => "Waiting for $numLagged lagged database(s)" )
- );
+ $this->checkBotReadOnly();
+ }
+ }
+
+ /**
+ * Check whether we are readonly for bots
+ */
+ private function checkBotReadOnly() {
+ // Figure out how many servers have passed the lag threshold
+ $numLagged = 0;
+ $lagLimit = $this->getConfig()->get( 'APIMaxLagThreshold' );
+ $laggedServers = array();
+ $loadBalancer = wfGetLB();
+ foreach ( $loadBalancer->getLagTimes() as $serverIndex => $lag ) {
+ if ( $lag > $lagLimit ) {
+ ++$numLagged;
+ $laggedServers[] = $loadBalancer->getServerName( $serverIndex ) . " ({$lag}s)";
}
}
+
+ // If a majority of slaves are too lagged then disallow writes
+ $slaveCount = wfGetLB()->getServerCount() - 1;
+ if ( $numLagged >= ceil( $slaveCount / 2 ) ) {
+ $laggedServers = join( ', ', $laggedServers );
+ wfDebugLog(
+ 'api-readonly',
+ "Api request failed as read only because the following DBs are lagged: $laggedServers"
+ );
+
+ $parsed = $this->parseMsg( array( 'readonlytext' ) );
+ $this->dieUsage(
+ $parsed['info'],
+ $parsed['code'],
+ /* http error */
+ 0,
+ array( 'readonlyreason' => "Waiting for $numLagged lagged database(s)" )
+ );
+ }
}
/**
public function setApiData( array $data );
}
+/**
+ * Trait to implement the IApiMessage interface for Message subclasses
+ * @since 1.27
+ * @ingroup API
+ */
+trait ApiMessageTrait {
+ protected $apiCode = null;
+ protected $apiData = array();
+
+ public function getApiCode() {
+ return $this->apiCode === null ? $this->getKey() : $this->apiCode;
+ }
+
+ public function setApiCode( $code, array $data = null ) {
+ $this->apiCode = $code;
+ if ( $data !== null ) {
+ $this->setApiData( $data );
+ }
+ }
+
+ public function getApiData() {
+ return $this->apiData;
+ }
+
+ public function setApiData( array $data ) {
+ $this->apiData = $data;
+ }
+
+ public function serialize() {
+ return serialize( array(
+ 'parent' => parent::serialize(),
+ 'apiCode' => $this->apiCode,
+ 'apiData' => $this->apiData,
+ ) );
+ }
+
+ public function unserialize( $serialized ) {
+ $data = unserialize( $serialized );
+ parent::unserialize( $data['parent'] );
+ $this->apiCode = $data['apiCode'];
+ $this->apiData = $data['apiData'];
+ }
+}
+
/**
* Extension of Message implementing IApiMessage
* @since 1.25
* @ingroup API
- * @todo: Would be nice to use a Trait here to avoid code duplication
*/
class ApiMessage extends Message implements IApiMessage {
- protected $apiCode = null;
- protected $apiData = array();
+ use ApiMessageTrait;
/**
* Create an IApiMessage for the message
$this->apiCode = $code;
$this->apiData = (array)$data;
}
-
- public function getApiCode() {
- return $this->apiCode === null ? $this->getKey() : $this->apiCode;
- }
-
- public function setApiCode( $code, array $data = null ) {
- $this->apiCode = $code;
- if ( $data !== null ) {
- $this->setApiData( $data );
- }
- }
-
- public function getApiData() {
- return $this->apiData;
- }
-
- public function setApiData( array $data ) {
- $this->apiData = $data;
- }
-
- public function serialize() {
- return serialize( array(
- 'parent' => parent::serialize(),
- 'apiCode' => $this->apiCode,
- 'apiData' => $this->apiData,
- ) );
- }
-
- public function unserialize( $serialized ) {
- $data = unserialize( $serialized );
- parent::unserialize( $data['parent'] );
- $this->apiCode = $data['apiCode'];
- $this->apiData = $data['apiData'];
- }
}
/**
* Extension of RawMessage implementing IApiMessage
* @since 1.25
* @ingroup API
- * @todo: Would be nice to use a Trait here to avoid code duplication
*/
class ApiRawMessage extends RawMessage implements IApiMessage {
- protected $apiCode = null;
- protected $apiData = array();
+ use ApiMessageTrait;
/**
* @param RawMessage|string|array $msg
$this->apiCode = $code;
$this->apiData = (array)$data;
}
-
- public function getApiCode() {
- return $this->apiCode === null ? $this->getKey() : $this->apiCode;
- }
-
- public function setApiCode( $code, array $data = null ) {
- $this->apiCode = $code;
- if ( $data !== null ) {
- $this->setApiData( $data );
- }
- }
-
- public function getApiData() {
- return $this->apiData;
- }
-
- public function setApiData( array $data ) {
- $this->apiData = $data;
- }
-
- public function serialize() {
- return serialize( array(
- 'parent' => parent::serialize(),
- 'apiCode' => $this->apiCode,
- 'apiData' => $this->apiData,
- ) );
- }
-
- public function unserialize( $serialized ) {
- $data = unserialize( $serialized );
- parent::unserialize( $data['parent'] );
- $this->apiCode = $data['apiCode'];
- $this->apiData = $data['apiData'];
- }
}
* @param string $index
* @return string
*/
- public function indexName( $index ) {
- // @FIXME: Make this protected once we move away from PHP 5.3
- // Needs to be public because of usage in closure (in DatabaseBase::replaceVars)
-
+ protected function indexName( $index ) {
// Backwards-compatibility hack
$renamed = array(
'ar_usertext_timestamp' => 'usertext_timestamp',
* @return string The new SQL statement with variables replaced
*/
protected function replaceVars( $ins ) {
- $that = $this;
$vars = $this->getSchemaVars();
return preg_replace_callback(
'!
`\{\$ (\w+) }` | # 4. addIdentifierQuotes
/\*\$ (\w+) \*/ # 5. leave unencoded
!x',
- function ( $m ) use ( $that, $vars ) {
+ function ( $m ) use ( $vars ) {
// Note: Because of <https://bugs.php.net/bug.php?id=51881>,
// check for both nonexistent keys *and* the empty string.
if ( isset( $m[1] ) && $m[1] !== '' ) {
if ( $m[1] === 'i' ) {
- return $that->indexName( $m[2] );
+ return $this->indexName( $m[2] );
} else {
- return $that->tableName( $m[2] );
+ return $this->tableName( $m[2] );
}
} elseif ( isset( $m[3] ) && $m[3] !== '' && array_key_exists( $m[3], $vars ) ) {
- return $that->addQuotes( $vars[$m[3]] );
+ return $this->addQuotes( $vars[$m[3]] );
} elseif ( isset( $m[4] ) && $m[4] !== '' && array_key_exists( $m[4], $vars ) ) {
- return $that->addIdentifierQuotes( $vars[$m[4]] );
+ return $this->addIdentifierQuotes( $vars[$m[4]] );
} elseif ( isset( $m[5] ) && $m[5] !== '' && array_key_exists( $m[5], $vars ) ) {
return $vars[$m[5]];
} else {
return null;
}
- $that = $this;
- $unlocker = new ScopedCallback( function () use ( $that, $lockKey, $fname ) {
- $that->commit( __METHOD__, 'flush' );
- $that->unlock( $lockKey, $fname );
+ $unlocker = new ScopedCallback( function () use ( $lockKey, $fname ) {
+ $this->commit( __METHOD__, 'flush' );
+ $this->unlock( $lockKey, $fname );
} );
$this->commit( __METHOD__, 'flush' );
$this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
);
- $that = $this;
return $cache->getWithSetCallback(
$key,
$cache::TTL_INDEFINITE,
- function () use ( $that, $cache, $key ) {
+ function () use ( $cache, $key ) {
// Get and leave a lock key in place for a short period
if ( !$cache->lock( $key, 0, 10 ) ) {
return false; // avoid master connection spike slams
}
- $conn = $that->getLazyMasterHandle();
+ $conn = $this->getLazyMasterHandle();
if ( !$conn ) {
return false; // something is misconfigured
}
Hooks::run( 'LinksUpdate', array( &$this ) );
$this->doIncrementalUpdate();
- $that = $this;
- $this->mDb->onTransactionIdle( function() use ( $that ) {
- Hooks::run( 'LinksUpdateComplete', array( &$that ) );
+ $this->mDb->onTransactionIdle( function() {
+ Hooks::run( 'LinksUpdateComplete', array( &$this ) );
} );
}
'body' => $params['content']
) );
- $that = $this;
$method = __METHOD__;
- $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+ $handler = function ( array $request, Status $status ) use ( $method, $params ) {
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
if ( $rcode === 201 ) {
// good
} elseif ( $rcode === 412 ) {
$status->fatal( 'backend-fail-contenttype', $params['dst'] );
} else {
- $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+ $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
}
};
'body' => $handle // resource
) );
- $that = $this;
$method = __METHOD__;
- $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+ $handler = function ( array $request, Status $status ) use ( $method, $params ) {
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
if ( $rcode === 201 ) {
// good
} elseif ( $rcode === 412 ) {
$status->fatal( 'backend-fail-contenttype', $params['dst'] );
} else {
- $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+ $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
}
};
) + $this->sanitizeHdrs( $params ), // extra headers merged into object
) );
- $that = $this;
$method = __METHOD__;
- $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+ $handler = function ( array $request, Status $status ) use ( $method, $params ) {
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
if ( $rcode === 201 ) {
// good
} elseif ( $rcode === 404 ) {
$status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
} else {
- $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+ $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
}
};
);
}
- $that = $this;
$method = __METHOD__;
- $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+ $handler = function ( array $request, Status $status ) use ( $method, $params ) {
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
if ( $request['method'] === 'PUT' && $rcode === 201 ) {
// good
} elseif ( $rcode === 404 ) {
$status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
} else {
- $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+ $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
}
};
'headers' => array()
) );
- $that = $this;
$method = __METHOD__;
- $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+ $handler = function ( array $request, Status $status ) use ( $method, $params ) {
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
if ( $rcode === 204 ) {
// good
$status->fatal( 'backend-fail-delete', $params['src'] );
}
} else {
- $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+ $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
}
};
'headers' => $metaHdrs + $customHdrs
) );
- $that = $this;
$method = __METHOD__;
- $handler = function ( array $request, Status $status ) use ( $that, $method, $params ) {
+ $handler = function ( array $request, Status $status ) use ( $method, $params ) {
list( $rcode, $rdesc, $rhdrs, $rbody, $rerr ) = $request['response'];
if ( $rcode === 202 ) {
// good
} elseif ( $rcode === 404 ) {
$status->fatal( 'backend-fail-describe', $params['src'] );
} else {
- $that->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
+ $this->onError( $status, $method, $params, $rerr, $rcode, $rdesc );
}
};
protected function doBatchPush( array $jobs, $flags ) {
$dbw = $this->getMasterDB();
- $that = $this;
$method = __METHOD__;
$dbw->onTransactionIdle(
- function () use ( $dbw, $that, $jobs, $flags, $method ) {
- $that->doBatchPushInternal( $dbw, $jobs, $flags, $method );
+ function () use ( $dbw, $jobs, $flags, $method ) {
+ $this->doBatchPushInternal( $dbw, $jobs, $flags, $method );
}
);
}
return new ArrayIterator( array() );
}
- $that = $this;
return new MappedIterator(
$unclaimed,
- function ( $value ) use ( $that ) {
- $that->jobFromSpecInternal( $value );
+ function ( $value ) {
+ $this->jobFromSpecInternal( $value );
}
);
}
return new ArrayIterator( array() );
}
- $that = $this;
return new MappedIterator(
$claimed,
- function ( $value ) use ( $that ) {
- $that->jobFromSpecInternal( $value );
+ function ( $value ) {
+ $this->jobFromSpecInternal( $value );
}
);
}
* @return MappedIterator
*/
protected function getJobIterator( RedisConnRef $conn, array $uids ) {
- $that = $this;
-
return new MappedIterator(
$uids,
- function ( $uid ) use ( $that, $conn ) {
- return $that->getJobFromUidInternal( $uid, $conn );
+ function ( $uid ) use ( $conn ) {
+ return $this->getJobFromUidInternal( $uid, $conn );
},
array( 'accept' => function ( $job ) {
return is_object( $job );
}
$lSince = microtime( true ); // lock timestamp
- // PHP 5.3: Can't use $this in a closure
- $that = $this;
- $logger = $this->logger;
- return new ScopedCallback( function() use ( $that, $logger, $key, $lSince, $expiry ) {
+ return new ScopedCallback( function() use ( $key, $lSince, $expiry ) {
$latency = .050; // latency skew (err towards keeping lock present)
$age = ( microtime( true ) - $lSince + $latency );
if ( ( $age + $latency ) >= $expiry ) {
- $logger->warning( "Lock for $key held too long ($age sec)." );
+ $this->logger->warning( "Lock for $key held too long ($age sec)." );
return; // expired; it's not "safe" to delete the key
}
- $that->unlock( $key );
+ $this->unlock( $key );
} );
}
* @return \ScopedCallback When this goes out of scope, a save will be triggered
*/
public function delaySave() {
- $that = $this;
$this->delaySave++;
- $ref = &$this->delaySave;
- return new \ScopedCallback( function () use ( $that, &$ref ) {
- if ( --$ref <= 0 ) {
- $ref = 0;
- $that->save();
+ return new \ScopedCallback( function () {
+ if ( --$this->delaySave <= 0 ) {
+ $this->delaySave = 0;
+ $this->save();
}
} );
}
private function checkPHPSession() {
if ( !$this->checkPHPSessionRecursionGuard ) {
$this->checkPHPSessionRecursionGuard = true;
- $ref = &$this->checkPHPSessionRecursionGuard;
- $reset = new \ScopedCallback( function () use ( &$ref ) {
- $ref = false;
+ $reset = new \ScopedCallback( function () {
+ $this->checkPHPSessionRecursionGuard = false;
} );
if ( $this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() &&
namespace MediaWiki\Session;
-use Psr\Log\LoggerInterface;
-use BagOStuff;
-use WebRequest;
-
/**
* Value object returned by SessionProvider
*
use CachedBagOStuff;
use Config;
use FauxRequest;
-use Language;
-use Message;
use User;
use WebRequest;
* http://www.gnu.org/copyleft/gpl.html
*/
-use MediaWiki\Session\BotPasswordSessionProvider;
-use MediaWiki\Session\SessionInfo;
-
/**
* Utility class for bot passwords
* @since 1.27
// ip-based limits
if ( isset( $limits['ip'] ) ) {
$ip = $this->getRequest()->getIP();
- $keys[wfMemcKey( 'limiter', $action, 'ip', $ip )] = $limits['ip'];
+ $keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
}
// subnet-based limits
if ( isset( $limits['subnet'] ) ) {
$ip = $this->getRequest()->getIP();
$subnet = IP::getSubnet( $ip );
if ( $subnet !== false ) {
- $keys[wfMemcKey( 'limiter', $action, 'subnet', $subnet )] = $limits['subnet'];
+ $keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
}
}
}
case T_NAMESPACE:
case T_CLASS:
case T_INTERFACE:
+ case T_TRAIT:
$this->startToken = $token;
}
}
case T_CLASS:
case T_INTERFACE:
+ case T_TRAIT:
$this->tokens[] = $token;
if ( is_array( $token ) && $token[0] === T_STRING ) {
$this->classes[] = $this->namespace . $this->implodeTokens();
/**
* @covers ApiMessage
+ * @covers ApiMessageTrait
*/
public function testApiMessage() {
$msg = new Message( array( 'foo', 'bar' ), array( 'baz' ) );
/**
* @covers ApiRawMessage
+ * @covers ApiMessageTrait
*/
public function testApiRawMessage() {
$msg = new RawMessage( 'foo', array( 'baz' ) );
}
public function testPersistSessionWithHook() {
- $that = $this;
-
$provider = new CookieSessionProvider( array(
'priority' => 1,
'sessionName' => 'MySessionName',
// Logged-in user, no remember
$mock = $this->getMock( __CLASS__, array( 'onUserSetCookies' ) );
$mock->expects( $this->once() )->method( 'onUserSetCookies' )
- ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $that, $user ) {
- $that->assertSame( $user, $u );
- $that->assertEquals( array(
+ ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
+ $this->assertSame( $user, $u );
+ $this->assertEquals( array(
'wsUserID' => $user->getId(),
'wsUserName' => $user->getName(),
'wsToken' => $user->getToken(),
), $sessionData );
- $that->assertEquals( array(
+ $this->assertEquals( array(
'UserID' => $user->getId(),
'UserName' => $user->getName(),
'Token' => false,
// Logged-in user, remember
$mock = $this->getMock( __CLASS__, array( 'onUserSetCookies' ) );
$mock->expects( $this->once() )->method( 'onUserSetCookies' )
- ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $that, $user ) {
- $that->assertSame( $user, $u );
- $that->assertEquals( array(
+ ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $user ) {
+ $this->assertSame( $user, $u );
+ $this->assertEquals( array(
'wsUserID' => $user->getId(),
'wsUserName' => $user->getName(),
'wsToken' => $user->getToken(),
), $sessionData );
- $that->assertEquals( array(
+ $this->assertEquals( array(
'UserID' => $user->getId(),
'UserName' => $user->getName(),
'Token' => $user->getToken(),
namespace MediaWiki\Session;
-use Psr\Log\LogLevel;
use MediaWikiTestCase;
/**
public function testAutoCreateUser() {
global $wgGroupPermissions;
- $that = $this;
-
\ObjectCache::$instances[__METHOD__] = new TestBagOStuff();
$this->setMwGlobals( array( 'wgMainCacheType' => __METHOD__ ) );
$this->setMWGlobals( array(
$logger->clearBuffer();
// Sanity check that creation still works, and test completion hook
- $cb = $this->callback( function ( User $user ) use ( $that ) {
- $that->assertNotEquals( 0, $user->getId() );
- $that->assertSame( 'UTSessionAutoCreate4', $user->getName() );
- $that->assertEquals(
+ $cb = $this->callback( function ( User $user ) {
+ $this->assertNotEquals( 0, $user->getId() );
+ $this->assertSame( 'UTSessionAutoCreate4', $user->getName() );
+ $this->assertEquals(
$user->getId(), User::idFromName( 'UTSessionAutoCreate4', User::READ_LATEST )
);
return true;
$this->assertSame( array(), $logger->getBuffer() );
// Hook
- $that = $this;
$called = false;
$data = array( 'foo' => 1 );
$this->store->setSession( $id, array( 'metadata' => $metadata, 'data' => $data ) );
) );
$this->mergeMwGlobalArrayValue( 'wgHooks', array(
'SessionCheckInfo' => array( function ( &$reason, $i, $r, $m, $d ) use (
- $that, $info, $metadata, $data, $request, &$called
+ $info, $metadata, $data, $request, &$called
) {
- $that->assertSame( $info->getId(), $i->getId() );
- $that->assertSame( $info->getProvider(), $i->getProvider() );
- $that->assertSame( $info->getUserInfo(), $i->getUserInfo() );
- $that->assertSame( $request, $r );
- $that->assertEquals( $metadata, $m );
- $that->assertEquals( $data, $d );
+ $this->assertSame( $info->getId(), $i->getId() );
+ $this->assertSame( $info->getProvider(), $i->getProvider() );
+ $this->assertSame( $info->getUserInfo(), $i->getUserInfo() );
+ $this->assertSame( $request, $r );
+ $this->assertEquals( $metadata, $m );
+ $this->assertEquals( $data, $d );
$called = true;
return false;
} )