MediaWiki 1.29 requires PHP 5.5.9 or later. There is experimental support for
HHVM 3.6.5 or later.
-MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
-support for them is somewhat less mature. There is experimental support for
+MySQL/MariaDB is the recommended DBMS. PostgreSQL or SQLite can also be used,
+but support for them is somewhat less mature. There is experimental support for
Oracle and Microsoft SQL Server.
The supported versions are:
table, the schema update may take quite long (minutes on a medium sized site,
many hours on a large site).
-If upgrading from before 1.11, and you are using a wiki as a commons
-repository, make sure that it is updated as well. Otherwise, errors may arise
-due to database schema changes.
-
-If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
-new database fields are filled with data.
-
-If you are upgrading from MediaWiki 1.4.x or earlier, you should upgrade to
-1.5 first. The upgrade script maintenance/upgrade1_5.php has been removed
-with MediaWiki 1.21.
-
Don't forget to always back up your database before upgrading!
-See the file UPGRADE for more detailed upgrade instructions.
+See the file UPGRADE for more detailed upgrade instructions, including
+important information when upgrading from versions prior to 1.11.
For notes on 1.28.x and older releases, see HISTORY.
script. This will update the searchindex table for those pages that
contain double-byte latin characters.
+== Upgrading from 1.10 or earlier ==
+
+If upgrading from before 1.11, and you are using a wiki as a commons
+repository, make sure that it is updated as well. Otherwise, errors may arise
+due to database schema changes.
+
== Upgrading from 1.8 or earlier ==
MediaWiki 1.9 and later no longer keep default localized message text
$wgLocalTZoffset was in hours, it is now using minutes.
+If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
+new database fields are filled with data.
+
== Upgrading from 1.5 or earlier ==
Major changes have been made to the schema from 1.4.x. The updater
'upgrade1_5.php', can do this -- run it prior to 'update.php' or
the web upgrader.
-NOTE that upgrade1_5.php does not work properly with recent version
+NOTE that upgrade1_5.php does not work properly with recent versions
of MediaWiki. If upgrading a 1.4.x wiki, you should upgrade to 1.5
first. upgrade1_5.php has been removed from MediaWiki 1.21.
'SVGReader' => __DIR__ . '/includes/media/SVGMetadataExtractor.php',
'SamplingStatsdClient' => __DIR__ . '/includes/libs/stats/SamplingStatsdClient.php',
'Sanitizer' => __DIR__ . '/includes/Sanitizer.php',
- 'SavepointPostgres' => __DIR__ . '/includes/libs/rdbms/database/utils/SavepointPostgres.php',
'ScopedCallback' => __DIR__ . '/includes/compat/ScopedCallback.php',
'ScopedLock' => __DIR__ . '/includes/libs/lockmanager/ScopedLock.php',
'SearchApi' => __DIR__ . '/includes/api/SearchApi.php',
'Wikimedia\\Rdbms\\ConnectionManager' => __DIR__ . '/includes/libs/rdbms/connectionmanager/ConnectionManager.php',
'Wikimedia\\Rdbms\\DBConnRef' => __DIR__ . '/includes/libs/rdbms/database/DBConnRef.php',
'Wikimedia\\Rdbms\\DBMasterPos' => __DIR__ . '/includes/libs/rdbms/database/position/DBMasterPos.php',
+ 'Wikimedia\\Rdbms\\Database' => __DIR__ . '/includes/libs/rdbms/database/Database.php',
'Wikimedia\\Rdbms\\DatabaseDomain' => __DIR__ . '/includes/libs/rdbms/database/DatabaseDomain.php',
+ 'Wikimedia\\Rdbms\\DatabaseMssql' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMssql.php',
+ 'Wikimedia\\Rdbms\\DatabaseMysql' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysql.php',
+ 'Wikimedia\\Rdbms\\DatabaseMysqlBase' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqlBase.php',
+ 'Wikimedia\\Rdbms\\DatabaseMysqli' => __DIR__ . '/includes/libs/rdbms/database/DatabaseMysqli.php',
+ 'Wikimedia\\Rdbms\\DatabasePostgres' => __DIR__ . '/includes/libs/rdbms/database/DatabasePostgres.php',
+ 'Wikimedia\\Rdbms\\DatabaseSqlite' => __DIR__ . '/includes/libs/rdbms/database/DatabaseSqlite.php',
'Wikimedia\\Rdbms\\FakeResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/FakeResultWrapper.php',
'Wikimedia\\Rdbms\\Field' => __DIR__ . '/includes/libs/rdbms/field/Field.php',
'Wikimedia\\Rdbms\\IBlob' => __DIR__ . '/includes/libs/rdbms/encasing/IBlob.php',
'Wikimedia\\Rdbms\\PostgresField' => __DIR__ . '/includes/libs/rdbms/field/PostgresField.php',
'Wikimedia\\Rdbms\\ResultWrapper' => __DIR__ . '/includes/libs/rdbms/database/resultwrapper/ResultWrapper.php',
'Wikimedia\\Rdbms\\SQLiteField' => __DIR__ . '/includes/libs/rdbms/field/SQLiteField.php',
+ 'Wikimedia\\Rdbms\\SavepointPostgres' => __DIR__ . '/includes/libs/rdbms/database/utils/SavepointPostgres.php',
'Wikimedia\\Rdbms\\SessionConsistentConnectionManager' => __DIR__ . '/includes/libs/rdbms/connectionmanager/SessionConsistentConnectionManager.php',
'Wikimedia\\Rdbms\\TransactionProfiler' => __DIR__ . '/includes/libs/rdbms/TransactionProfiler.php',
'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
* @file
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\MediaWikiServices;
* @todo Replace calls to wfGetDB with calls to LoadBalancer::getConnection()
* on an injected instance of LoadBalancer.
*
- * @return Database
+ * @return \Wikimedia\Rdbms\Database
*/
function wfGetDB( $db, $groups = [], $wiki = false ) {
return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
$overwriteMessage = wfMessage(
'delete_and_move_reason',
$this->oldTitle->getPrefixedText()
- )->text();
+ )->inContentLanguage()->text();
$newpage = WikiPage::factory( $nt );
$errs = [];
$status = $newpage->doDeleteArticleReal(
/** @var array Profiling data */
private $limitReportJSData = [];
+ /**
+ * Link: header contents
+ */
+ private $mLinkHeader = [];
+
/**
* Constructor for OutputPage. This should not be called directly.
* Instead a new RequestContext should be created and it will implicitly create
return 'Vary: ' . implode( ', ', array_keys( $this->mVaryHeader ) );
}
+ /**
+ * Add an HTTP Link: header
+ *
+ * @param string $header Header value
+ */
+ public function addLinkHeader( $header ) {
+ $this->mLinkHeader[] = $header;
+ }
+
+ /**
+ * Return a Link: header. Based on the values of $mLinkHeader.
+ *
+ * @return string
+ */
+ public function getLinkHeader() {
+ if ( !$this->mLinkHeader ) {
+ return false;
+ }
+
+ return 'Link: ' . implode( ',', $this->mLinkHeader );
+ }
+
/**
* Get a complete Key header
*
// jQuery etc. can work correctly.
$response->header( 'X-UA-Compatible: IE=Edge' );
+ $this->addLogoPreloadLinkHeaders();
+ $linkHeader = $this->getLinkHeader();
+ if ( $linkHeader ) {
+ $response->header( $linkHeader );
+ }
+
// Prevent framing, if requested
$frameOptions = $this->getFrameOptions();
if ( $frameOptions ) {
'mediawiki.widgets.styles',
] );
}
+
+ /**
+ * Add Link headers for preloading the wiki's logo.
+ *
+ * @since 1.26
+ */
+ protected function addLogoPreloadLinkHeaders() {
+ $logo = $this->getConfig()->get( 'Logo' ); // wgLogo
+ $logoHD = $this->getConfig()->get( 'LogoHD' ); // wgLogoHD
+
+ $tags = [];
+ $logosPerDppx = [];
+ $logos = [];
+
+ $logosPerDppx['1.0'] = $logo;
+
+ if ( !$logoHD ) {
+ // No media queries required if we only have one variant
+ $this->addLinkHeader( '<' . $logo . '>;rel=preload;as=image' );
+ return;
+ }
+
+ foreach ( $logoHD as $dppx => $src ) {
+ // Only 1.5x and 2x are supported
+ // Note: Keep in sync with ResourceLoaderSkinModule
+ if ( in_array( $dppx, [ '1.5x', '2x' ] ) ) {
+ // LogoHD uses a string in this format: "1.5x"
+ $dppx = substr( $dppx, 0, -1 );
+ $logosPerDppx[$dppx] = $src;
+ }
+ }
+
+ // Because PHP can't have floats as array keys
+ uksort( $logosPerDppx, function ( $a , $b ) {
+ $a = floatval( $a );
+ $b = floatval( $b );
+
+ if ( $a == $b ) {
+ return 0;
+ }
+ // Sort from smallest to largest (e.g. 1x, 1.5x, 2x)
+ return ( $a < $b ) ? -1 : 1;
+ } );
+
+ foreach ( $logosPerDppx as $dppx => $src ) {
+ $logos[] = [ 'dppx' => $dppx, 'src' => $src ];
+ }
+
+ $logosCount = count( $logos );
+ // Logic must match ResourceLoaderSkinModule:
+ // - 1x applies to resolution < 1.5dppx
+ // - 1.5x applies to resolution >= 1.5dppx && < 2dppx
+ // - 2x applies to resolution >= 2dppx
+ // Note that min-resolution and max-resolution are both inclusive.
+ for ( $i = 0; $i < $logosCount; $i++ ) {
+ if ( $i === 0 ) {
+ // Smallest dppx
+ // min-resolution is ">=" (larger than or equal to)
+ // "not min-resolution" is essentially "<"
+ $media_query = 'not all and (min-resolution: ' . $logos[ 1 ]['dppx'] . 'dppx)';
+ } elseif ( $i !== $logosCount - 1 ) {
+ // In between
+ // Media query expressions can only apply "not" to the entire expression
+ // (e.g. can't express ">= 1.5 and not >= 2).
+ // Workaround: Use <= 1.9999 in place of < 2.
+ $upper_bound = floatval( $logos[ $i + 1 ]['dppx'] ) - 0.000001;
+ $media_query = '(min-resolution: ' . $logos[ $i ]['dppx'] .
+ 'dppx) and (max-resolution: ' . $upper_bound . 'dppx)';
+ } else {
+ // Largest dppx
+ $media_query = '(min-resolution: ' . $logos[ $i ]['dppx'] . 'dppx)';
+ }
+
+ $this->addLinkHeader(
+ '<' . $logos[$i]['src'] . '>;rel=preload;as=image;media=' . $media_query
+ );
+ }
+ }
}
* @file
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
'rb' => $common,
'rp' => $common,
'rt' => $common, # array_merge( $common, array( 'rbspan' ) ),
- 'rtc' => $common,
+ 'rtc' => $common,
# MathML root element, where used for extensions
# 'title' may not be 100% valid here; it's XHTML
* @file
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
* @file
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\Interwiki\InterwikiLookup;
}
/**
- * Whether the magic words __INDEX__ and __NOINDEX__ function for this page.
+ * Whether the magic words __INDEX__ and __NOINDEX__ function for this page.
*
* @return bool
*/
}
if ( array_key_exists( 'rcTypes', $options ) ) {
- $conds['rc_type'] = array_map( 'intval', $options['rcTypes'] );
+ $conds['rc_type'] = array_map( 'intval', $options['rcTypes'] );
}
$conds = array_merge(
}
if ( isset( $options['filter'] ) ) {
$filter = $options['filter'];
- if ( $filter === self::FILTER_CHANGED ) {
+ if ( $filter === self::FILTER_CHANGED ) {
$conds[] = 'wl_notificationtimestamp IS NOT NULL';
} else {
$conds[] = 'wl_notificationtimestamp IS NULL';
class WikiReference {
private $mCanonicalServer; ///< canonical server URL, e.g. 'https://www.mediawiki.org'
private $mServer; ///< server URL, may be protocol-relative, e.g. '//www.mediawiki.org'
- private $mPath; ///< path, '/wiki/$1'
+ private $mPath; ///< path, '/wiki/$1'
/**
* @param string $canonicalServer
*/
use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\Database;
/**
* Displays information about a page.
/**
* Gets a default replica DB connection object
- * @return Database
+ * @return IDatabase
*/
protected function getDB() {
if ( !isset( $this->mSlaveDB ) ) {
*/
use MediaWiki\MediaWikiServices;
+use Wikimedia\Rdbms\Database;
/**
* @ingroup API
/**
* Get the database connection (read-only)
- * @return Database
+ * @return IDatabase
*/
protected function getDB() {
return $this->mDbSource->getDB();
* @file
*/
+use Wikimedia\Rdbms\IDatabase;
+
/**
* This is the main query class. It behaves similar to ApiMain: based on the
* parameters given, it will create a list of titles to work on (an ApiPageSet
* @param string $name Name to assign to the database connection
* @param int $db One of the DB_* constants
* @param array $groups Query groups
- * @return Database
+ * @return IDatabase
*/
public function getNamedDB( $name, $db, $groups ) {
if ( !array_key_exists( $name, $this->mNamedDB ) ) {
* @file
*/
+use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\ResultWrapper;
/**
/**
* Get the Query database connection (read-only)
- * @return Database
+ * @return IDatabase
*/
protected function getDB() {
if ( is_null( $this->mDb ) ) {
* @param string $name Name to assign to the database connection
* @param int $db One of the DB_* constants
* @param array $groups Query groups
- * @return Database
+ * @return IDatabase
*/
public function selectNamedDB( $name, $db, $groups ) {
$this->mDb = $this->getQuery()->getNamedDB( $name, $db, $groups );
$throttleKey = wfGlobalCacheKey( 'throttler', $this->type, $index, $ipKey, $userKey );
$throttleCount = $this->cache->get( $throttleKey );
- if ( !$throttleCount ) { // counter not started yet
+ if ( !$throttleCount ) { // counter not started yet
$this->cache->add( $throttleKey, 1, $expiry );
} elseif ( $throttleCount < $count ) { // throttle limited not yet reached
$this->cache->incr( $throttleKey );
/**
* Get the replica DB connection to the database
* When non existing, will initialize the connection.
- * @return Database
+ * @return IDatabase
*/
protected function getDB() {
if ( !isset( $this->db ) ) {
* @file
* @ingroup Cache
*/
+
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
+use Wikimedia\Rdbms\Database;
/**
* This class generates message blobs for use by ResourceLoader modules.
use MediaWiki\MediaWikiServices;
use Wikimedia\ScopedCallback;
use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\Database;
/**
* MediaWiki message cache structure version.
'rc_logid' => 0,
'rc_log_type' => null,
'rc_log_action' => '',
- 'rc_params' => serialize( [
+ 'rc_params' => serialize( [
'hidden-cat' => WikiCategoryPage::factory( $categoryTitle )->isHidden()
] )
];
* @ingroup Change tagging
*/
+use Wikimedia\Rdbms\Database;
+
class ChangeTags {
/**
* Can't delete tags with more than this many uses. Similar in intent to
<?php
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\LoadBalancer;
/**
* @file
* @ingroup Database
*/
+
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\Blob;
use Wikimedia\Rdbms\ResultWrapper;
$deleteWheres = []; // list of WHERE clause arrays for each DB delete() call
if ( $table === 'pagelinks' || $table === 'templatelinks' || $table === 'iwlinks' ) {
- $baseKey = ( $table === 'iwlinks' ) ? 'iwl_prefix' : "{$prefix}_namespace";
+ $baseKey = ( $table === 'iwlinks' ) ? 'iwl_prefix' : "{$prefix}_namespace";
$curBatchSize = 0;
$curDeletionBatch = [];
* @ingroup FileRepo
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
*/
use Wikimedia\Rdbms\ResultWrapper;
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
* @ingroup FileAbstraction
*/
-use \MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Logger\LoggerFactory;
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
$error = array_merge( [ $error['message'] ], $error['params'] );
}
}
- } elseif ( $elementsType === 'errors' ) {
+ } elseif ( $elementsType === 'errors' ) {
$errors = $elements;
if ( !is_array( $errors ) ) {
$errors = [ $errors ];
$this->parseHeader();
$this->setStatus();
- return Status::wrap( $this->status ); // TODO B/C; move this to callers
+ return Status::wrap( $this->status ); // TODO B/C; move this to callers
}
/**
* @ingroup Deployment
*/
use Wikimedia\Rdbms\LBFactorySingle;
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
* @file
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use MediaWiki\MediaWikiServices;
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\Database;
+
/**
* Class for setting up the MediaWiki database using Microsoft SQL Server.
*
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\DatabaseMssql;
+
/**
* Class for setting up the MediaWiki database using Microsoft SQL Server.
*
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\Database;
+
/**
* Class for setting up the MediaWiki database using MySQL.
*
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\Database;
+
/**
* Class for setting up the MediaWiki database using Postgres.
*
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\DatabasePostgres;
+
/**
* Class for handling updates to Postgres databases.
*
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\DatabaseSqlite;
+
/**
* Class for setting up the MediaWiki database using SQLLite.
*
* @ingroup Deployment
*/
+use Wikimedia\Rdbms\DatabaseSqlite;
+
/**
* Class for handling updates to Sqlite databases.
*
*/
use \Cdb\Exception as CdbException;
use \Cdb\Reader as CdbReader;
-use Database;
+use Wikimedia\Rdbms\Database;
use Hooks;
use Interwiki;
use Language;
* @since 1.26
*/
public function lazyPush( $jobs ) {
- if ( PHP_SAPI === 'cli' ) {
+ if ( PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg' ) {
$this->push( $jobs );
return;
}
* @ingroup LockManager
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
namespace Wikimedia\Rdbms;
-use Database;
use InvalidArgumentException;
/**
namespace Wikimedia\Rdbms;
-use Database;
-
/**
* Database connection manager.
*
namespace Wikimedia\Rdbms;
-use Database;
use InvalidArgumentException;
/**
}
}
-class_alias( 'Wikimedia\Rdbms\DBConnRef', 'DBConnRef' );
+class_alias( DBConnRef::class, 'DBConnRef' );
* @file
* @ingroup Database
*/
+namespace Wikimedia\Rdbms;
+
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerInterface;
use Wikimedia\ScopedCallback;
-use Wikimedia\Rdbms\TransactionProfiler;
-use Wikimedia\Rdbms\LikeMatch;
-use Wikimedia\Rdbms\DatabaseDomain;
-use Wikimedia\Rdbms\ResultWrapper;
-use Wikimedia\Rdbms\DBMasterPos;
-use Wikimedia\Rdbms\Blob;
use Wikimedia\Timestamp\ConvertibleTimestamp;
-use Wikimedia\Rdbms\IDatabase;
-use Wikimedia\Rdbms\IMaintainableDatabase;
+use MediaWiki;
+use BagOStuff;
+use HashBagOStuff;
+use InvalidArgumentException;
+use DBQueryError;
+use DBUnexpectedError;
+use DBConnectionError;
+use DBReadOnlyError;
+use Exception;
+use RuntimeException;
/**
* Relational database abstraction object
'oracle' => [],
'mssql' => [],
];
+ static $classAliases = [
+ 'DatabaseMssql' => DatabaseMssql::class,
+ 'DatabaseMysql' => DatabaseMysql::class,
+ 'DatabaseMysqli' => DatabaseMysqli::class,
+ 'DatabaseSqlite' => DatabaseSqlite::class,
+ 'DatabasePostgres' => DatabasePostgres::class
+ ];
$driver = false;
$dbType = strtolower( $dbType );
} else {
$driver = $dbType;
}
+
if ( $driver === false || $driver === '' ) {
throw new InvalidArgumentException( __METHOD__ .
" no viable database extension found for type '$dbType'" );
}
$class = 'Database' . ucfirst( $driver );
+ if ( isset( $classAliases[$class] ) ) {
+ $class = $classAliases[$class];
+ }
+
if ( class_exists( $class ) && is_subclass_of( $class, IDatabase::class ) ) {
// Resolve some defaults for b/c
$p['host'] = isset( $p['host'] ) ? $p['host'] : false;
}
}
-class_alias( Database::class, 'DatabaseBase' );
+class_alias( Database::class, 'DatabaseBase' ); // b/c for old name
+class_alias( Database::class, 'Database' ); // b/c global alias
* @author Ryan Biesemeyer <v-ryanbi at microsoft dot com>
* @author Ryan Schmidt <skizzerz at gmail dot com>
*/
-use Wikimedia\Rdbms\Blob;
-use Wikimedia\Rdbms\MssqlBlob;
-use Wikimedia\Rdbms\MssqlField;
-use Wikimedia\Rdbms\ResultWrapper;
-use Wikimedia\Rdbms\MssqlResultWrapper;
+
+namespace Wikimedia\Rdbms;
+
+use MediaWiki;
+use DBConnectionError;
+use DBUnexpectedError;
+use DBQueryError;
+use Exception;
+use stdClass;
/**
* @ingroup Database
return $old;
}
}
+
+class_alias( DatabaseMssql::class, 'DatabaseMssql' );
* @file
* @ingroup Database
*/
+namespace Wikimedia\Rdbms;
+
+use DBConnectionError;
/**
* Database abstraction object for PHP extension mysql.
return mysql_real_escape_string( $s, $conn );
}
}
+
+class_alias( DatabaseMysql::class, 'DatabaseMysql' );
* @file
* @ingroup Database
*/
-use Wikimedia\Rdbms\DBMasterPos;
-use Wikimedia\Rdbms\MySQLMasterPos;
-use Wikimedia\Rdbms\MySQLField;
-use Wikimedia\Rdbms\ResultWrapper;
+namespace Wikimedia\Rdbms;
+
+use DateTime;
+use DateTimeZone;
+use MediaWiki;
+use InvalidArgumentException;
+use DBError;
+use DBExpectedError;
+use DBUnexpectedError;
+use DBConnectionError;
+use Exception;
+use stdClass;
/**
* Database abstraction object for MySQL.
}
}
}
+
+class_alias( DatabaseMysqlBase::class, 'DatabaseMysqlBase' );
* @file
* @ingroup Database
*/
+namespace Wikimedia\Rdbms;
-use Wikimedia\Rdbms\ResultWrapper;
+use mysqli;
+use DBConnectionError;
+use IP;
/**
* Database abstraction object for PHP extension mysqli.
}
/**
- * @param resource|ResultWrapper $res
+ * @param mysqli $res
* @param int $n
* @return mixed
*/
}
/**
- * @param resource|ResultWrapper $res
+ * @param mysqli $res
* @param int $n
* @return mixed
*/
}
/**
- * @param resource|ResultWrapper $res
+ * @param mysqli $res
* @param int $row
* @return mixed
*/
}
}
}
+
+class_alias( DatabaseMysqli::class, 'DatabaseMysqli' );
* @file
* @ingroup Database
*/
+namespace Wikimedia\Rdbms;
+
use Wikimedia\Timestamp\ConvertibleTimestamp;
use Wikimedia\WaitConditionLoop;
-use Wikimedia\Rdbms\Blob;
-use Wikimedia\Rdbms\PostgresBlob;
-use Wikimedia\Rdbms\PostgresField;
-use Wikimedia\Rdbms\ResultWrapper;
+use MediaWiki;
+use DBUnexpectedError;
+use DBConnectionError;
+use Exception;
/**
* @ingroup Database
*
* @since 1.19
* @param string $text Postgreql array returned in a text form like {a,b}
- * @param string $output
+ * @param string[] $output
* @param int|bool $limit
* @param int $offset
- * @return string
+ * @return string[]
*/
private function pg_array_parse( $text, &$output, $limit = false, $offset = 1 ) {
if ( false === $limit ) {
* @return string Integer
*/
private function bigintFromLockName( $lockName ) {
- return Wikimedia\base_convert( substr( sha1( $lockName ), 0, 15 ), 16, 10 );
+ return \Wikimedia\base_convert( substr( sha1( $lockName ), 0, 15 ), 16, 10 );
}
}
+
+class_alias( DatabasePostgres::class, 'DatabasePostgres' );
* @file
* @ingroup Database
*/
-use Wikimedia\Rdbms\Blob;
-use Wikimedia\Rdbms\SQLiteField;
-use Wikimedia\Rdbms\ResultWrapper;
+namespace Wikimedia\Rdbms;
+
+use PDO;
+use PDOException;
+use LockManager;
+use FSLockManager;
+use DBConnectionError;
+use DBReadOnlyError;
+use InvalidArgumentException;
+use RuntimeException;
+use DBError;
+use stdClass;
/**
* @ingroup Database
$p['dbFilePath'] = $filename;
$p['schema'] = false;
$p['tablePrefix'] = '';
+ /** @var DatabaseSqlite $db */
+ $db = Database::factory( 'sqlite', $p );
- return Database::factory( 'sqlite', $p );
+ return $db;
}
/**
return 'SQLite ' . (string)$this->mConn->getAttribute( PDO::ATTR_SERVER_VERSION );
}
}
+
+class_alias( DatabaseSqlite::class, 'DatabaseSqlite' );
+
public function setTableAliases( array $aliases );
}
-class_alias( 'Wikimedia\Rdbms\IDatabase', 'IDatabase' );
+class_alias( IDatabase::class, 'IDatabase' );
public function unlockTables( $method );
}
-class_alias( 'Wikimedia\Rdbms\IMaintainableDatabase', 'IMaintainableDatabase' );
+class_alias( IMaintainableDatabase::class, 'IMaintainableDatabase' );
}
}
-class_alias( 'Wikimedia\Rdbms\MaintainableDBConnRef', 'MaintainableDBConnRef' );
+class_alias( MaintainableDBConnRef::class, 'MaintainableDBConnRef' );
* @file
* @ingroup Database
*/
+
+namespace Wikimedia\Rdbms;
+
use Psr\Log\LoggerInterface;
/**
* @file
* @ingroup Database
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
namespace Wikimedia\Rdbms;
-use DatabasePostgres;
-
class PostgresField implements Field {
private $name, $tablename, $type, $nullable, $max_length, $deferred, $deferrable, $conname,
$has_default, $default;
*/
namespace Wikimedia\Rdbms;
-use Database;
use DBError;
use DBAccessError;
use DBTransactionError;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Wikimedia\ScopedCallback;
-use Database;
use BagOStuff;
use EmptyBagOStuff;
use WANObjectCache;
class LoadBalancer implements ILoadBalancer {
/** @var array[] Map of (server index => server config array) */
private $mServers;
- /** @var \Database[][][] Map of local/foreignUsed/foreignFree => server index => IDatabase array */
+ /** @var Database[][][] Map of local/foreignUsed/foreignFree => server index => IDatabase array */
private $mConns;
/** @var float[] Map of (server index => weight) */
private $mLoads;
/** @var LoggerInterface */
protected $perfLogger;
- /** @var \Database Database connection that caused a problem */
+ /** @var Database DB connection object that caused a problem */
private $errorConnection;
/** @var integer The generic (not query grouped) replica DB index (of $mServers) */
private $mReadIndex;
* @ingroup Cache
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
use \MediaWiki\MediaWikiServices;
use \Wikimedia\WaitConditionLoop;
$styles['all'][] = '.mw-wiki-logo { background-image: ' .
CSSMin::buildUrlValue( $logo1 ) .
'; }';
+ // Only 1.5x and 2x are supported
+ // Note: Keep in sync with OutputPage::addLogoPreloadLinkHeaders()
if ( $logoHD ) {
if ( isset( $logoHD['1.5x'] ) ) {
$styles[
* @author Roan Kattouw
*/
+use Wikimedia\Rdbms\Database;
use Wikimedia\Rdbms\IDatabase;
/**
},
'cssClassSuffix' => 'patrolled',
'isRowApplicableCallable' => function ( $ctx, $rc ) {
- return $rc->getAttribute( 'rc_patrolled' );
+ return $rc->getAttribute( 'rc_patrolled' );
},
],
[
},
'cssClassSuffix' => 'unpatrolled',
'isRowApplicableCallable' => function ( $ctx, $rc ) {
- return !$rc->getAttribute( 'rc_patrolled' );
+ return !$rc->getAttribute( 'rc_patrolled' );
},
],
],
&$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedExpLevels ) {
global $wgLearnerEdits,
- $wgExperiencedUserEdits,
- $wgLearnerMemberSince,
- $wgExperiencedUserMemberSince;
+ $wgExperiencedUserEdits,
+ $wgLearnerMemberSince,
+ $wgExperiencedUserMemberSince;
$LEVEL_COUNT = 3;
);
if ( $selectedExpLevels === [ 'newcomer' ] ) {
- $conds[] = "NOT ( $aboveNewcomer )";
+ $conds[] = "NOT ( $aboveNewcomer )";
} elseif ( $selectedExpLevels === [ 'learner' ] ) {
$conds[] = $dbr->makeList(
[ $aboveNewcomer, "NOT ( $aboveLearner )" ],
'text',
[
'id' => 'emailusertarget',
- 'class' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
+ 'class' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
'autofocus' => true,
'size' => 30,
]
*/
protected function transformFilterDefinition( array $filterDefinition ) {
if ( isset( $filterDefinition['showHideSuffix'] ) ) {
- $filterDefinition['showHide'] = 'wl' . $filterDefinition['showHideSuffix'];
+ $filterDefinition['showHide'] = 'wl' . $filterDefinition['showHideSuffix'];
}
return $filterDefinition;
*/
use MediaWiki\Session\BotPasswordSessionProvider;
+use Wikimedia\Rdbms\IMaintainableDatabase;
/**
* Utility class for bot passwords
/**
* Get a database connection for the bot passwords database
* @param int $db Index of the connection to get, e.g. DB_MASTER or DB_REPLICA.
- * @return Database
+ * @return IMaintainableDatabase
*/
public static function getDB( $db ) {
global $wgBotPasswordsCluster, $wgBotPasswordsDatabase;
use MediaWiki\Auth\AuthenticationResponse;
use MediaWiki\Auth\AuthenticationRequest;
use Wikimedia\ScopedCallback;
+use Wikimedia\Rdbms\Database;
/**
* String Some punctuation to prevent editing from broken text-mangling proxies.
epcoordinator
epinstructor
eponline
-eqiad
erevoke
errno
error
wmlsc
wmlscript
wmlscriptc
-wmnet
wordcount
wordprocessingml
wordwg
require_once __DIR__ . '/Maintenance.php';
use Wikimedia\Rdbms\IMaintainableDatabase;
+use Wikimedia\Rdbms\DatabaseSqlite;
/**
* Maintenance script that rebuilds search index table from scratch.
* @ingroup Maintenance
*/
+use Wikimedia\Rdbms\DatabaseSqlite;
+
/**
* This class contains code common to different SQLite-related maintenance scripts
*
* @member mw.hook
*/
$( function () {
- var loading = $.grep( mw.loader.getModuleNames(), function ( module ) {
+ var loading, modules;
+
+ modules = $.grep( mw.loader.getModuleNames(), function ( module ) {
return mw.loader.getState( module ) === 'loading';
} );
// We only need a callback, not any actual module. First try a single using()
// for all loading modules. If one fails, fall back to tracking each module
// separately via $.when(), this is expensive.
- loading = mw.loader.using( loading ).then( null, function () {
- var all = loading.map( function ( module ) {
+ loading = mw.loader.using( modules ).then( null, function () {
+ var all = modules.map( function ( module ) {
return mw.loader.using( module ).then( null, function () {
return $.Deferred().resolve();
} );
$this->assertEquals( [ 0 => 'Test' ], $outputPage->getCategories( 'hidden' ) );
}
+ /**
+ * @dataProvider provideLinkHeaders
+ * @covers OutputPage::addLinkHeader
+ * @covers OutputPage::getLinkHeader
+ */
+ public function testLinkHeaders( $headers, $result ) {
+ $outputPage = $this->newInstance();
+
+ foreach ( $headers as $header ) {
+ $outputPage->addLinkHeader( $header );
+ }
+
+ $this->assertEquals( $result, $outputPage->getLinkHeader() );
+ }
+
+ public function provideLinkHeaders() {
+ return [
+ [
+ [],
+ false
+ ],
+ [
+ [ '<https://foo/bar.jpg>;rel=preload;as=image' ],
+ 'Link: <https://foo/bar.jpg>;rel=preload;as=image',
+ ],
+ [
+ [ '<https://foo/bar.jpg>;rel=preload;as=image','<https://foo/baz.jpg>;rel=preload;as=image' ],
+ 'Link: <https://foo/bar.jpg>;rel=preload;as=image,<https://foo/baz.jpg>;rel=preload;as=image',
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider providePreloadLinkHeaders
+ * @covers OutputPage::addLogoPreloadLinkHeaders
+ */
+ public function testPreloadLinkHeaders( $config, $result ) {
+ $out = TestingAccessWrapper::newFromObject( $this->newInstance( $config ) );
+ $out->addLogoPreloadLinkHeaders();
+
+ $this->assertEquals( $result, $out->getLinkHeader() );
+ }
+
+ public function providePreloadLinkHeaders() {
+ return [
+ [
+ [
+ 'Logo' => '/img/default.png',
+ 'LogoHD' => [
+ '1.5x' => '/img/one-point-five.png',
+ '2x' => '/img/two-x.png',
+ ],
+ ],
+ 'Link: </img/default.png>;rel=preload;as=image;media=' .
+ 'not all and (min-resolution: 1.5dppx),' .
+ '</img/one-point-five.png>;rel=preload;as=image;media=' .
+ '(min-resolution: 1.5dppx) and (max-resolution: 1.999999dppx),' .
+ '</img/two-x.png>;rel=preload;as=image;media=(min-resolution: 2dppx)'
+ ],
+ [
+ [
+ 'Logo' => '/img/default.png',
+ 'LogoHD' => false,
+ ],
+ 'Link: </img/default.png>;rel=preload;as=image'
+ ],
+ [
+ [
+ 'Logo' => '/img/default.png',
+ 'LogoHD' => [
+ '2x' => '/img/two-x.png',
+ ],
+ ],
+ 'Link: </img/default.png>;rel=preload;as=image;media=' .
+ 'not all and (min-resolution: 2dppx),' .
+ '</img/two-x.png>;rel=preload;as=image;media=(min-resolution: 2dppx)'
+ ],
+ ];
+ }
+
/**
* @return OutputPage
*/
- private function newInstance() {
+ private function newInstance( $config = [] ) {
$context = new RequestContext();
- $context->setConfig( new HashConfig( [
+ $context->setConfig( new HashConfig( $config + [
'AppleTouchIcon' => false,
'DisableLangConversion' => true,
'EnableAPI' => false,
use Wikimedia\Rdbms\TransactionProfiler;
use Wikimedia\Rdbms\DatabaseDomain;
use Wikimedia\Rdbms\MySQLMasterPos;
+use Wikimedia\Rdbms\DatabaseMysqlBase;
/**
* Fake class around abstract class so we can call concrete methods.
<?php
use Wikimedia\Rdbms\Blob;
+use Wikimedia\Rdbms\Database;
+use Wikimedia\Rdbms\DatabaseSqlite;
class DatabaseSqliteMock extends DatabaseSqlite {
private $lastQuery;
* @dataProvider provideRecords
*/
public function testPickServer( $params, $expected ) {
- $discoverer = new DnsSrvDiscoverer( '_etcd._tcp.eqiad.wmnet' );
+ $discoverer = new DnsSrvDiscoverer( 'etcd-tcp.example.net' );
$record = $discoverer->pickServer( $params );
$this->assertEquals( $expected, $record );
[
[ // record list
[
- 'target' => 'conf1003.eqiad.wmnet',
+ 'target' => 'conf03.example.net',
'port' => 'SRV',
'pri' => 0,
'weight' => 1,
],
[
- 'target' => 'conf1002.eqiad.wmnet',
+ 'target' => 'conf02.example.net',
'port' => 'SRV',
'pri' => 1,
'weight' => 1,
],
[
- 'target' => 'conf1001.eqiad.wmnet',
+ 'target' => 'conf01.example.net',
'port' => 'SRV',
'pri' => 2,
'weight' => 1,
],
], // selected record
[
- 'target' => 'conf1003.eqiad.wmnet',
+ 'target' => 'conf03.example.net',
'port' => 'SRV',
'pri' => 0,
'weight' => 1,
[
[ // record list
[
- 'target' => 'conf1003or2.eqiad.wmnet',
+ 'target' => 'conf03or2.example.net',
'port' => 'SRV',
'pri' => 0,
'weight' => 1,
],
[
- 'target' => 'conf1003or2.eqiad.wmnet',
+ 'target' => 'conf03or2.example.net',
'port' => 'SRV',
'pri' => 0,
'weight' => 1,
],
[
- 'target' => 'conf1001.eqiad.wmnet',
+ 'target' => 'conf01.example.net',
'port' => 'SRV',
'pri' => 2,
'weight' => 1,
],
[
- 'target' => 'conf1004.eqiad.wmnet',
+ 'target' => 'conf04.example.net',
'port' => 'SRV',
'pri' => 2,
'weight' => 1,
],
[
- 'target' => 'conf1005.eqiad.wmnet',
+ 'target' => 'conf05.example.net',
'port' => 'SRV',
'pri' => 3,
'weight' => 1,
],
], // selected record
[
- 'target' => 'conf1003or2.eqiad.wmnet',
+ 'target' => 'conf03or2.example.net',
'port' => 'SRV',
'pri' => 0,
'weight' => 1,
$servers = [
[
- 'target' => 'conf1001.eqiad.wmnet',
+ 'target' => 'conf01.example.net',
'port' => 35,
'pri' => 2,
'weight' => 1,
],
[
- 'target' => 'conf1004.eqiad.wmnet',
+ 'target' => 'conf04.example.net',
'port' => 74,
'pri' => 2,
'weight' => 1,
],
[
- 'target' => 'conf1005.eqiad.wmnet',
+ 'target' => 'conf05.example.net',
'port' => 77,
'pri' => 3,
'weight' => 1,
$expected = [
[
- 'target' => 'conf1001.eqiad.wmnet',
+ 'target' => 'conf01.example.net',
'port' => 35,
'pri' => 2,
'weight' => 1,
],
[
- 'target' => 'conf1005.eqiad.wmnet',
+ 'target' => 'conf05.example.net',
'port' => 77,
'pri' => 3,
'weight' => 1,