*/
private static function getUrlDomainDistance( $url ) {
$clusterWiki = WikiMap::getWikiFromUrl( $url );
- if ( $clusterWiki === wfWikiID() ) {
+ if ( WikiMap::isCurrentWikiId( $clusterWiki ) ) {
return 'local'; // the current wiki
- } elseif ( $clusterWiki !== false ) {
+ }
+ if ( $clusterWiki !== false ) {
return 'remote'; // another wiki in this cluster/farm
}
$this->saveParseLogger = new NullLogger();
}
+ /**
+ * @param LoggerInterface $saveParseLogger
+ */
+ public function setLogger( LoggerInterface $saveParseLogger ) {
+ $this->saveParseLogger = $saveParseLogger;
+ }
+
/**
* @param RevisionRecord $rev
* @param ParserOptions|null $options
},
'RevisionRenderer' => function ( MediaWikiServices $services ) : RevisionRenderer {
- return new RevisionRenderer( $services->getDBLoadBalancer() );
+ $renderer = new RevisionRenderer( $services->getDBLoadBalancer() );
+ $renderer->setLogger( LoggerFactory::getInstance( 'SaveParse' ) );
+
+ return $renderer;
},
'RevisionStore' => function ( MediaWikiServices $services ) : RevisionStore {
$multi = is_array( $settings );
$settings = (array)$settings;
- if ( $wiki === wfWikiID() ) { // $wiki is this wiki
+ if ( WikiMap::isCurrentWikiId( $wiki ) ) { // $wiki is this wiki
$res = [];
foreach ( $settings as $name ) {
if ( !preg_match( '/^wg[A-Z]/', $name ) ) {
$infoMap = [];
// Make sure at least the current wiki is set, for simple configurations.
// This also makes it the first in the map, which is useful for common cases.
- $infoMap[wfWikiID()] = [
+ $wikiId = self::getWikiIdFromDomain( self::getCurrentWikiDomain() );
+ $infoMap[$wikiId] = [
'url' => $wgCanonicalServer,
'parts' => wfParseUrl( $wgCanonicalServer )
];
* @return string
*/
public static function getWikiIdFromDomain( $domain ) {
- if ( !( $domain instanceof DatabaseDomain ) ) {
- $domain = DatabaseDomain::newFromId( $domain );
- }
+ $domain = DatabaseDomain::newFromId( $domain );
return strlen( $domain->getTablePrefix() )
? "{$domain->getDatabase()}-{$domain->getTablePrefix()}"
: $domain->getDatabase();
}
+
+ /**
+ * @return DatabaseDomain Database domain of the current wiki
+ * @since 1.33
+ */
+ public static function getCurrentWikiDomain() {
+ global $wgDBname, $wgDBmwschema, $wgDBprefix;
+ // Avoid invoking LBFactory to avoid any chance of recursion
+ return new DatabaseDomain( $wgDBname, $wgDBmwschema, (string)$wgDBprefix );
+ }
+
+ /**
+ * @param DatabaseDomain|string $domain
+ * @return bool Whether $domain has the same DB/prefix as the current wiki
+ * @since 1.33
+ */
+ public static function isCurrentWikiDomain( $domain ) {
+ $domain = DatabaseDomain::newFromId( $domain );
+ $curDomain = self::getCurrentWikiDomain();
+
+ return (
+ $curDomain->getDatabase() === $domain->getDatabase() &&
+ // @TODO: check schema instead of assuming it's ""/"mediawiki" and never collides
+ $curDomain->getTablePrefix() === $domain->getTablePrefix()
+ );
+ }
+
+ /**
+ * @param string $wikiId
+ * @return bool Whether $wikiId has the same DB/prefix as the current wiki
+ * @since 1.33
+ */
+ public static function isCurrentWikiId( $wikiId ) {
+ return ( self::getWikiIdFromDomain( self::getCurrentWikiDomain() ) === $wikiId );
+ }
}
foreach ( $a as &$row ) {
$this->insertOneRow( $table, $row, $fname );
}
- $retVal = true;
if ( in_array( 'IGNORE', $options ) ) {
$this->ignoreDupValOnIndex = false;
}
- return $retVal;
+ return true;
}
private function fieldBindStatement( $table, $col, &$val, $includeCol = false ) {
$this->ignoreDupValOnIndex = true;
}
- $retval = $this->query( $sql, $fname );
+ $this->query( $sql, $fname );
if ( in_array( 'IGNORE', $insertOptions ) ) {
$this->ignoreDupValOnIndex = false;
}
-
- return $retval;
}
public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
* @param string $function
* @param bool $isMaster
* @param float $runTime Query run time
- * @return int ID number of the query to pass to queryTime or -1 if the
- * debugger is disabled
+ * @return bool True if debugger is enabled, false otherwise
*/
public static function query( $sql, $function, $isMaster, $runTime ) {
if ( !self::$enabled ) {
- return -1;
+ return false;
}
// Replace invalid UTF-8 chars with a square UTF-8 character
'time' => $runTime,
];
- return count( self::$query ) - 1;
+ return true;
}
/**
use DateTimeZone;
use Exception;
+use WikiMap;
use MWDebug;
use MWExceptionHandler;
use Psr\Log\AbstractLogger;
* @return null
*/
public function log( $level, $message, array $context = [] ) {
+ global $wgDBerrorLog;
+
if ( is_string( $level ) ) {
$level = self::$levelMapping[$level];
}
- if ( $this->channel === 'DBQuery' && isset( $context['method'] )
- && isset( $context['master'] ) && isset( $context['runtime'] )
+ if ( $this->channel === 'DBQuery'
+ && isset( $context['method'] )
+ && isset( $context['master'] )
+ && isset( $context['runtime'] )
) {
- MWDebug::query( $message, $context['method'], $context['master'], $context['runtime'] );
- return; // only send profiling data to MWDebug profiling
+ // Also give the query information to the MWDebug tools
+ $enabled = MWDebug::query(
+ $message,
+ $context['method'],
+ $context['master'],
+ $context['runtime']
+ );
+ if ( $enabled ) {
+ // If we the toolbar was enabled, return early so that we don't
+ // also log the query to the main debug output.
+ return;
+ }
}
+ // If this is a DB-related error, and the site has $wgDBerrorLog
+ // configured, rewrite the channel as wfLogDBError instead.
+ // Likewise, if the site does not use $wgDBerrorLog, it should
+ // configurable like any other channel via $wgDebugLogGroups
+ // or $wgMWLoggerDefaultSpi.
if ( isset( self::$dbChannels[$this->channel] )
&& $level >= self::$levelMapping[LogLevel::ERROR]
+ && $wgDBerrorLog
) {
// Format and write DB errors to the legacy locations
$effectiveChannel = 'wfLogDBError';
$date = $d->format( 'D M j G:i:s T Y' );
$host = wfHostname();
- $wiki = wfWikiID();
+ $wiki = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
$text = "{$date}\t{$host}\t{$wiki}\t{$message}\n";
return $text;
*/
protected static function formatAsWfDebugLog( $channel, $message, $context ) {
$time = wfTimestamp( TS_DB );
- $wiki = wfWikiID();
+ $wiki = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
$host = wfHostname();
$text = "{$time} {$host} {$wiki}: {$message}\n";
return $text;
namespace MediaWiki\Logger\Monolog;
+use WikiMap;
+
/**
* Annotate log records with request-global metadata, such as the hostname,
* wiki / request ID, and MediaWiki version.
public function __invoke( array $record ) {
global $wgVersion;
$record['extra']['host'] = wfHostname();
- $record['extra']['wiki'] = wfWikiID();
+ $record['extra']['wiki'] = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
$record['extra']['mwversion'] = $wgVersion;
$record['extra']['reqId'] = \WebRequest::getRequestId();
if ( wfIsCLI() && isset( $_SERVER['argv'] ) ) {
// Make sure ID is roughly lexicographically increasing for performance
$id = str_pad( UIDGenerator::newTimestampedUID128( 32 ), 26, '0', STR_PAD_LEFT );
// Segregate items by wiki ID for the sake of bookkeeping
+ // @FIXME: this does not include the domain for b/c but it ideally should
$wiki = $this->params['wiki'] ?? wfWikiID();
$url = $be->getContainerStoragePath( 'data' ) . '/' . rawurlencode( $wiki );
$config = $this->backends[$name]['config'];
$config['class'] = $class;
$config += [ // set defaults
+ // @FIXME: this does not include the domain for b/c but it ideally should
'wikiId' => wfWikiID(), // e.g. "my_wiki-en_"
'mimeCallback' => [ $this, 'guessMimeInternal' ],
'obResetFunc' => 'wfResetOutputBuffers',
* @return LockManagerGroup
*/
public static function singleton( $domain = false ) {
- $domain = ( $domain === false ) ? wfWikiID() : $domain;
+ if ( $domain === false ) {
+ $domain = WikiMap::getCurrentWikiDomain()->getId();
+ }
+
if ( !isset( self::$instances[$domain] ) ) {
self::$instances[$domain] = new self( $domain );
self::$instances[$domain]->initFromGlobals();
use Hooks;
use Interwiki;
use Language;
+use WikiMap;
use MapCacheLRU;
use WANObjectCache;
use Wikimedia\Rdbms\Database;
*/
private function getInterwikiCacheEntry( $prefix ) {
wfDebug( __METHOD__ . "( $prefix )\n" );
+
+ $wikiId = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
+
$value = false;
try {
// Resolve site name
if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
- $this->thisSite = $this->getCacheValue( '__sites:' . wfWikiID() );
+ $this->thisSite = $this->getCacheValue( '__sites:' . $wikiId );
if ( $this->thisSite == '' ) {
$this->thisSite = $this->fallbackSite;
}
}
- $value = $this->getCacheValue( wfWikiID() . ':' . $prefix );
+ $value = $this->getCacheValue( $wikiId . ':' . $prefix );
// Site level
if ( $value == '' && $this->interwikiScopes >= 3 ) {
$value = $this->getCacheValue( "_{$this->thisSite}:{$prefix}" );
*/
private function getAllPrefixesCached( $local ) {
wfDebug( __METHOD__ . "()\n" );
+
+ $wikiId = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
+
$data = [];
try {
/* Resolve site name */
if ( $this->interwikiScopes >= 3 && !$this->thisSite ) {
- $site = $this->getCacheValue( '__sites:' . wfWikiID() );
+ $site = $this->getCacheValue( '__sites:' . $wikiId );
if ( $site == '' ) {
$this->thisSite = $this->fallbackSite;
if ( $this->interwikiScopes >= 3 ) {
$sources[] = '_' . $this->thisSite;
}
- $sources[] = wfWikiID();
+ $sources[] = $wikiId;
foreach ( $sources as $source ) {
$list = $this->getCacheValue( '__list:' . $source );
global $wgJobClasses;
$this->assertNotReadOnly();
- if ( $this->wiki !== wfWikiID() ) {
+ if ( !WikiMap::isCurrentWikiId( $this->wiki ) ) {
throw new MWException( "Cannot pop '{$this->type}' job off foreign wiki queue." );
} elseif ( !isset( $wgJobClasses[$this->type] ) ) {
// Do not pop jobs if there is no class for the queue type
self::$instances[$wiki] = new self( $wiki, wfConfiguredReadOnlyReason() );
// Make sure jobs are not getting pushed to bogus wikis. This can confuse
// the job runner system into spawning endless RPC requests that fail (T171371).
- if ( $wiki !== wfWikiID() && !in_array( $wiki, $wgLocalDatabases ) ) {
+ if ( !WikiMap::isCurrentWikiId( $wiki ) && !in_array( $wiki, $wgLocalDatabases ) ) {
self::$instances[$wiki]->invalidWiki = true;
}
}
*/
private function getCachedConfigVar( $name ) {
// @TODO: cleanup this whole method with a proper config system
- if ( $this->wiki === wfWikiID() ) {
+ if ( WikiMap::isCurrentWikiId( $this->wiki ) ) {
return $GLOBALS[$name]; // common case
} else {
$wiki = $this->wiki;
protected function purgeExpiredRows() {
global $wgRCMaxAge, $wgUpdateRowsPerQuery;
- $lockKey = wfWikiID() . ':recentchanges-prune';
-
$dbw = wfGetDB( DB_MASTER );
+ $lockKey = $dbw->getDomainID() . ':recentchanges-prune';
if ( !$dbw->lock( $lockKey, __METHOD__, 0 ) ) {
// already in progress
return;
$factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
$ticket = $factory->getEmptyTransactionTicket( __METHOD__ );
- $lockKey = wfWikiID() . '-activeusers';
+ $lockKey = $dbw->getDomainID() . '-activeusers';
if ( !$dbw->lock( $lockKey, __METHOD__, 0 ) ) {
// Exclusive update (avoids duplicate entries)… it's usually fine to just
// drop out here, if the Job is already running.
$options = [ $options ];
}
- $fh = $options['fileHandle'] ?? null;
$options = $this->makeInsertOptions( $options );
if ( isset( $a[0] ) && is_array( $a[0] ) ) {
$sql .= '(' . $this->makeList( $a ) . ')';
}
- if ( $fh !== null && false === fwrite( $fh, $sql ) ) {
- return false;
- } elseif ( $fh !== null ) {
- return true;
- }
+ $this->query( $sql, $fname );
- return (bool)$this->query( $sql, $fname );
+ return true;
}
/**
$sql .= " WHERE " . $this->makeList( $conds, self::LIST_AND );
}
- return (bool)$this->query( $sql, $fname );
+ $this->query( $sql, $fname );
+
+ return true;
}
public function makeList( $a, $mode = self::LIST_COMMA ) {
* @param string $table Table name
* @param array|string $rows Row(s) to insert
* @param string $fname Caller function name
- *
- * @return ResultWrapper
*/
protected function nativeReplace( $table, $rows, $fname ) {
$table = $this->tableName( $table );
$sql .= '(' . $this->makeList( $row ) . ')';
}
- return $this->query( $sql, $fname );
+ $this->query( $sql, $fname );
}
public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
$this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
# Update any existing conflicting row(s)
if ( $where !== false ) {
- $ok = $this->update( $table, $set, $where, $fname );
+ $this->update( $table, $set, $where, $fname );
$affectedRowCount += $this->affectedRows();
- } else {
- $ok = true;
}
# Now insert any non-conflicting row(s)
- $ok = $this->insert( $table, $rows, $fname, [ 'IGNORE' ] ) && $ok;
+ $this->insert( $table, $rows, $fname, [ 'IGNORE' ] );
$affectedRowCount += $this->affectedRows();
$this->endAtomic( $fname );
$this->affectedRowCount = $affectedRowCount;
throw $e;
}
- return $ok;
+ return true;
}
public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
$sql .= ' WHERE ' . $conds;
}
- return $this->query( $sql, $fname );
+ $this->query( $sql, $fname );
+
+ return true;
}
final public function insertSelect(
if ( $this->cliMode && $this->isInsertSelectSafe( $insertOptions, $selectOptions ) ) {
// For massive migrations with downtime, we don't want to select everything
// into memory and OOM, so do all this native on the server side if possible.
- return $this->nativeInsertSelect(
+ $this->nativeInsertSelect(
+ $destTable,
+ $srcTable,
+ $varMap,
+ $conds,
+ $fname,
+ array_diff( $insertOptions, $hints ),
+ $selectOptions,
+ $selectJoinConds
+ );
+ } else {
+ $this->nonNativeInsertSelect(
$destTable,
$srcTable,
$varMap,
);
}
- return $this->nonNativeInsertSelect(
- $destTable,
- $srcTable,
- $varMap,
- $conds,
- $fname,
- array_diff( $insertOptions, $hints ),
- $selectOptions,
- $selectJoinConds
- );
+ return true;
}
/**
* @param array $insertOptions
* @param array $selectOptions
* @param array $selectJoinConds
- * @return bool
*/
protected function nonNativeInsertSelect( $destTable, $srcTable, $varMap, $conds,
$fname = __METHOD__,
$srcTable, implode( ',', $fields ), $conds, $fname, $selectOptions, $selectJoinConds
);
if ( !$res ) {
- return false;
+ return;
}
try {
} else {
$this->cancelAtomic( $fname );
}
- return $ok;
} catch ( Exception $e ) {
$this->cancelAtomic( $fname );
throw $e;
* @param array $insertOptions
* @param array $selectOptions
* @param array $selectJoinConds
- * @return bool
*/
protected function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds,
$fname = __METHOD__,
" INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ') ' .
$selectSql;
- return $this->query( $sql, $fname );
+ $this->query( $sql, $fname );
}
/**
throw $e;
}
$this->scrollableCursor = true;
+
+ return true;
}
/**
$this->ignoreDupKeyErrors = false;
- return $ret;
+ return true;
}
/**
* @param array $insertOptions
* @param array $selectOptions
* @param array $selectJoinConds
- * @return bool
* @throws Exception
*/
- public function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
+ protected function nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
$insertOptions = [], $selectOptions = [], $selectJoinConds = []
) {
$this->scrollableCursor = false;
try {
- $ret = parent::nativeInsertSelect(
+ parent::nativeInsertSelect(
$destTable,
$srcTable,
$varMap,
throw $e;
}
$this->scrollableCursor = true;
-
- return $ret;
}
/**
* @param array $uniqueIndexes
* @param array $rows
* @param string $fname
- * @return ResultWrapper
*/
public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
- return $this->nativeReplace( $table, $rows, $fname );
+ $this->nativeReplace( $table, $rows, $fname );
}
protected function isInsertSelectSafe( array $insertOptions, array $selectOptions ) {
* @param array|string $conds
* @param bool|string $fname
* @throws DBUnexpectedError
- * @return bool|ResultWrapper
*/
public function deleteJoin(
$delTable, $joinTable, $delVar, $joinVar, $conds, $fname = __METHOD__
$sql .= ' AND ' . $this->makeList( $conds, self::LIST_AND );
}
- return $this->query( $sql, $fname );
+ $this->query( $sql, $fname );
}
/**
$sql .= implode( ',', $rowTuples );
$sql .= " ON DUPLICATE KEY UPDATE " . $this->makeList( $set, self::LIST_SET );
- return (bool)$this->query( $sql, $fname );
+ $this->query( $sql, $fname );
+
+ return true;
}
/**
* @param array $insertOptions
* @param array $selectOptions
* @param array $selectJoinConds
- * @return bool
*/
- public function nativeInsertSelect(
+ protected function nativeInsertSelect(
$destTable, $srcTable, $varMap, $conds, $fname = __METHOD__,
$insertOptions = [], $selectOptions = [], $selectJoinConds = []
) {
$sql = "INSERT INTO $destTable (" . implode( ',', array_keys( $varMap ) ) . ') ' .
$selectSql . ' ON CONFLICT DO NOTHING';
- return $this->query( $sql, $fname );
+ $this->query( $sql, $fname );
} else {
// IGNORE and we don't have ON CONFLICT DO NOTHING, so just use the non-native version
- return $this->nonNativeInsertSelect(
+ $this->nonNativeInsertSelect(
$destTable, $srcTable, $varMap, $conds, $fname,
$insertOptions, $selectOptions, $selectJoinConds
);
}
+ } else {
+ parent::nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname,
+ $insertOptions, $selectOptions, $selectJoinConds );
}
-
- return parent::nativeInsertSelect( $destTable, $srcTable, $varMap, $conds, $fname,
- $insertOptions, $selectOptions, $selectJoinConds );
}
public function tableName( $name, $format = 'quoted' ) {
use PDO;
use PDOException;
+use Exception;
use LockManager;
use FSLockManager;
use InvalidArgumentException;
# SQLite can't handle multi-row inserts, so divide up into multiple single-row inserts
if ( isset( $a[0] ) && is_array( $a[0] ) ) {
- $ret = true;
- foreach ( $a as $v ) {
- if ( !parent::insert( $table, $v, "$fname/multi-row", $options ) ) {
- $ret = false;
+ $affectedRowCount = 0;
+ try {
+ $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
+ foreach ( $a as $v ) {
+ parent::insert( $table, $v, "$fname/multi-row", $options );
}
+ $this->endAtomic( $fname );
+ } catch ( Exception $e ) {
+ $this->cancelAtomic( $fname );
+ throw $e;
}
+ $this->affectedRowCount = $affectedRowCount;
} else {
- $ret = parent::insert( $table, $a, "$fname/single-row", $options );
+ parent::insert( $table, $a, "$fname/single-row", $options );
}
- return $ret;
+ return true;
}
/**
* @param array $uniqueIndexes Unused
* @param string|array $rows
* @param string $fname
- * @return bool|ResultWrapper
*/
function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
if ( !count( $rows ) ) {
- return true;
+ return;
}
# SQLite can't handle multi-row replaces, so divide up into multiple single-row queries
if ( isset( $rows[0] ) && is_array( $rows[0] ) ) {
- $ret = true;
- foreach ( $rows as $v ) {
- if ( !$this->nativeReplace( $table, $v, "$fname/multi-row" ) ) {
- $ret = false;
+ $affectedRowCount = 0;
+ try {
+ $this->startAtomic( $fname, self::ATOMIC_CANCELABLE );
+ foreach ( $rows as $v ) {
+ $this->nativeReplace( $table, $v, "$fname/multi-row" );
+ $affectedRowCount += $this->affectedRows();
}
+ $this->endAtomic( $fname );
+ } catch ( Exception $e ) {
+ $this->cancelAtomic( $fname );
+ throw $e;
}
+ $this->affectedRowCount = $affectedRowCount;
} else {
- $ret = $this->nativeReplace( $table, $rows, "$fname/single-row" );
+ $this->nativeReplace( $table, $rows, "$fname/single-row" );
}
-
- return $ret;
}
/**
* @param array $a Array of rows to insert
* @param string $fname Calling function name (use __METHOD__) for logs/profiling
* @param array $options Array of options
- *
- * @return bool
+ * @return bool Return true if no exception was thrown (deprecated since 1.33)
* @throws DBError
*/
public function insert( $table, $a, $fname = __METHOD__, $options = [] );
* @param array $options An array of UPDATE options, can be:
* - IGNORE: Ignore unique key conflicts
* - LOW_PRIORITY: MySQL-specific, see MySQL manual.
- * @return bool
+ * @return bool Return true if no exception was thrown (deprecated since 1.33)
* @throws DBError
*/
public function update( $table, $values, $conds, $fname = __METHOD__, $options = [] );
* things like "field = field + 1" or similar computed values.
* @param string $fname Calling function name (use __METHOD__) for logs/profiling
* @throws DBError
- * @return bool
+ * @return bool Return true if no exception was thrown (deprecated since 1.33)
*/
public function upsert(
$table, array $rows, array $uniqueIndexes, array $set, $fname = __METHOD__
* for the format. Use $conds == "*" to delete all rows
* @param string $fname Name of the calling function
* @throws DBUnexpectedError
- * @return bool|IResultWrapper
+ * @return bool Return true if no exception was thrown (deprecated since 1.33)
* @throws DBError
*/
public function delete( $table, $conds, $fname = __METHOD__ );
* @param array $selectJoinConds Join conditions for the SELECT part of the query, see
* IDatabase::select() for details.
*
- * @return bool
+ * @return bool Return true if no exception was thrown (deprecated since 1.33)
* @throws DBError
*/
public function insertSelect( $destTable, $srcTable, $varMap, $conds,
static function makeMsgId() {
global $wgSMTP, $wgServer;
- $msgid = uniqid( wfWikiID() . ".", true ); /* true required for cygwin */
+ $domainId = WikiMap::getCurrentWikiDomain()->getId();
+ $msgid = uniqid( $domainId . ".", true /** for cygwin */ );
if ( is_array( $wgSMTP ) && isset( $wgSMTP['IDHost'] ) && $wgSMTP['IDHost'] ) {
$domain = $wgSMTP['IDHost'];
} else {
return $keyspace;
}
- return wfWikiID();
+ return WikiMap::getCurrentWikiDomain()->getId();
}
/**
// Do not include the namespace since there can be multiple aliases to it
// due to different namespace text definitions on different wikis. This only
// means that some cache invalidations happen that are not strictly needed.
- $cache->makeGlobalKey( 'interwiki-page', wfWikiID(), $title->getDBkey() )
+ $cache->makeGlobalKey(
+ 'interwiki-page',
+ WikiMap::getCurrentWikiDomain()->getId(),
+ $title->getDBkey()
+ )
);
} );
}
*/
public function getProfileID() {
if ( $this->profileID === false ) {
- return wfWikiID();
+ return WikiMap::getCurrentWikiDomain()->getId();
} else {
return $this->profileID;
}
$packet['server_name'] = $wgServerName;
$packet['server_script_path'] = $wgScriptPath ?: '/';
- $packet['wiki'] = wfWikiID();
+ $packet['wiki'] = WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() );
return $this->formatArray( $packet );
}
return $newMessagesAlert;
}
- if ( count( $newtalks ) == 1 && $newtalks[0]['wiki'] === wfWikiID() ) {
+ if ( count( $newtalks ) == 1 && WikiMap::isCurrentWikiId( $newtalks[0]['wiki'] ) ) {
$uTalkTitle = $user->getTalkPage();
$lastSeenRev = $newtalks[0]['rev'] ?? null;
$nofAuthors = 0;
$parts = explode( $this->getConfig()->get( 'UserrightsInterwikiDelimiter' ), $username );
if ( count( $parts ) < 2 ) {
$name = trim( $username );
- $database = '';
+ $wikiId = '';
} else {
- list( $name, $database ) = array_map( 'trim', $parts );
+ list( $name, $wikiId ) = array_map( 'trim', $parts );
- if ( $database == wfWikiID() ) {
- $database = '';
+ if ( WikiMap::isCurrentWikiId( $wikiId ) ) {
+ $wikiId = '';
} else {
if ( $writing && !$this->getUser()->isAllowed( 'userrights-interwiki' ) ) {
return Status::newFatal( 'userrights-no-interwiki' );
}
- if ( !UserRightsProxy::validDatabase( $database ) ) {
- return Status::newFatal( 'userrights-nodatabase', $database );
+ if ( !UserRightsProxy::validDatabase( $wikiId ) ) {
+ return Status::newFatal( 'userrights-nodatabase', $wikiId );
}
}
}
// We'll do a lookup for the name internally.
$id = intval( substr( $name, 1 ) );
- if ( $database == '' ) {
+ if ( $wikiId == '' ) {
$name = User::whoIs( $id );
} else {
- $name = UserRightsProxy::whoIs( $database, $id );
+ $name = UserRightsProxy::whoIs( $wikiId, $id );
}
if ( !$name ) {
}
}
- if ( $database == '' ) {
+ if ( $wikiId == '' ) {
$user = User::newFromName( $name );
} else {
- $user = UserRightsProxy::newFromName( $database, $name );
+ $user = UserRightsProxy::newFromName( $wikiId, $name );
}
if ( !$user || $user->isAnon() ) {
}
// Easy case, we're checking locally
- if ( $wikiId === null || $wikiId === wfWikiID() ) {
+ if ( $wikiId === null || WikiMap::isCurrentWikiId( $wikiId ) ) {
return true;
}
$this->isAnon() ? [ 'user_ip' => $this->getName() ] : [ 'user_id' => $this->getId() ],
__METHOD__ );
$rev = $timestamp ? Revision::loadFromTimestamp( $dbr, $utp, $timestamp ) : null;
- return [ [ 'wiki' => wfWikiID(), 'link' => $utp->getLocalURL(), 'rev' => $rev ] ];
+ return [
+ [
+ 'wiki' => WikiMap::getWikiIdFromDomain( WikiMap::getCurrentWikiDomain() ),
+ 'link' => $utp->getLocalURL(),
+ 'rev' => $rev
+ ]
+ ];
}
/**
// and it is always for the same wiki, but we double-check here in
// case that changes some time in the future.
if ( count( $newMessageLinks ) === 1
- && $newMessageLinks[0]['wiki'] === wfWikiID()
+ && WikiMap::isCurrentWikiId( $newMessageLinks[0]['wiki'] )
&& $newMessageLinks[0]['rev']
) {
/** @var Revision $newMessageRevision */
* user rights manipulation.
*/
class UserRightsProxy {
+ /** @var IDatabase */
+ private $db;
+ /** @var string */
+ private $wikiId;
+ /** @var string */
+ private $name;
+ /** @var int */
+ private $id;
+ /** @var array */
+ private $newOptions;
/**
* @see newFromId()
* @see newFromName()
* @param IDatabase $db Db connection
- * @param string $database Database name
+ * @param string $wikiId Database name
* @param string $name User name
* @param int $id User ID
*/
- private function __construct( $db, $database, $name, $id ) {
+ private function __construct( $db, $wikiId, $name, $id ) {
$this->db = $db;
- $this->database = $database;
+ $this->wikiId = $wikiId;
$this->name = $name;
$this->id = intval( $id );
$this->newOptions = [];
}
- /**
- * Accessor for $this->database
- *
- * @return string Database name
- */
- public function getDBName() {
- return $this->database;
- }
-
/**
* Confirm the selected database name is a valid local interwiki database name.
*
- * @param string $database Database name
+ * @param string $wikiId Database name
* @return bool
*/
- public static function validDatabase( $database ) {
+ public static function validDatabase( $wikiId ) {
global $wgLocalDatabases;
- return in_array( $database, $wgLocalDatabases );
+ return in_array( $wikiId, $wgLocalDatabases );
}
/**
* Same as User::whoIs()
*
- * @param string $database Database name
+ * @param string $wikiId Database name
* @param int $id User ID
- * @param bool $ignoreInvalidDB If true, don't check if $database is in $wgLocalDatabases
+ * @param bool $ignoreInvalidDB If true, don't check if $wikiId is in $wgLocalDatabases
* @return string User name or false if the user doesn't exist
*/
- public static function whoIs( $database, $id, $ignoreInvalidDB = false ) {
- $user = self::newFromId( $database, $id, $ignoreInvalidDB );
+ public static function whoIs( $wikiId, $id, $ignoreInvalidDB = false ) {
+ $user = self::newFromId( $wikiId, $id, $ignoreInvalidDB );
if ( $user ) {
return $user->name;
} else {
/**
* Factory function; get a remote user entry by ID number.
*
- * @param string $database Database name
+ * @param string $wikiId Database name
* @param int $id User ID
- * @param bool $ignoreInvalidDB If true, don't check if $database is in $wgLocalDatabases
+ * @param bool $ignoreInvalidDB If true, don't check if $wikiId is in $wgLocalDatabases
* @return UserRightsProxy|null If doesn't exist
*/
- public static function newFromId( $database, $id, $ignoreInvalidDB = false ) {
- return self::newFromLookup( $database, 'user_id', intval( $id ), $ignoreInvalidDB );
+ public static function newFromId( $wikiId, $id, $ignoreInvalidDB = false ) {
+ return self::newFromLookup( $wikiId, 'user_id', intval( $id ), $ignoreInvalidDB );
}
/**
* Factory function; get a remote user entry by name.
*
- * @param string $database Database name
+ * @param string $wikiId Database name
* @param string $name User name
- * @param bool $ignoreInvalidDB If true, don't check if $database is in $wgLocalDatabases
+ * @param bool $ignoreInvalidDB If true, don't check if $wikiId is in $wgLocalDatabases
* @return UserRightsProxy|null If doesn't exist
*/
- public static function newFromName( $database, $name, $ignoreInvalidDB = false ) {
- return self::newFromLookup( $database, 'user_name', $name, $ignoreInvalidDB );
+ public static function newFromName( $wikiId, $name, $ignoreInvalidDB = false ) {
+ return self::newFromLookup( $wikiId, 'user_name', $name, $ignoreInvalidDB );
}
/**
- * @param string $database
+ * @param string $wikiId
* @param string $field
* @param string $value
* @param bool $ignoreInvalidDB
* @return null|UserRightsProxy
*/
- private static function newFromLookup( $database, $field, $value, $ignoreInvalidDB = false ) {
+ private static function newFromLookup( $wikiId, $field, $value, $ignoreInvalidDB = false ) {
global $wgSharedDB, $wgSharedTables;
// If the user table is shared, perform the user query on it,
// but don't pass it to the UserRightsProxy,
if ( $wgSharedDB && in_array( 'user', $wgSharedTables ) ) {
$userdb = self::getDB( $wgSharedDB, $ignoreInvalidDB );
} else {
- $userdb = self::getDB( $database, $ignoreInvalidDB );
+ $userdb = self::getDB( $wikiId, $ignoreInvalidDB );
}
- $db = self::getDB( $database, $ignoreInvalidDB );
+ $db = self::getDB( $wikiId, $ignoreInvalidDB );
if ( $db && $userdb ) {
$row = $userdb->selectRow( 'user',
__METHOD__ );
if ( $row !== false ) {
- return new UserRightsProxy( $db, $database,
- $row->user_name,
- intval( $row->user_id ) );
+ return new UserRightsProxy(
+ $db, $wikiId, $row->user_name, intval( $row->user_id ) );
}
}
return null;
* Open a database connection to work on for the requested user.
* This may be a new connection to another database for remote users.
*
- * @param string $database
- * @param bool $ignoreInvalidDB If true, don't check if $database is in $wgLocalDatabases
+ * @param string $wikiId
+ * @param bool $ignoreInvalidDB If true, don't check if $wikiId is in $wgLocalDatabases
* @return IDatabase|null If invalid selection
*/
- public static function getDB( $database, $ignoreInvalidDB = false ) {
- global $wgDBname;
- if ( $ignoreInvalidDB || self::validDatabase( $database ) ) {
- if ( $database == $wgDBname ) {
+ public static function getDB( $wikiId, $ignoreInvalidDB = false ) {
+ if ( $ignoreInvalidDB || self::validDatabase( $wikiId ) ) {
+ if ( WikiMap::isCurrentWikiId( $wikiId ) ) {
// Hmm... this shouldn't happen though. :)
return wfGetDB( DB_MASTER );
} else {
- return wfGetDB( DB_MASTER, [], $database );
+ return wfGetDB( DB_MASTER, [], $wikiId );
}
}
return null;
* @return string
*/
public function getName() {
- return $this->name . '@' . $this->database;
+ return $this->name . '@' . $this->wikiId;
}
/**
$this->assertEquals( $wiki, WikiMap::getWikiFromUrl( $url ) );
}
+ public function provideGetWikiIdFromDomain() {
+ return [
+ [ 'db-prefix', 'db-prefix' ],
+ [ wfWikiID(), wfWikiID() ],
+ [ new DatabaseDomain( 'db-dash', null, 'prefix' ), 'db-dash-prefix' ]
+ ];
+ }
+
/**
* @dataProvider provideGetWikiIdFromDomain
* @covers WikiMap::getWikiIdFromDomain()
$this->assertEquals( $wikiId, WikiMap::getWikiIdFromDomain( $domain ) );
}
- public function provideGetWikiIdFromDomain() {
+ /**
+ * @covers WikiMap::isCurrentWikiDomain()
+ * @covers WikiMap::getCurrentWikiDomain()
+ */
+ public function testIsCurrentWikiDomain() {
+ $this->assertTrue( WikiMap::isCurrentWikiDomain( wfWikiID() ) );
+
+ $localDomain = DatabaseDomain::newFromId( wfWikiID() );
+ $domain1 = new DatabaseDomain(
+ $localDomain->getDatabase(), 'someschema', $localDomain->getTablePrefix() );
+ $domain2 = new DatabaseDomain(
+ $localDomain->getDatabase(), null, $localDomain->getTablePrefix() );
+
+ $this->assertTrue( WikiMap::isCurrentWikiDomain( $domain1 ), 'Schema ignored' );
+ $this->assertTrue( WikiMap::isCurrentWikiDomain( $domain2 ), 'Schema ignored' );
+
+ $this->assertTrue( WikiMap::isCurrentWikiDomain( WikiMap::getCurrentWikiDomain() ) );
+ }
+
+ public function provideIsCurrentWikiId() {
return [
- [ 'db-prefix', 'db-prefix' ],
- [ wfWikiID(), wfWikiID() ],
- [ new DatabaseDomain( 'db-dash', null, 'prefix' ), 'db-dash-prefix' ]
+ [ 'db', 'db', null, '' ],
+ [ 'db','db', 'schema', '' ],
+ [ 'db-prefix', 'db', null, 'prefix' ],
+ [ 'db-prefix', 'db', 'schema', 'prefix' ],
+ // Bad hyphen cases (best effort support)
+ [ 'db-stuff', 'db-stuff', null, '' ],
+ [ 'db-stuff-prefix', 'db-stuff', null, 'prefix' ]
];
}
+
+ /**
+ * @dataProvider provideIsCurrentWikiId
+ * @covers WikiMap::isCurrentWikiId()
+ * @covers WikiMap::getCurrentWikiDomain()
+ * @covers WikiMap::getWikiIdFromDomain()
+ */
+ public function testIsCurrentWikiId( $wikiId, $db, $schema, $prefix ) {
+ $this->setMwGlobals(
+ [ 'wgDBname' => $db, 'wgDBmwschema' => $schema, 'wgDBprefix' => $prefix ] );
+
+ $this->assertTrue( WikiMap::isCurrentWikiId( $wikiId ), "ID matches" );
+ $this->assertNotTrue( WikiMap::isCurrentWikiId( $wikiId . '-more' ), "Bogus ID" );
+ }
}
// Redeclare parent method to make it public
public function nativeReplace( $table, $rows, $fname ) {
- return parent::nativeReplace( $table, $rows, $fname );
+ parent::nativeReplace( $table, $rows, $fname );
}
function getType() {