23 if ( !defined(
'MEDIAWIKI' ) ) {
24 die(
"This file is part of MediaWiki, it is not a valid entry point" );
34 use Wikimedia\AtEase\AtEase;
35 use Wikimedia\WrappedString;
50 $path =
"$wgExtensionDirectory/$ext/extension.json";
71 foreach ( $exts as
$ext ) {
72 $registry->queue(
"$wgExtensionDirectory/$ext/extension.json" );
87 $path =
"$wgStyleDirectory/$skin/skin.json";
102 foreach ( $skins as $skin ) {
103 $registry->queue(
"$wgStyleDirectory/$skin/skin.json" );
114 return array_udiff( $a, $b,
'wfArrayDiff2_cmp' );
123 if ( is_string( $a ) && is_string( $b ) ) {
124 return strcmp( $a, $b );
125 } elseif ( count( $a ) !== count( $b ) ) {
126 return count( $a ) <=> count( $b );
130 while ( key( $a ) !==
null && key( $b ) !==
null ) {
131 $valueA = current( $a );
132 $valueB = current( $b );
133 $cmp = strcmp( $valueA, $valueB );
154 if ( is_null( $changed ) ) {
155 throw new MWException(
'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
157 if ( $default[$key] !== $value ) {
158 $changed[$key] = $value;
183 foreach (
$args as $errors ) {
184 foreach ( $errors as $params ) {
185 $originalParams = $params;
188 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
190 # @todo FIXME: Sometimes get nested arrays for $params,
191 # which leads to E_NOTICEs
192 $spec = implode(
"\t", $params );
193 $out[$spec] = $originalParams;
196 return array_values( $out );
209 $keys = array_keys( $array );
210 $offsetByKey = array_flip(
$keys );
212 $offset = $offsetByKey[$after];
215 $before = array_slice( $array, 0, $offset + 1,
true );
216 $after = array_slice( $array, $offset + 1, count( $array ) - $offset,
true );
218 $output = $before + $insert + $after;
232 if ( is_object( $objOrArray ) ) {
233 $objOrArray = get_object_vars( $objOrArray );
235 foreach ( $objOrArray as $key => $value ) {
236 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
240 $array[$key] = $value;
259 $max = mt_getrandmax() + 1;
260 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12,
'.',
'' );
276 for ( $n = 0; $n < $length; $n += 7 ) {
277 $str .= sprintf(
'%07x', mt_rand() & 0xfffffff );
279 return substr( $str, 0, $length );
312 if ( is_null(
$s ) ) {
318 if ( is_null( $needle ) ) {
319 $needle = [
'%3B',
'%40',
'%24',
'%21',
'%2A',
'%28',
'%29',
'%2C',
'%2F',
'%7E' ];
320 if ( !isset( $_SERVER[
'SERVER_SOFTWARE'] ) ||
321 ( strpos( $_SERVER[
'SERVER_SOFTWARE'],
'Microsoft-IIS/7' ) ===
false )
327 $s = urlencode(
$s );
330 [
';',
'@',
'$',
'!',
'*',
'(',
')',
',',
'/',
'~',
':' ],
348 if ( !is_null( $array2 ) ) {
349 $array1 = $array1 + $array2;
353 foreach ( $array1 as $key => $value ) {
354 if ( !is_null( $value ) && $value !==
false ) {
358 if ( $prefix !==
'' ) {
359 $key = $prefix .
"[$key]";
361 if ( is_array( $value ) ) {
363 foreach ( $value as $k => $v ) {
364 $cgi .= $firstTime ?
'' :
'&';
365 if ( is_array( $v ) ) {
368 $cgi .= urlencode( $key .
"[$k]" ) .
'=' . urlencode( $v );
373 if ( is_object( $value ) ) {
374 $value = $value->__toString();
376 $cgi .= urlencode( $key ) .
'=' . urlencode( $value );
393 if ( isset( $query[0] ) && $query[0] ==
'?' ) {
394 $query = substr( $query, 1 );
396 $bits = explode(
'&', $query );
398 foreach ( $bits as $bit ) {
402 if ( strpos( $bit,
'=' ) ===
false ) {
407 list( $key, $value ) = explode(
'=', $bit );
409 $key = urldecode( $key );
410 $value = urldecode( $value );
411 if ( strpos( $key,
'[' ) !==
false ) {
412 $keys = array_reverse( explode(
'[', $key ) );
413 $key = array_pop(
$keys );
415 foreach (
$keys as $k ) {
416 $k = substr( $k, 0, -1 );
417 $temp = [ $k => $temp ];
419 if ( isset( $ret[$key] ) ) {
420 $ret[$key] = array_merge( $ret[$key], $temp );
440 if ( is_array( $query ) ) {
443 if ( $query !=
'' ) {
446 $hashPos = strpos( $url,
'#' );
447 if ( $hashPos !==
false ) {
448 $fragment = substr( $url, $hashPos );
449 $url = substr( $url, 0, $hashPos );
453 if ( strpos( $url,
'?' ) ===
false ) {
461 if ( $fragment !==
false ) {
502 $defaultProto =
$wgRequest->getProtocol() .
'://';
508 $serverHasProto = $bits && $bits[
'scheme'] !=
'';
511 if ( $serverHasProto ) {
512 $defaultProto = $bits[
'scheme'] .
'://';
521 $defaultProtoWithoutSlashes = $defaultProto !==
null ? substr( $defaultProto, 0, -2 ) :
'';
523 if ( substr( $url, 0, 2 ) ==
'//' ) {
524 $url = $defaultProtoWithoutSlashes . $url;
525 } elseif ( substr( $url, 0, 1 ) ==
'/' ) {
528 if ( $serverHasProto ) {
529 $url = $serverUrl . $url;
534 if ( isset( $bits[
'port'] ) ) {
535 throw new Exception(
'A protocol-relative $wgServer may not contain a port number' );
537 $url = $defaultProtoWithoutSlashes . $serverUrl .
':' .
$wgHttpsPort . $url;
539 $url = $defaultProtoWithoutSlashes . $serverUrl . $url;
546 if ( $bits && isset( $bits[
'path'] ) ) {
552 } elseif ( substr( $url, 0, 1 ) !=
'/' ) {
553 # URL is a relative path
557 # Expanded URL is not valid.
571 return substr( $url, 0, -1 );
590 if ( isset( $urlParts[
'delimiter'] ) ) {
591 if ( isset( $urlParts[
'scheme'] ) ) {
592 $result .= $urlParts[
'scheme'];
595 $result .= $urlParts[
'delimiter'];
598 if ( isset( $urlParts[
'host'] ) ) {
599 if ( isset( $urlParts[
'user'] ) ) {
600 $result .= $urlParts[
'user'];
601 if ( isset( $urlParts[
'pass'] ) ) {
602 $result .=
':' . $urlParts[
'pass'];
607 $result .= $urlParts[
'host'];
609 if ( isset( $urlParts[
'port'] ) ) {
610 $result .=
':' . $urlParts[
'port'];
614 if ( isset( $urlParts[
'path'] ) ) {
615 $result .= $urlParts[
'path'];
618 if ( isset( $urlParts[
'query'] ) ) {
619 $result .=
'?' . $urlParts[
'query'];
622 if ( isset( $urlParts[
'fragment'] ) ) {
623 $result .=
'#' . $urlParts[
'fragment'];
644 $inputLength = strlen( $urlPath );
646 while ( $inputOffset < $inputLength ) {
647 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
648 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
649 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
650 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
653 if ( $prefixLengthTwo ==
'./' ) {
654 # Step A, remove leading "./"
656 } elseif ( $prefixLengthThree ==
'../' ) {
657 # Step A, remove leading "../"
659 } elseif ( ( $prefixLengthTwo ==
'/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
660 # Step B, replace leading "/.$" with "/"
662 $urlPath[$inputOffset] =
'/';
663 } elseif ( $prefixLengthThree ==
'/./' ) {
664 # Step B, replace leading "/./" with "/"
666 } elseif ( $prefixLengthThree ==
'/..' && ( $inputOffset + 3 == $inputLength ) ) {
667 # Step C, replace leading "/..$" with "/" and
668 # remove last path component in output
670 $urlPath[$inputOffset] =
'/';
672 } elseif ( $prefixLengthFour ==
'/../' ) {
673 # Step C, replace leading "/../" with "/" and
674 # remove last path component in output
677 } elseif ( ( $prefixLengthOne ==
'.' ) && ( $inputOffset + 1 == $inputLength ) ) {
678 # Step D, remove "^.$"
680 } elseif ( ( $prefixLengthTwo ==
'..' ) && ( $inputOffset + 2 == $inputLength ) ) {
681 # Step D, remove "^..$"
684 # Step E, move leading path segment to output
685 if ( $prefixLengthOne ==
'/' ) {
686 $slashPos = strpos( $urlPath,
'/', $inputOffset + 1 );
688 $slashPos = strpos( $urlPath,
'/', $inputOffset );
690 if ( $slashPos ===
false ) {
691 $output .= substr( $urlPath, $inputOffset );
692 $inputOffset = $inputLength;
694 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
695 $inputOffset += $slashPos - $inputOffset;
700 $slashPos = strrpos(
$output,
'/' );
701 if ( $slashPos ===
false ) {
723 static $withProtRel =
null, $withoutProtRel =
null;
724 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
725 if ( !is_null( $cachedValue ) ) {
735 if ( $includeProtocolRelative || $protocol !==
'//' ) {
736 $protocols[] = preg_quote( $protocol,
'/' );
740 $retval = implode(
'|', $protocols );
750 if ( $includeProtocolRelative ) {
751 $withProtRel = $retval;
753 $withoutProtRel = $retval;
799 $wasRelative = substr( $url, 0, 2 ) ==
'//';
800 if ( $wasRelative ) {
803 AtEase::suppressWarnings();
804 $bits = parse_url( $url );
805 AtEase::restoreWarnings();
808 if ( !$bits || !isset( $bits[
'scheme'] ) ) {
813 $bits[
'scheme'] = strtolower( $bits[
'scheme'] );
817 $bits[
'delimiter'] =
'://';
819 $bits[
'delimiter'] =
':';
822 if ( isset( $bits[
'path'] ) ) {
823 $bits[
'host'] = $bits[
'path'];
831 if ( !isset( $bits[
'host'] ) ) {
835 if ( isset( $bits[
'path'] ) ) {
837 if ( substr( $bits[
'path'], 0, 1 ) !==
'/' ) {
838 $bits[
'path'] =
'/' . $bits[
'path'];
846 if ( $wasRelative ) {
847 $bits[
'scheme'] =
'';
848 $bits[
'delimiter'] =
'//';
864 return preg_replace_callback(
865 '/((?:%[89A-F][0-9A-F])+)/i',
881 if ( is_array( $bits ) && isset( $bits[
'host'] ) ) {
882 $host =
'.' . $bits[
'host'];
883 foreach ( (array)$domains as $domain ) {
884 $domain =
'.' . $domain;
885 if ( substr( $host, -strlen( $domain ) ) === $domain ) {
921 $text = trim( $text );
924 $context[
'seconds_elapsed'] = sprintf(
926 microtime(
true ) - $_SERVER[
'REQUEST_TIME_FLOAT']
930 ( memory_get_usage(
true ) / ( 1024 * 1024 ) )
937 $context[
'private'] = ( $dest ===
false || $dest ===
'private' );
939 $logger = LoggerFactory::getInstance(
'wfDebug' );
954 if ( ( isset( $_GET[
'action'] ) && $_GET[
'action'] ==
'raw' )
956 isset( $_SERVER[
'SCRIPT_NAME'] )
957 && substr( $_SERVER[
'SCRIPT_NAME'], -8 ) ==
'load.php'
973 $mem = memory_get_usage();
975 $mem = floor( $mem / 1024 ) .
' KiB';
979 wfDebug(
"Memory usage: $mem\n" );
1008 $logGroup, $text, $dest =
'all', array
$context = []
1010 $text = trim( $text );
1012 $logger = LoggerFactory::getInstance( $logGroup );
1013 $context[
'private'] = ( $dest ===
false || $dest ===
'private' );
1026 $logger = LoggerFactory::getInstance(
'wfLogDBError' );
1027 $logger->error( trim( $text ),
$context );
1044 function wfDeprecated( $function, $version =
false, $component =
false, $callerOffset = 2 ) {
1045 if ( is_string( $version ) || is_bool( $version ) ) {
1048 throw new Exception(
1049 "MediaWiki version must either be a string or a boolean. " .
1050 "Example valid version: '1.33'"
1065 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1078 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1094 $profiler->logData();
1098 MediaWikiServices::getInstance()->getStatsdDataFactory(),
1117 $ctx = [
'elapsed' => $request->getElapsedTime() ];
1118 if ( !empty( $_SERVER[
'HTTP_X_FORWARDED_FOR'] ) ) {
1119 $ctx[
'forwarded_for'] = $_SERVER[
'HTTP_X_FORWARDED_FOR'];
1121 if ( !empty( $_SERVER[
'HTTP_CLIENT_IP'] ) ) {
1122 $ctx[
'client_ip'] = $_SERVER[
'HTTP_CLIENT_IP'];
1124 if ( !empty( $_SERVER[
'HTTP_FROM'] ) ) {
1125 $ctx[
'from'] = $_SERVER[
'HTTP_FROM'];
1127 if ( isset( $ctx[
'forwarded_for'] ) ||
1128 isset( $ctx[
'client_ip'] ) ||
1129 isset( $ctx[
'from'] ) ) {
1131 $ctx[
'proxy'] = $_SERVER[
'REMOTE_ADDR'];
1138 $ctx[
'anon'] = $user->isItemLoaded(
'id' ) && $user->isAnon();
1143 $ctx[
'url'] = urldecode( $request->getRequestURL() );
1144 }
catch ( Exception $ignored ) {
1148 $ctx[
'output'] = $profiler->getOutput();
1150 $log = LoggerFactory::getInstance(
'profileoutput' );
1151 $log->info(
"Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1162 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1163 $stats->updateCount( $key, $count );
1172 return MediaWikiServices::getInstance()->getReadOnlyMode()
1185 return MediaWikiServices::getInstance()->getReadOnlyMode()
1196 return MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1216 # Identify which language to get or create a language object for.
1217 # Using is_object here due to Stub objects.
1218 if ( is_object( $langcode ) ) {
1219 # Great, we already have the object (hopefully)!
1225 # $langcode is the language code of the wikis content language object.
1226 # or it is a boolean and value is true
1227 return MediaWikiServices::getInstance()->getContentLanguage();
1231 if ( $langcode ===
false || $langcode ===
$wgLang->getCode() ) {
1232 # $langcode is the language code of user language object.
1233 # or it was a boolean and value is false
1238 if ( in_array( $langcode, $validCodes ) ) {
1239 # $langcode corresponds to a valid language.
1243 # $langcode is a string, but not a valid language code; use content language.
1244 wfDebug(
"Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1245 return MediaWikiServices::getInstance()->getContentLanguage();
1265 $message =
new Message( $key );
1269 $message->params( ...$params );
1288 return Message::newFallbackSequence( ...
$keys );
1300 # Fix windows line-endings
1301 # Some messages are split with explode("\n", $msg)
1302 $message = str_replace(
"\r",
'', $message );
1306 if ( is_array(
$args[0] ) ) {
1309 $replacementKeys = [];
1310 foreach (
$args as $n => $param ) {
1311 $replacementKeys[
'$' . ( $n + 1 )] = $param;
1313 $message = strtr( $message, $replacementKeys );
1328 if ( is_null( $host ) ) {
1329 # Hostname overriding
1332 # Set static and skip any detection
1337 if ( function_exists(
'posix_uname' ) ) {
1339 $uname = posix_uname();
1343 if ( is_array( $uname ) && isset( $uname[
'nodename'] ) ) {
1344 $host = $uname[
'nodename'];
1345 } elseif ( getenv(
'COMPUTERNAME' ) ) {
1346 # Windows computer name
1347 $host = getenv(
'COMPUTERNAME' );
1349 # This may be a virtual server.
1350 $host = $_SERVER[
'SERVER_NAME'];
1369 $elapsed = ( microtime(
true ) - $_SERVER[
'REQUEST_TIME_FLOAT'] );
1371 $responseTime = round( $elapsed * 1000 );
1372 $reportVars = [
'wgBackendResponseTime' => $responseTime ];
1390 static $disabled =
null;
1392 if ( is_null( $disabled ) ) {
1393 $disabled = !function_exists(
'debug_backtrace' );
1395 wfDebug(
"debug_backtrace() is disabled\n" );
1403 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1405 return array_slice( debug_backtrace(), 1 );
1420 if ( $raw ===
null ) {
1425 $frameFormat =
"%s line %s calls %s()\n";
1426 $traceFormat =
"%s";
1428 $frameFormat =
"<li>%s line %s calls %s()</li>\n";
1429 $traceFormat =
"<ul>\n%s</ul>\n";
1432 $frames = array_map(
function ( $frame ) use ( $frameFormat ) {
1433 $file = !empty( $frame[
'file'] ) ? basename( $frame[
'file'] ) :
'-';
1434 $line = $frame[
'line'] ??
'-';
1435 $call = $frame[
'function'];
1436 if ( !empty( $frame[
'class'] ) ) {
1437 $call = $frame[
'class'] . $frame[
'type'] . $call;
1439 return sprintf( $frameFormat,
$file,
$line, $call );
1442 return sprintf( $traceFormat, implode(
'', $frames ) );
1456 if ( isset( $backtrace[$level] ) ) {
1472 if ( !$limit || $limit > count( $trace ) - 1 ) {
1473 $limit = count( $trace ) - 1;
1475 $trace = array_slice( $trace, -$limit - 1, $limit );
1476 return implode(
'/', array_map(
'wfFormatStackFrame', $trace ) );
1486 if ( !isset( $frame[
'function'] ) ) {
1487 return 'NO_FUNCTION_GIVEN';
1489 return isset( $frame[
'class'] ) && isset( $frame[
'type'] ) ?
1490 $frame[
'class'] . $frame[
'type'] . $frame[
'function'] :
1504 return wfMessage(
'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1517 static $result =
null;
1518 if ( $result ===
null || $force ) {
1520 if ( isset( $_SERVER[
'HTTP_ACCEPT_ENCODING'] ) ) {
1521 # @todo FIXME: We may want to blacklist some broken browsers
1524 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1525 $_SERVER[
'HTTP_ACCEPT_ENCODING'],
1529 if ( isset( $m[2] ) && ( $m[1] ==
'q' ) && ( $m[2] == 0 ) ) {
1533 wfDebug(
"wfClientAcceptsGzip: client accepts gzip.\n" );
1553 static $repl =
null, $repl2 =
null;
1554 if ( $repl ===
null || defined(
'MW_PARSER_TEST' ) || defined(
'MW_PHPUNIT_TEST' ) ) {
1558 '"' =>
'"',
'&' =>
'&',
"'" =>
''',
'<' =>
'<',
1559 '=' =>
'=',
'>' =>
'>',
'[' =>
'[',
']' =>
']',
1560 '{' =>
'{',
'|' =>
'|',
'}' =>
'}',
';' =>
';',
1561 "\n#" =>
"\n#",
"\r#" =>
"\r#",
1562 "\n*" =>
"\n*",
"\r*" =>
"\r*",
1563 "\n:" =>
"\n:",
"\r:" =>
"\r:",
1564 "\n " =>
"\n ",
"\r " =>
"\r ",
1565 "\n\n" =>
"\n ",
"\r\n" =>
" \n",
1566 "\n\r" =>
"\n ",
"\r\r" =>
"\r ",
1567 "\n\t" =>
"\n	",
"\r\t" =>
"\r	",
1568 "\n----" =>
"\n----",
"\r----" =>
"\r----",
1569 '__' =>
'__',
'://' =>
'://',
1574 foreach ( $magicLinks as $magic ) {
1575 $repl[
"$magic "] =
"$magic ";
1576 $repl[
"$magic\t"] =
"$magic	";
1577 $repl[
"$magic\r"] =
"$magic ";
1578 $repl[
"$magic\n"] =
"$magic ";
1579 $repl[
"$magic\f"] =
"$magic";
1586 if ( substr( $prot, -1 ) ===
':' ) {
1587 $repl2[] = preg_quote( substr( $prot, 0, -1 ),
'/' );
1590 $repl2 = $repl2 ?
'/\b(' . implode(
'|', $repl2 ) .
'):/i' :
'/^(?!)/';
1592 $text = substr( strtr(
"\n$text", $repl ), 1 );
1593 $text = preg_replace( $repl2,
'$1:', $text );
1609 if ( !is_null(
$source ) || $force ) {
1625 $temp = (bool)( $dest & $bit );
1626 if ( !is_null( $state ) ) {
1644 $s = str_replace(
"\n",
"<br />\n", var_export( $var,
true ) .
"\n" );
1645 if ( headers_sent() || !isset(
$wgOut ) || !is_object(
$wgOut ) ) {
1664 $wgOut->sendCacheControl();
1668 header(
'Content-type: text/html; charset=utf-8' );
1669 print
'<!DOCTYPE html>' .
1670 '<html><head><title>' .
1671 htmlspecialchars( $label ) .
1672 '</title></head><body><h1>' .
1673 htmlspecialchars( $label ) .
1675 nl2br( htmlspecialchars( $desc ) ) .
1676 "</p></body></html>\n";
1697 if ( $resetGzipEncoding ) {
1703 while (
$status = ob_get_status() ) {
1704 if ( isset(
$status[
'flags'] ) ) {
1705 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1706 $deleteable = (
$status[
'flags'] & $flags ) === $flags;
1707 } elseif ( isset(
$status[
'del'] ) ) {
1711 $deleteable =
$status[
'type'] !== 0;
1713 if ( !$deleteable ) {
1718 if (
$status[
'name'] ===
'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1722 if ( !ob_end_clean() ) {
1727 if ( $resetGzipEncoding &&
$status[
'name'] ==
'ob_gzhandler' ) {
1730 header_remove(
'Content-Encoding' );
1761 # No arg means accept anything (per HTTP spec)
1763 return [ $def => 1.0 ];
1768 $parts = explode(
',', $accept );
1770 foreach ( $parts as $part ) {
1771 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1772 $values = explode(
';', trim( $part ) );
1774 if ( count( $values ) == 1 ) {
1775 $prefs[$values[0]] = 1.0;
1776 } elseif ( preg_match(
'/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1777 $prefs[$values[0]] = floatval( $match[1] );
1797 if ( array_key_exists(
$type, $avail ) ) {
1800 $mainType = explode(
'/',
$type )[0];
1801 if ( array_key_exists(
"$mainType/*", $avail ) ) {
1802 return "$mainType/*";
1803 } elseif ( array_key_exists(
'*/*', $avail ) ) {
1828 foreach ( array_keys( $sprefs ) as
$type ) {
1829 $subType = explode(
'/',
$type )[1];
1830 if ( $subType !=
'*' ) {
1833 $combine[
$type] = $sprefs[
$type] * $cprefs[$ckey];
1838 foreach ( array_keys( $cprefs ) as
$type ) {
1839 $subType = explode(
'/',
$type )[1];
1840 if ( $subType !=
'*' && !array_key_exists(
$type, $sprefs ) ) {
1843 $combine[
$type] = $sprefs[$skey] * $cprefs[
$type];
1851 foreach ( array_keys( $combine ) as
$type ) {
1852 if ( $combine[
$type] > $bestq ) {
1854 $bestq = $combine[
$type];
1870 $ret = MWTimestamp::convert( $outputtype, $ts );
1871 if ( $ret ===
false ) {
1872 wfDebug(
"wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
1886 if ( is_null( $ts ) ) {
1899 return MWTimestamp::now( TS_MW );
1908 static $isWindows =
null;
1909 if ( $isWindows ===
null ) {
1910 $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) ===
'WIN';
1933 return PHP_SAPI ===
'cli' || PHP_SAPI ===
'phpdbg';
1970 throw new MWException( __FUNCTION__ .
" given storage path '$dir'." );
1973 if ( !is_null( $caller ) ) {
1974 wfDebug(
"$caller: called wfMkdirParents($dir)\n" );
1977 if ( strval( $dir ) ===
'' || is_dir( $dir ) ) {
1981 $dir = str_replace( [
'\\',
'/' ], DIRECTORY_SEPARATOR, $dir );
1983 if ( is_null( $mode ) ) {
1988 AtEase::suppressWarnings();
1989 $ok = mkdir( $dir, $mode,
true );
1990 AtEase::restoreWarnings();
1994 if ( is_dir( $dir ) ) {
1999 wfLogWarning( sprintf(
"failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
2010 wfDebug( __FUNCTION__ .
"( $dir )\n" );
2012 if ( is_dir( $dir ) ) {
2013 $objects = scandir( $dir );
2014 foreach ( $objects as $object ) {
2015 if ( $object !=
"." && $object !=
".." ) {
2016 if ( filetype( $dir .
'/' . $object ) ==
"dir" ) {
2019 unlink( $dir .
'/' . $object );
2035 $ret = sprintf(
"%.${acc}f", $nr );
2036 return $round ? round( (
float)$ret, $acc ) .
'%' :
"$ret%";
2079 $val = strtolower( $val );
2084 || preg_match(
"/^\s*[+-]?0*[1-9]/", $val );
2100 return Shell::escape( ...
$args );
2128 $limits = [], $options = []
2130 if ( Shell::isDisabled() ) {
2133 return 'Unable to run external programs, proc_open() is disabled.';
2136 if ( is_array( $cmd ) ) {
2137 $cmd = Shell::escape( $cmd );
2140 $includeStderr = isset( $options[
'duplicateStderr'] ) && $options[
'duplicateStderr'];
2141 $profileMethod = $options[
'profileMethod'] ??
wfGetCaller();
2144 $result = Shell::command( [] )
2145 ->unsafeParams( (array)$cmd )
2146 ->environment( $environ )
2148 ->includeStderr( $includeStderr )
2149 ->profileMethod( $profileMethod )
2151 ->restrict( Shell::RESTRICT_NONE )
2158 $retval = $result->getExitCode();
2160 return $result->getStdout();
2181 return wfShellExec( $cmd, $retval, $environ, $limits,
2182 [
'duplicateStderr' =>
true,
'profileMethod' =>
wfGetCaller() ] );
2204 Hooks::run(
'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2205 $cmd = [ $options[
'php'] ??
$wgPhpCli ];
2206 if ( isset( $options[
'wrapper'] ) ) {
2207 $cmd[] = $options[
'wrapper'];
2211 return Shell::escape( array_merge( $cmd, $parameters ) );
2225 function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult =
null ) {
2228 # This check may also protect against code injection in
2229 # case of broken installations.
2230 AtEase::suppressWarnings();
2232 AtEase::restoreWarnings();
2234 if ( !$haveDiff3 ) {
2235 wfDebug(
"diff3 not found\n" );
2239 # Make temporary files
2241 $oldtextFile = fopen( $oldtextName = tempnam( $td,
'merge-old-' ),
'w' );
2242 $mytextFile = fopen( $mytextName = tempnam( $td,
'merge-mine-' ),
'w' );
2243 $yourtextFile = fopen( $yourtextName = tempnam( $td,
'merge-your-' ),
'w' );
2245 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2246 # a newline character. To avoid this, we normalize the trailing whitespace before
2247 # creating the diff.
2249 fwrite( $oldtextFile, rtrim( $old ) .
"\n" );
2250 fclose( $oldtextFile );
2251 fwrite( $mytextFile, rtrim( $mine ) .
"\n" );
2252 fclose( $mytextFile );
2253 fwrite( $yourtextFile, rtrim( $yours ) .
"\n" );
2254 fclose( $yourtextFile );
2256 # Check for a conflict
2257 $cmd = Shell::escape(
$wgDiff3,
'-a',
'--overlap-only', $mytextName,
2258 $oldtextName, $yourtextName );
2259 $handle = popen( $cmd,
'r' );
2261 $mergeAttemptResult =
'';
2263 $data = fread( $handle, 8192 );
2264 if ( strlen( $data ) == 0 ) {
2267 $mergeAttemptResult .= $data;
2271 $conflict = $mergeAttemptResult !==
'';
2274 $cmd = Shell::escape(
$wgDiff3,
'-a',
'-e',
'--merge', $mytextName,
2275 $oldtextName, $yourtextName );
2276 $handle = popen( $cmd,
'r' );
2279 $data = fread( $handle, 8192 );
2280 if ( strlen( $data ) == 0 ) {
2286 unlink( $mytextName );
2287 unlink( $oldtextName );
2288 unlink( $yourtextName );
2290 if ( $result ===
'' && $old !==
'' && !$conflict ) {
2291 wfDebug(
"Unexpected null result from diff3. Command: $cmd\n" );
2308 function wfDiff( $before, $after, $params =
'-u' ) {
2309 if ( $before == $after ) {
2314 AtEase::suppressWarnings();
2316 AtEase::restoreWarnings();
2318 # This check may also protect against code injection in
2319 # case of broken installations.
2321 wfDebug(
"diff executable not found\n" );
2322 $diffs =
new Diff( explode(
"\n", $before ), explode(
"\n", $after ) );
2324 return $format->format( $diffs );
2327 # Make temporary files
2329 $oldtextFile = fopen( $oldtextName = tempnam( $td,
'merge-old-' ),
'w' );
2330 $newtextFile = fopen( $newtextName = tempnam( $td,
'merge-your-' ),
'w' );
2332 fwrite( $oldtextFile, $before );
2333 fclose( $oldtextFile );
2334 fwrite( $newtextFile, $after );
2335 fclose( $newtextFile );
2338 $cmd =
"$wgDiff " . $params .
' ' . Shell::escape( $oldtextName, $newtextName );
2340 $h = popen( $cmd,
'r' );
2342 unlink( $oldtextName );
2343 unlink( $newtextName );
2344 throw new Exception( __METHOD__ .
'(): popen() failed' );
2350 $data = fread( $h, 8192 );
2351 if ( strlen( $data ) == 0 ) {
2359 unlink( $oldtextName );
2360 unlink( $newtextName );
2363 $diff_lines = explode(
"\n", $diff );
2364 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0],
'---' ) === 0 ) {
2365 unset( $diff_lines[0] );
2367 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1],
'+++' ) === 0 ) {
2368 unset( $diff_lines[1] );
2371 $diff = implode(
"\n", $diff_lines );
2389 if ( $suffix ==
'' ) {
2392 $encSuffix =
'(?:' . preg_quote( $suffix,
'#' ) .
')?';
2396 if ( preg_match(
"#([^/\\\\]*?){$encSuffix}[/\\\\]*$#",
$path,
$matches ) ) {
2414 $path = str_replace(
'/', DIRECTORY_SEPARATOR,
$path );
2415 $from = str_replace(
'/', DIRECTORY_SEPARATOR, $from );
2419 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2421 $pieces = explode( DIRECTORY_SEPARATOR, dirname(
$path ) );
2422 $against = explode( DIRECTORY_SEPARATOR, $from );
2424 if ( $pieces[0] !== $against[0] ) {
2431 while ( count( $pieces ) && count( $against )
2432 && $pieces[0] == $against[0] ) {
2433 array_shift( $pieces );
2434 array_shift( $against );
2438 while ( count( $against ) ) {
2439 array_unshift( $pieces,
'..' );
2440 array_shift( $against );
2445 return implode( DIRECTORY_SEPARATOR, $pieces );
2461 session_id( $sessionId );
2464 $session = SessionManager::getGlobalSession();
2465 $session->persist();
2467 if ( session_id() !== $session->getId() ) {
2468 session_id( $session->getId() );
2470 AtEase::quietCall(
'session_start' );
2482 $file =
"$IP/serialized/$name";
2483 if ( file_exists(
$file ) ) {
2514 $keyspace = $prefix ?
"$db-$prefix" : $db;
2544 return "$wgDBname-$wgDBprefix";
2575 function wfGetDB( $db, $groups = [], $wiki =
false ) {
2576 return wfGetLB( $wiki )->getMaintenanceConnectionRef( $db, $groups, $wiki );
2589 if ( $wiki ===
false ) {
2590 return MediaWikiServices::getInstance()->getDBLoadBalancer();
2592 $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2593 return $factory->getMainLB( $wiki );
2605 return MediaWikiServices::getInstance()->getRepoGroup()->findFile(
$title, $options );
2617 return MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile(
$title );
2644 if ( $script ===
'index' ) {
2646 } elseif ( $script ===
'load' ) {
2649 return "{$wgScriptPath}/{$script}.php";
2659 if ( isset( $_SERVER[
'SCRIPT_NAME'] ) ) {
2670 return $_SERVER[
'SCRIPT_NAME'];
2672 return $_SERVER[
'URL'];
2684 return $value ?
'true' :
'false';
2719 $ifWritesSince =
null, $wiki =
false, $cluster =
false, $timeout =
null
2721 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2723 if ( $cluster ===
'*' ) {
2726 } elseif ( $wiki ===
false ) {
2727 $domain = $lbFactory->getLocalDomainID();
2733 'domain' => $domain,
2734 'cluster' => $cluster,
2736 'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince :
null
2738 if ( $timeout !==
null ) {
2739 $opts[
'timeout'] = $timeout;
2742 return $lbFactory->waitForReplication( $opts );
2756 $name = preg_replace(
2774 if ( $oldLimit != -1 ) {
2776 if ( $newLimit == -1 ) {
2777 wfDebug(
"Removing PHP's memory limit\n" );
2778 Wikimedia\suppressWarnings();
2779 ini_set(
'memory_limit', $newLimit );
2780 Wikimedia\restoreWarnings();
2781 } elseif ( $newLimit > $oldLimit ) {
2782 wfDebug(
"Raising PHP's memory limit to $newLimit bytes\n" );
2783 Wikimedia\suppressWarnings();
2784 ini_set(
'memory_limit', $newLimit );
2785 Wikimedia\restoreWarnings();
2799 $timeLimit = (int)ini_get(
'max_execution_time' );
2805 ignore_user_abort(
true );
2818 $string = trim( $string );
2819 if ( $string ===
'' ) {
2822 $last = $string[strlen( $string ) - 1];
2823 $val = intval( $string );
2887 if ( $length !==
false ) {
2888 $realLen = strlen( $data );
2889 if ( $realLen < $length ) {
2890 throw new MWException(
"Tried to use wfUnpack on a "
2891 .
"string of length $realLen, but needed one "
2892 .
"of at least length $length."
2897 Wikimedia\suppressWarnings();
2898 $result = unpack( $format, $data );
2899 Wikimedia\restoreWarnings();
2901 if ( $result ===
false ) {
2903 throw new MWException(
"unpack could not unpack binary data" );
2925 $services = MediaWikiServices::getInstance();
2926 if ( $blacklist !==
null ) {
2927 wfDeprecated( __METHOD__ .
' with $blacklist parameter',
'1.34' );
2929 function () use ( $blacklist ) {
2932 $services->getLocalServerObjectCache(),
2933 $services->getRepoGroup(),
2934 $services->getTitleParser()
2935 ) )->isBadFile( $name, $contextTitle ?:
null );
2937 return $services->getBadFileLookup()->isBadFile( $name, $contextTitle ?:
null );
2949 Hooks::run(
'CanIPUseHTTPS', [ $ip, &$canDo ] );
2950 return (
bool)$canDo;
2962 $infinityValues = [
'infinite',
'indefinite',
'infinity',
'never' ];
2963 return in_array( $str, $infinityValues );
2983 $multipliers = [ 1 ];
2987 $multipliers[] = 1.5;
2991 $handler =
$file->getHandler();
2992 if ( !$handler || !isset( $params[
'width'] ) ) {
2997 if ( isset( $params[
'page'] ) ) {
2998 $basicParams[
'page'] = $params[
'page'];
3004 foreach ( $multipliers as $multiplier ) {
3005 $thumbLimits = array_merge( $thumbLimits, array_map(
3006 function ( $width ) use ( $multiplier ) {
3007 return round( $width * $multiplier );
3010 $imageLimits = array_merge( $imageLimits, array_map(
3011 function ( $pair ) use ( $multiplier ) {
3013 round( $pair[0] * $multiplier ),
3014 round( $pair[1] * $multiplier ),
3021 if ( in_array( $params[
'width'], $thumbLimits ) ) {
3022 $normalParams = $basicParams + [
'width' => $params[
'width'] ];
3024 $handler->normaliseParams(
$file, $normalParams );
3028 foreach ( $imageLimits as $pair ) {
3029 $normalParams = $basicParams + [
'width' => $pair[0],
'height' => $pair[1] ];
3032 $handler->normaliseParams(
$file, $normalParams );
3034 if ( $normalParams[
'width'] == $params[
'width'] ) {
3045 foreach ( $params as $key => $value ) {
3046 if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3068 foreach ( $baseArray as $name => &$groupVal ) {
3069 if ( isset( $newValues[$name] ) ) {
3070 $groupVal += $newValues[$name];
3074 $baseArray += $newValues;
3088 if ( !function_exists(
'getrusage' ) ) {
3090 } elseif ( defined(
'HHVM_VERSION' ) && PHP_OS ===
'Linux' ) {
3091 return getrusage( 2 );
3093 return getrusage( 0 );