summaryrefslogtreecommitdiff
path: root/includes/GlobalFunctions.php
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2015-06-04 07:31:04 +0200
committerPierre Schmitz <pierre@archlinux.de>2015-06-04 07:58:39 +0200
commitf6d65e533c62f6deb21342d4901ece24497b433e (patch)
treef28adf0362d14bcd448f7b65a7aaf38650f923aa /includes/GlobalFunctions.php
parentc27b2e832fe25651ef2410fae85b41072aae7519 (diff)
Update to MediaWiki 1.25.1
Diffstat (limited to 'includes/GlobalFunctions.php')
-rw-r--r--includes/GlobalFunctions.php723
1 files changed, 398 insertions, 325 deletions
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 27f7cacb..240cb97b 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -24,13 +24,17 @@ if ( !defined( 'MEDIAWIKI' ) ) {
die( "This file is part of MediaWiki, it is not a valid entry point" );
}
+use Liuggio\StatsdClient\StatsdClient;
+use Liuggio\StatsdClient\Sender\SocketSender;
+use MediaWiki\Logger\LoggerFactory;
+
// Hide compatibility functions from Doxygen
/// @cond
/**
* Compatibility functions
*
- * We support PHP 5.3.2 and up.
+ * We support PHP 5.3.3 and up.
* Re-implementations of newer functions or functions in non-standard
* PHP extensions may be included here.
*/
@@ -161,6 +165,72 @@ if ( !function_exists( 'hash_equals' ) ) {
/// @endcond
/**
+ * Load an extension
+ *
+ * This queues an extension to be loaded through
+ * the ExtensionRegistry system.
+ *
+ * @param string $ext Name of the extension to load
+ * @param string|null $path Absolute path of where to find the extension.json file
+ */
+function wfLoadExtension( $ext, $path = null ) {
+ if ( !$path ) {
+ global $wgExtensionDirectory;
+ $path = "$wgExtensionDirectory/$ext/extension.json";
+ }
+ ExtensionRegistry::getInstance()->queue( $path );
+}
+
+/**
+ * Load multiple extensions at once
+ *
+ * Same as wfLoadExtension, but more efficient if you
+ * are loading multiple extensions.
+ *
+ * If you want to specify custom paths, you should interact with
+ * ExtensionRegistry directly.
+ *
+ * @see wfLoadExtension
+ * @param string[] $exts Array of extension names to load
+ */
+function wfLoadExtensions( array $exts ) {
+ global $wgExtensionDirectory;
+ $registry = ExtensionRegistry::getInstance();
+ foreach ( $exts as $ext ) {
+ $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
+ }
+}
+
+/**
+ * Load a skin
+ *
+ * @see wfLoadExtension
+ * @param string $skin Name of the extension to load
+ * @param string|null $path Absolute path of where to find the skin.json file
+ */
+function wfLoadSkin( $skin, $path = null ) {
+ if ( !$path ) {
+ global $wgStyleDirectory;
+ $path = "$wgStyleDirectory/$skin/skin.json";
+ }
+ ExtensionRegistry::getInstance()->queue( $path );
+}
+
+/**
+ * Load multiple skins at once
+ *
+ * @see wfLoadExtensions
+ * @param string[] $skins Array of extension names to load
+ */
+function wfLoadSkins( array $skins ) {
+ global $wgStyleDirectory;
+ $registry = ExtensionRegistry::getInstance();
+ foreach ( $skins as $skin ) {
+ $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
+ }
+}
+
+/**
* Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
* @param array $a
* @param array $b
@@ -950,44 +1020,40 @@ function wfMatchesDomainList( $url, $domains ) {
* $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output.
* $wgDebugComments - if on, some debug items may appear in comments in the HTML output.
*
+ * @since 1.25 support for additional context data
+ *
* @param string $text
- * @param string|bool $dest Destination of the message:
- * - 'all': both to the log and HTML (debug toolbar or HTML comments)
- * - 'log': only to the log and not in HTML
- * For backward compatibility, it can also take a boolean:
- * - true: same as 'all'
- * - false: same as 'log'
+ * @param string|bool $dest Unused
+ * @param array $context Additional logging context data
*/
-function wfDebug( $text, $dest = 'all' ) {
- global $wgDebugLogFile, $wgDebugRawPage, $wgDebugLogPrefix;
+function wfDebug( $text, $dest = 'all', array $context = array() ) {
+ global $wgDebugRawPage, $wgDebugLogPrefix;
+ global $wgDebugTimestamps, $wgRequestTime;
if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
return;
}
- // Turn $dest into a string if it's a boolean (for b/c)
- if ( $dest === true ) {
- $dest = 'all';
- } elseif ( $dest === false ) {
- $dest = 'log';
- }
+ $text = trim( $text );
- $timer = wfDebugTimer();
- if ( $timer !== '' ) {
- $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 );
+ // Inline logic from deprecated wfDebugTimer()
+ if ( $wgDebugTimestamps ) {
+ $context['seconds_elapsed'] = sprintf(
+ '%6.4f',
+ microtime( true ) - $wgRequestTime
+ );
+ $context['memory_used'] = sprintf(
+ '%5.1fM',
+ ( memory_get_usage( true ) / ( 1024 * 1024 ) )
+ );
}
- if ( $dest === 'all' ) {
- MWDebug::debugMsg( $text );
+ if ( $wgDebugLogPrefix !== '' ) {
+ $context['prefix'] = $wgDebugLogPrefix;
}
- if ( $wgDebugLogFile != '' ) {
- # Strip unprintables; they can switch terminal modes when binary data
- # gets dumped, which is pretty annoying.
- $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
- $text = $wgDebugLogPrefix . $text;
- wfErrorLog( $text, $wgDebugLogFile );
- }
+ $logger = LoggerFactory::getInstance( 'wfDebug' );
+ $logger->debug( $text, $context );
}
/**
@@ -1016,11 +1082,14 @@ function wfIsDebugRawPage() {
/**
* Get microsecond timestamps for debug logs
*
+ * @deprecated since 1.25
* @return string
*/
function wfDebugTimer() {
global $wgDebugTimestamps, $wgRequestTime;
+ wfDeprecated( __METHOD__, '1.25' );
+
if ( !$wgDebugTimestamps ) {
return '';
}
@@ -1046,30 +1115,34 @@ function wfDebugMem( $exact = false ) {
}
/**
- * Send a line to a supplementary debug log file, if configured, or main debug log if not.
- * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to a string
- * filename or an associative array mapping 'destination' to the desired filename. The
- * associative array may also contain a 'sample' key with an integer value, specifying
- * a sampling factor.
+ * Send a line to a supplementary debug log file, if configured, or main debug
+ * log if not.
+ *
+ * To configure a supplementary log file, set $wgDebugLogGroups[$logGroup] to
+ * a string filename or an associative array mapping 'destination' to the
+ * desired filename. The associative array may also contain a 'sample' key
+ * with an integer value, specifying a sampling factor. Sampled log events
+ * will be emitted with a 1 in N random chance.
*
* @since 1.23 support for sampling log messages via $wgDebugLogGroups.
+ * @since 1.25 support for additional context data
+ * @since 1.25 sample behavior dependent on configured $wgMWLoggerDefaultSpi
*
* @param string $logGroup
* @param string $text
* @param string|bool $dest Destination of the message:
* - 'all': both to the log and HTML (debug toolbar or HTML comments)
* - 'log': only to the log and not in HTML
- * - 'private': only to the specifc log if set in $wgDebugLogGroups and
+ * - 'private': only to the specific log if set in $wgDebugLogGroups and
* discarded otherwise
* For backward compatibility, it can also take a boolean:
* - true: same as 'all'
* - false: same as 'private'
+ * @param array $context Additional logging context data
*/
-function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
- global $wgDebugLogGroups;
-
- $text = trim( $text ) . "\n";
-
+function wfDebugLog(
+ $logGroup, $text, $dest = 'all', array $context = array()
+) {
// Turn $dest into a string if it's a boolean (for b/c)
if ( $dest === true ) {
$dest = 'all';
@@ -1077,66 +1150,24 @@ function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
$dest = 'private';
}
- if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
- if ( $dest !== 'private' ) {
- wfDebug( "[$logGroup] $text", $dest );
- }
- return;
- }
-
- if ( $dest === 'all' ) {
- MWDebug::debugMsg( "[$logGroup] $text" );
- }
-
- $logConfig = $wgDebugLogGroups[$logGroup];
- if ( $logConfig === false ) {
- return;
- }
- if ( is_array( $logConfig ) ) {
- if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
- return;
- }
- $destination = $logConfig['destination'];
- } else {
- $destination = strval( $logConfig );
- }
+ $text = trim( $text );
- $time = wfTimestamp( TS_DB );
- $wiki = wfWikiID();
- $host = wfHostname();
- wfErrorLog( "$time $host $wiki: $text", $destination );
+ $logger = LoggerFactory::getInstance( $logGroup );
+ $context['private'] = ( $dest === 'private' );
+ $logger->info( $text, $context );
}
/**
* Log for database errors
*
+ * @since 1.25 support for additional context data
+ *
* @param string $text Database error message.
+ * @param array $context Additional logging context data
*/
-function wfLogDBError( $text ) {
- global $wgDBerrorLog, $wgDBerrorLogTZ;
- static $logDBErrorTimeZoneObject = null;
-
- if ( $wgDBerrorLog ) {
- $host = wfHostname();
- $wiki = wfWikiID();
-
- if ( $wgDBerrorLogTZ && !$logDBErrorTimeZoneObject ) {
- $logDBErrorTimeZoneObject = new DateTimeZone( $wgDBerrorLogTZ );
- }
-
- // Workaround for https://bugs.php.net/bug.php?id=52063
- // Can be removed when min PHP > 5.3.2
- if ( $logDBErrorTimeZoneObject === null ) {
- $d = date_create( "now" );
- } else {
- $d = date_create( "now", $logDBErrorTimeZoneObject );
- }
-
- $date = $d->format( 'D M j G:i:s T Y' );
-
- $text = "$date\t$host\t$wiki\t" . trim( $text ) . "\n";
- wfErrorLog( $text, $wgDBerrorLog );
- }
+function wfLogDBError( $text, array $context = array() ) {
+ $logger = LoggerFactory::getInstance( 'wfLogDBError' );
+ $logger->error( trim( $text ), $context );
}
/**
@@ -1188,138 +1219,93 @@ function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
*
* Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
* send lines to the specified port, prefixed by the specified prefix and a space.
+ * @since 1.25 support for additional context data
*
* @param string $text
* @param string $file Filename
+ * @param array $context Additional logging context data
* @throws MWException
+ * @deprecated since 1.25 Use MediaWiki\Logger\LegacyLogger::emit or UDPTransport
*/
-function wfErrorLog( $text, $file ) {
- if ( substr( $file, 0, 4 ) == 'udp:' ) {
- # Needs the sockets extension
- if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
- // IPv6 bracketed host
- $host = $m[2];
- $port = intval( $m[3] );
- $prefix = isset( $m[4] ) ? $m[4] : false;
- $domain = AF_INET6;
- } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
- $host = $m[2];
- if ( !IP::isIPv4( $host ) ) {
- $host = gethostbyname( $host );
- }
- $port = intval( $m[3] );
- $prefix = isset( $m[4] ) ? $m[4] : false;
- $domain = AF_INET;
- } else {
- throw new MWException( __METHOD__ . ': Invalid UDP specification' );
- }
-
- // Clean it up for the multiplexer
- if ( strval( $prefix ) !== '' ) {
- $text = preg_replace( '/^/m', $prefix . ' ', $text );
-
- // Limit to 64KB
- if ( strlen( $text ) > 65506 ) {
- $text = substr( $text, 0, 65506 );
- }
-
- if ( substr( $text, -1 ) != "\n" ) {
- $text .= "\n";
- }
- } elseif ( strlen( $text ) > 65507 ) {
- $text = substr( $text, 0, 65507 );
- }
-
- $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
- if ( !$sock ) {
- return;
- }
-
- socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port );
- socket_close( $sock );
- } else {
- wfSuppressWarnings();
- $exists = file_exists( $file );
- $size = $exists ? filesize( $file ) : false;
- if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
- file_put_contents( $file, $text, FILE_APPEND );
- }
- wfRestoreWarnings();
- }
+function wfErrorLog( $text, $file, array $context = array() ) {
+ wfDeprecated( __METHOD__, '1.25' );
+ $logger = LoggerFactory::getInstance( 'wfErrorLog' );
+ $context['destination'] = $file;
+ $logger->info( trim( $text ), $context );
}
/**
* @todo document
*/
function wfLogProfilingData() {
- global $wgRequestTime, $wgDebugLogFile, $wgDebugLogGroups, $wgDebugRawPage;
- global $wgProfileLimit, $wgUser, $wgRequest;
+ global $wgDebugLogGroups, $wgDebugRawPage;
- StatCounter::singleton()->flush();
+ $context = RequestContext::getMain();
+ $request = $context->getRequest();
$profiler = Profiler::instance();
+ $profiler->setContext( $context );
+ $profiler->logData();
- # Profiling must actually be enabled...
- if ( $profiler->isStub() ) {
- return;
+ $config = $context->getConfig();
+ if ( $config->has( 'StatsdServer' ) ) {
+ $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
+ $statsdHost = $statsdServer[0];
+ $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
+ $statsdSender = new SocketSender( $statsdHost, $statsdPort );
+ $statsdClient = new StatsdClient( $statsdSender );
+ $statsdClient->send( $context->getStats()->getBuffer() );
}
- // Get total page request time and only show pages that longer than
- // $wgProfileLimit time (default is 0)
- $elapsed = microtime( true ) - $wgRequestTime;
- if ( $elapsed <= $wgProfileLimit ) {
+ # Profiling must actually be enabled...
+ if ( $profiler instanceof ProfilerStub ) {
return;
}
- $profiler->logData();
-
- // Check whether this should be logged in the debug file.
if ( isset( $wgDebugLogGroups['profileoutput'] )
&& $wgDebugLogGroups['profileoutput'] === false
) {
- // Explicitely disabled
- return;
- }
- if ( !isset( $wgDebugLogGroups['profileoutput'] ) && $wgDebugLogFile == '' ) {
- // Logging not enabled; no point going further
+ // Explicitly disabled
return;
}
if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
return;
}
- $forward = '';
+ $ctx = array( 'elapsed' => $request->getElapsedTime() );
if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
- $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
+ $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
- $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
+ $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
}
if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
- $forward .= ' from ' . $_SERVER['HTTP_FROM'];
+ $ctx['from'] = $_SERVER['HTTP_FROM'];
}
- if ( $forward ) {
- $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
+ if ( isset( $ctx['forwarded_for'] ) ||
+ isset( $ctx['client_ip'] ) ||
+ isset( $ctx['from'] ) ) {
+ $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
}
+
// Don't load $wgUser at this late stage just for statistics purposes
- // @todo FIXME: We can detect some anons even if it is not loaded. See User::getId()
- if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) {
- $forward .= ' anon';
- }
+ // @todo FIXME: We can detect some anons even if it is not loaded.
+ // See User::getId()
+ $user = $context->getUser();
+ $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
// Command line script uses a FauxRequest object which does not have
// any knowledge about an URL and throw an exception instead.
try {
- $requestUrl = $wgRequest->getRequestURL();
- } catch ( MWException $e ) {
- $requestUrl = 'n/a';
+ $ctx['url'] = urldecode( $request->getRequestURL() );
+ } catch ( Exception $ignored ) {
+ // no-op
}
- $log = sprintf( "%s\t%04.3f\t%s\n",
- gmdate( 'YmdHis' ), $elapsed,
- urldecode( $requestUrl . $forward ) );
+ $ctx['output'] = $profiler->getOutput();
- wfDebugLog( 'profileoutput', $log . $profiler->getOutput() );
+ $log = LoggerFactory::getInstance( 'profileoutput' );
+ $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
}
/**
@@ -1330,7 +1316,8 @@ function wfLogProfilingData() {
* @return void
*/
function wfIncrStats( $key, $count = 1 ) {
- StatCounter::singleton()->incr( $key, $count );
+ $stats = RequestContext::getMain()->getStats();
+ $stats->updateCount( $key, $count );
}
/**
@@ -1573,10 +1560,8 @@ function wfMsgForContentNoTrans( $key ) {
function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
wfDeprecated( __METHOD__, '1.21' );
- wfProfileIn( __METHOD__ );
$message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
$message = wfMsgReplaceArgs( $message, $args );
- wfProfileOut( __METHOD__ );
return $message;
}
@@ -1595,7 +1580,7 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform
function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
wfDeprecated( __METHOD__, '1.21' );
- wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
+ Hooks::run( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
$cache = MessageCache::singleton();
$message = $cache->get( $key, $useDB, $langCode );
@@ -1710,15 +1695,15 @@ function wfMsgExt( $key, $options ) {
array_shift( $args );
array_shift( $args );
$options = (array)$options;
+ $validOptions = array( 'parse', 'parseinline', 'escape', 'escapenoentities', 'replaceafter',
+ 'parsemag', 'content' );
foreach ( $options as $arrayKey => $option ) {
if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
- # An unknown index, neither numeric nor "language"
+ // An unknown index, neither numeric nor "language"
wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
- } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
- array( 'parse', 'parseinline', 'escape', 'escapenoentities',
- 'replaceafter', 'parsemag', 'content' ) ) ) {
- # A numeric index with unknown value
+ } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, $validOptions ) ) {
+ // A numeric index with unknown value
wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
}
}
@@ -1879,52 +1864,37 @@ function wfDebugBacktrace( $limit = 0 ) {
/**
* Get a debug backtrace as a string
*
+ * @param bool|null $raw If true, the return value is plain text. If false, HTML.
+ * Defaults to $wgCommandLineMode if unset.
* @return string
+ * @since 1.25 Supports $raw parameter.
*/
-function wfBacktrace() {
+function wfBacktrace( $raw = null ) {
global $wgCommandLineMode;
- if ( $wgCommandLineMode ) {
- $msg = '';
- } else {
- $msg = "<ul>\n";
+ if ( $raw === null ) {
+ $raw = $wgCommandLineMode;
}
- $backtrace = wfDebugBacktrace();
- foreach ( $backtrace as $call ) {
- if ( isset( $call['file'] ) ) {
- $f = explode( DIRECTORY_SEPARATOR, $call['file'] );
- $file = $f[count( $f ) - 1];
- } else {
- $file = '-';
- }
- if ( isset( $call['line'] ) ) {
- $line = $call['line'];
- } else {
- $line = '-';
- }
- if ( $wgCommandLineMode ) {
- $msg .= "$file line $line calls ";
- } else {
- $msg .= '<li>' . $file . ' line ' . $line . ' calls ';
- }
- if ( !empty( $call['class'] ) ) {
- $msg .= $call['class'] . $call['type'];
- }
- $msg .= $call['function'] . '()';
- if ( $wgCommandLineMode ) {
- $msg .= "\n";
- } else {
- $msg .= "</li>\n";
- }
- }
- if ( $wgCommandLineMode ) {
- $msg .= "\n";
+ if ( $raw ) {
+ $frameFormat = "%s line %s calls %s()\n";
+ $traceFormat = "%s";
} else {
- $msg .= "</ul>\n";
+ $frameFormat = "<li>%s line %s calls %s()</li>\n";
+ $traceFormat = "<ul>\n%s</ul>\n";
}
- return $msg;
+ $frames = array_map( function ( $frame ) use ( $frameFormat ) {
+ $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
+ $line = isset( $frame['line'] ) ? $frame['line'] : '-';
+ $call = $frame['function'];
+ if ( !empty( $frame['class'] ) ) {
+ $call = $frame['class'] . $frame['type'] . $call;
+ }
+ return sprintf( $frameFormat, $file, $line, $call );
+ }, wfDebugBacktrace() );
+
+ return sprintf( $traceFormat, implode( '', $frames ) );
}
/**
@@ -2148,10 +2118,12 @@ function wfVarDump( $var ) {
*/
function wfHttpError( $code, $label, $desc ) {
global $wgOut;
- $wgOut->disable();
header( "HTTP/1.0 $code $label" );
header( "Status: $code $label" );
- $wgOut->sendCacheControl();
+ if ( $wgOut ) {
+ $wgOut->disable();
+ $wgOut->sendCacheControl();
+ }
header( 'Content-type: text/html; charset=utf-8' );
print "<!doctype html>" .
@@ -2496,20 +2468,6 @@ function wfIsHHVM() {
}
/**
- * Swap two variables
- *
- * @deprecated since 1.24
- * @param mixed $x
- * @param mixed $y
- */
-function swap( &$x, &$y ) {
- wfDeprecated( __FUNCTION__, '1.24' );
- $z = $x;
- $x = $y;
- $y = $z;
-}
-
-/**
* Tries to get the system directory for temporary files. First
* $wgTmpDirectory is checked, and then the TMPDIR, TMP, and TEMP
* environment variables are then checked in sequence, and if none are
@@ -2659,13 +2617,19 @@ function wfIniGetBool( $setting ) {
* Also fixes the locale problems on Linux in PHP 5.2.6+ (bug backported to
* earlier distro releases of PHP)
*
- * @param string $args,...
+ * @param string ... strings to escape and glue together, or a single array of strings parameter
* @return string
*/
function wfEscapeShellArg( /*...*/ ) {
wfInitShellLocale();
$args = func_get_args();
+ if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
+ // If only one argument has been passed, and that argument is an array,
+ // treat it as a list of arguments
+ $args = reset( $args );
+ }
+
$first = true;
$retVal = '';
foreach ( $args as $arg ) {
@@ -2756,6 +2720,8 @@ function wfShellExecDisabled() {
* @param array $options Array of options:
* - duplicateStderr: Set this to true to duplicate stderr to stdout,
* including errors from limit.sh
+ * - profileMethod: By default this function will profile based on the calling
+ * method. Set this to a string for an alternative method to profile from
*
* @return string Collected stdout as a string
*/
@@ -2774,6 +2740,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
}
$includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
+ $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
wfInitShellLocale();
@@ -2795,12 +2762,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
}
}
if ( is_array( $cmd ) ) {
- // Command line may be given as an array, escape each value and glue them together with a space
- $cmdVals = array();
- foreach ( $cmd as $val ) {
- $cmdVals[] = wfEscapeShellArg( $val );
- }
- $cmd = implode( ' ', $cmdVals );
+ $cmd = wfEscapeShellArg( $cmd );
}
$cmd = $envcmd . $cmd;
@@ -2847,6 +2809,7 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
$desc[3] = array( 'pipe', 'w' );
}
$pipes = null;
+ $scoped = Profiler::instance()->scopedProfileIn( __FUNCTION__ . '-' . $profileMethod );
$proc = proc_open( $cmd, $desc, $pipes );
if ( !$proc ) {
wfDebugLog( 'exec', "proc_open() failed: $cmd" );
@@ -2986,7 +2949,9 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
* function, as all the arguments to wfShellExec can become unwieldy.
*
* @note This also includes errors from limit.sh, e.g. if $wgMaxShellFileSize is exceeded.
- * @param string $cmd Command line, properly escaped for shell.
+ * @param string|string[] $cmd If string, a properly shell-escaped command line,
+ * or an array of unescaped arguments, in which case each value will be escaped
+ * Example: [ 'convert', '-font', 'font name' ] would produce "'convert' '-font' 'font name'"
* @param null|mixed &$retval Optional, will receive the program's exit code.
* (non-zero is usually failure)
* @param array $environ Optional environment variables which should be
@@ -2996,7 +2961,8 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
* @return string Collected stdout and stderr as a string
*/
function wfShellExecWithStderr( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
- return wfShellExec( $cmd, $retval, $environ, $limits, array( 'duplicateStderr' => true ) );
+ return wfShellExec( $cmd, $retval, $environ, $limits,
+ array( 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ) );
}
/**
@@ -3017,15 +2983,6 @@ function wfInitShellLocale() {
}
/**
- * Alias to wfShellWikiCmd()
- *
- * @see wfShellWikiCmd()
- */
-function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) {
- return wfShellWikiCmd( $script, $parameters, $options );
-}
-
-/**
* Generate a shell-escaped command line string to run a MediaWiki cli script.
* Note that $parameters should be a flat array and an option with an argument
* should consist of two consecutive items in the array (do not use "--option value").
@@ -3041,14 +2998,14 @@ function wfShellWikiCmd( $script, array $parameters = array(), array $options =
global $wgPhpCli;
// Give site config file a chance to run the script in a wrapper.
// The caller may likely want to call wfBasename() on $script.
- wfRunHooks( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) );
+ Hooks::run( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) );
$cmd = isset( $options['php'] ) ? array( $options['php'] ) : array( $wgPhpCli );
if ( isset( $options['wrapper'] ) ) {
$cmd[] = $options['wrapper'];
}
$cmd[] = $script;
// Escape each parameter for shell
- return implode( " ", array_map( 'wfEscapeShellArg', array_merge( $cmd, $parameters ) ) );
+ return wfEscapeShellArg( array_merge( $cmd, $parameters ) );
}
/**
@@ -3093,10 +3050,8 @@ function wfMerge( $old, $mine, $yours, &$result ) {
fclose( $yourtextFile );
# Check for a conflict
- $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a --overlap-only ' .
- wfEscapeShellArg( $mytextName ) . ' ' .
- wfEscapeShellArg( $oldtextName ) . ' ' .
- wfEscapeShellArg( $yourtextName );
+ $cmd = wfEscapeShellArg( $wgDiff3, '-a', '--overlap-only', $mytextName,
+ $oldtextName, $yourtextName );
$handle = popen( $cmd, 'r' );
if ( fgets( $handle, 1024 ) ) {
@@ -3107,8 +3062,8 @@ function wfMerge( $old, $mine, $yours, &$result ) {
pclose( $handle );
# Merge differences
- $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a -e --merge ' .
- wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName );
+ $cmd = wfEscapeShellArg( $wgDiff3, '-a', '-e', '--merge', $mytextName,
+ $oldtextName, $yourtextName );
$handle = popen( $cmd, 'r' );
$result = '';
do {
@@ -3132,7 +3087,9 @@ function wfMerge( $old, $mine, $yours, &$result ) {
/**
* Returns unified plain-text diff of two texts.
- * Useful for machine processing of diffs.
+ * "Useful" for machine processing of diffs.
+ *
+ * @deprecated since 1.25, use DiffEngine/UnifiedDiffFormatter directly
*
* @param string $before The text before the changes.
* @param string $after The text after the changes.
@@ -3172,6 +3129,11 @@ function wfDiff( $before, $after, $params = '-u' ) {
$cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
$h = popen( $cmd, 'r' );
+ if ( !$h ) {
+ unlink( $oldtextName );
+ unlink( $newtextName );
+ throw new Exception( __METHOD__ . '(): popen() failed' );
+ }
$diff = '';
@@ -3493,7 +3455,7 @@ function wfResetSessionID() {
$_SESSION = $tmp;
}
$newSessionId = session_id();
- wfRunHooks( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
+ Hooks::run( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
}
/**
@@ -3628,7 +3590,7 @@ function wfSplitWikiID( $wiki ) {
*
* @return DatabaseBase
*/
-function &wfGetDB( $db, $groups = array(), $wiki = false ) {
+function wfGetDB( $db, $groups = array(), $wiki = false ) {
return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
}
@@ -3647,7 +3609,7 @@ function wfGetLB( $wiki = false ) {
*
* @return LBFactory
*/
-function &wfGetLBFactory() {
+function wfGetLBFactory() {
return LBFactory::singleton();
}
@@ -3656,19 +3618,7 @@ function &wfGetLBFactory() {
* Shortcut for RepoGroup::singleton()->findFile()
*
* @param string $title String or Title object
- * @param array $options Associative array of options:
- * time: requested time for an archived image, or false for the
- * current version. An image object will be returned which was
- * created at the specified time.
- *
- * ignoreRedirect: If true, do not follow file redirects
- *
- * private: If true, return restricted (deleted) files if the current
- * user is allowed to view them. Otherwise, such files will not
- * be found.
- *
- * bypassCache: If true, do not use the process-local cache of File objects
- *
+ * @param array $options Associative array of options (see RepoGroup::findFile)
* @return File|bool File, or false if the file does not exist
*/
function wfFindFile( $title, $options = array() ) {
@@ -3763,46 +3713,79 @@ function wfGetNull() {
}
/**
- * Modern version of wfWaitForSlaves(). Instead of looking at replication lag
- * and waiting for it to go down, this waits for the slaves to catch up to the
- * master position. Use this when updating very large numbers of rows, as
- * in maintenance scripts, to avoid causing too much lag. Of course, this is
- * a no-op if there are no slaves.
+ * Waits for the slaves to catch up to the master position
+ *
+ * Use this when updating very large numbers of rows, as in maintenance scripts,
+ * to avoid causing too much lag. Of course, this is a no-op if there are no slaves.
+ *
+ * By default this waits on the main DB cluster of the current wiki.
+ * If $cluster is set to "*" it will wait on all DB clusters, including
+ * external ones. If the lag being waiting on is caused by the code that
+ * does this check, it makes since to use $ifWritesSince, particularly if
+ * cluster is "*", to avoid excess overhead.
+ *
+ * Never call this function after a big DB write that is still in a transaction.
+ * This only makes sense after the possible lag inducing changes were committed.
*
* @param float|null $ifWritesSince Only wait if writes were done since this UNIX timestamp
* @param string|bool $wiki Wiki identifier accepted by wfGetLB
* @param string|bool $cluster Cluster name accepted by LBFactory. Default: false.
+ * @param int|null $timeout Max wait time. Default: 1 day (cli), ~10 seconds (web)
* @return bool Success (able to connect and no timeouts reached)
*/
-function wfWaitForSlaves( $ifWritesSince = false, $wiki = false, $cluster = false ) {
+function wfWaitForSlaves(
+ $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
+) {
// B/C: first argument used to be "max seconds of lag"; ignore such values
- $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : false;
+ $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null;
- if ( $cluster !== false ) {
- $lb = wfGetLBFactory()->getExternalLB( $cluster );
- } else {
- $lb = wfGetLB( $wiki );
+ if ( $timeout === null ) {
+ $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
}
- // bug 27975 - Don't try to wait for slaves if there are none
- // Prevents permission error when getting master position
- if ( $lb->getServerCount() > 1 ) {
- if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
- return true; // assume no writes done
- }
- $dbw = $lb->getConnection( DB_MASTER, array(), $wiki );
- if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
- return true; // no writes since the last wait
+ // Figure out which clusters need to be checked
+ $lbs = array();
+ if ( $cluster === '*' ) {
+ wfGetLBFactory()->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
+ $lbs[] = $lb;
+ } );
+ } elseif ( $cluster !== false ) {
+ $lbs[] = wfGetLBFactory()->getExternalLB( $cluster );
+ } else {
+ $lbs[] = wfGetLB( $wiki );
+ }
+
+ // Get all the master positions of applicable DBs right now.
+ // This can be faster since waiting on one cluster reduces the
+ // time needed to wait on the next clusters.
+ $masterPositions = array_fill( 0, count( $lbs ), false );
+ foreach ( $lbs as $i => $lb ) {
+ // bug 27975 - Don't try to wait for slaves if there are none
+ // Prevents permission error when getting master position
+ if ( $lb->getServerCount() > 1 ) {
+ if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
+ continue; // assume no writes done
+ }
+ // Use the empty string to not trigger selectDB() since the connection
+ // may have been to a server that does not have a DB for the current wiki.
+ $dbw = $lb->getConnection( DB_MASTER, array(), '' );
+ if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
+ continue; // no writes since the last wait
+ }
+ $masterPositions[$i] = $dbw->getMasterPos();
}
- $pos = $dbw->getMasterPos();
- // The DBMS may not support getMasterPos() or the whole
- // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
- if ( $pos !== false ) {
- return $lb->waitForAll( $pos, PHP_SAPI === 'cli' ? 86400 : null );
+ }
+
+ $ok = true;
+ foreach ( $lbs as $i => $lb ) {
+ if ( $masterPositions[$i] ) {
+ // The DBMS may not support getMasterPos() or the whole
+ // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
+ $ok = $lb->waitForAll( $masterPositions[$i], $timeout ) && $ok;
}
}
- return true;
+ return $ok;
}
/**
@@ -3935,7 +3918,7 @@ function wfBCP47( $code ) {
/**
* Get a cache object.
*
- * @param int $inputType Cache type, one the the CACHE_* constants.
+ * @param int $inputType Cache type, one of the CACHE_* constants.
* @return BagOStuff
*/
function wfGetCache( $inputType ) {
@@ -3973,16 +3956,6 @@ function wfGetParserCacheStorage() {
}
/**
- * Get the cache object used by the language converter
- *
- * @return BagOStuff
- */
-function wfGetLangConverterCacheStorage() {
- global $wgLanguageConverterCacheType;
- return ObjectCache::getInstance( $wgLanguageConverterCacheType );
-}
-
-/**
* Call hook functions defined in $wgHooks
*
* @param string $event Event name
@@ -3990,6 +3963,7 @@ function wfGetLangConverterCacheStorage() {
* @param string|null $deprecatedVersion Optionally mark hook as deprecated with version number
*
* @return bool True if no handler aborted the hook
+ * @deprecated 1.25 - use Hooks::run
*/
function wfRunHooks( $event, array $args = array(), $deprecatedVersion = null ) {
return Hooks::run( $event, $args, $deprecatedVersion );
@@ -4047,7 +4021,6 @@ function wfUnpack( $format, $data, $length = false ) {
*/
function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
static $badImageCache = null; // based on bad_image_list msg
- wfProfileIn( __METHOD__ );
# Handle redirects
$redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) );
@@ -4057,8 +4030,7 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
# Run the extension hook
$bad = false;
- if ( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
- wfProfileOut( __METHOD__ );
+ if ( !Hooks::run( 'BadImage', array( $name, &$bad ) ) ) {
return $bad;
}
@@ -4108,7 +4080,6 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
$contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
$bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
- wfProfileOut( __METHOD__ );
return $bad;
}
@@ -4121,11 +4092,23 @@ function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
*/
function wfCanIPUseHTTPS( $ip ) {
$canDo = true;
- wfRunHooks( 'CanIPUseHTTPS', array( $ip, &$canDo ) );
+ Hooks::run( 'CanIPUseHTTPS', array( $ip, &$canDo ) );
return !!$canDo;
}
/**
+ * Determine input string is represents as infinity
+ *
+ * @param string $str The string to determine
+ * @return bool
+ * @since 1.25
+ */
+function wfIsInfinity( $str ) {
+ $infinityValues = array( 'infinite', 'indefinite', 'infinity', 'never' );
+ return in_array( $str, $infinityValues );
+}
+
+/**
* Work out the IP address based on various globals
* For trusted proxies, use the XFF client IP (first of the chain)
*
@@ -4148,6 +4131,7 @@ function wfGetIP() {
* @return bool
*/
function wfIsTrustedProxy( $ip ) {
+ wfDeprecated( __METHOD__, '1.24' );
return IP::isTrustedProxy( $ip );
}
@@ -4160,5 +4144,94 @@ function wfIsTrustedProxy( $ip ) {
* @since 1.23 Supports CIDR ranges in $wgSquidServersNoPurge
*/
function wfIsConfiguredProxy( $ip ) {
+ wfDeprecated( __METHOD__, '1.24' );
return IP::isConfiguredProxy( $ip );
}
+
+/**
+ * Returns true if these thumbnail parameters match one that MediaWiki
+ * requests from file description pages and/or parser output.
+ *
+ * $params is considered non-standard if they involve a non-standard
+ * width or any non-default parameters aside from width and page number.
+ * The number of possible files with standard parameters is far less than
+ * that of all combinations; rate-limiting for them can thus be more generious.
+ *
+ * @param File $file
+ * @param array $params
+ * @return bool
+ * @since 1.24 Moved from thumb.php to GlobalFunctions in 1.25
+ */
+function wfThumbIsStandard( File $file, array $params ) {
+ global $wgThumbLimits, $wgImageLimits, $wgResponsiveImages;
+
+ $multipliers = array( 1 );
+ if ( $wgResponsiveImages ) {
+ // These available sizes are hardcoded currently elsewhere in MediaWiki.
+ // @see Linker::processResponsiveImages
+ $multipliers[] = 1.5;
+ $multipliers[] = 2;
+ }
+
+ $handler = $file->getHandler();
+ if ( !$handler || !isset( $params['width'] ) ) {
+ return false;
+ }
+
+ $basicParams = array();
+ if ( isset( $params['page'] ) ) {
+ $basicParams['page'] = $params['page'];
+ }
+
+ $thumbLimits = array();
+ $imageLimits = array();
+ // Expand limits to account for multipliers
+ foreach ( $multipliers as $multiplier ) {
+ $thumbLimits = array_merge( $thumbLimits, array_map(
+ function ( $width ) use ( $multiplier ) {
+ return round( $width * $multiplier );
+ }, $wgThumbLimits )
+ );
+ $imageLimits = array_merge( $imageLimits, array_map(
+ function ( $pair ) use ( $multiplier ) {
+ return array(
+ round( $pair[0] * $multiplier ),
+ round( $pair[1] * $multiplier ),
+ );
+ }, $wgImageLimits )
+ );
+ }
+
+ // Check if the width matches one of $wgThumbLimits
+ if ( in_array( $params['width'], $thumbLimits ) ) {
+ $normalParams = $basicParams + array( 'width' => $params['width'] );
+ // Append any default values to the map (e.g. "lossy", "lossless", ...)
+ $handler->normaliseParams( $file, $normalParams );
+ } else {
+ // If not, then check if the width matchs one of $wgImageLimits
+ $match = false;
+ foreach ( $imageLimits as $pair ) {
+ $normalParams = $basicParams + array( 'width' => $pair[0], 'height' => $pair[1] );
+ // Decide whether the thumbnail should be scaled on width or height.
+ // Also append any default values to the map (e.g. "lossy", "lossless", ...)
+ $handler->normaliseParams( $file, $normalParams );
+ // Check if this standard thumbnail size maps to the given width
+ if ( $normalParams['width'] == $params['width'] ) {
+ $match = true;
+ break;
+ }
+ }
+ if ( !$match ) {
+ return false; // not standard for description pages
+ }
+ }
+
+ // Check that the given values for non-page, non-width, params are just defaults
+ foreach ( $params as $key => $value ) {
+ if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
+ return false;
+ }
+ }
+
+ return true;
+}