23 if ( !defined(
'MEDIAWIKI' ) ) {
24 die(
"This file is part of MediaWiki, it is not a valid entry point" );
32 use Wikimedia\AtEase\AtEase;
34 use Wikimedia\WrappedString;
49 $path =
"$wgExtensionDirectory/$ext/extension.json";
70 foreach ( $exts as
$ext ) {
71 $registry->queue(
"$wgExtensionDirectory/$ext/extension.json" );
86 $path =
"$wgStyleDirectory/$skin/skin.json";
101 foreach ( $skins as $skin ) {
102 $registry->queue(
"$wgStyleDirectory/$skin/skin.json" );
113 return array_udiff( $a, $b,
'wfArrayDiff2_cmp' );
122 if ( is_string( $a ) && is_string( $b ) ) {
123 return strcmp( $a, $b );
124 } elseif ( count( $a ) !== count( $b ) ) {
125 return count( $a ) <=> count( $b );
129 while ( key( $a ) !==
null && key( $b ) !==
null ) {
130 $valueA = current( $a );
131 $valueB = current( $b );
132 $cmp = strcmp( $valueA, $valueB );
156 if ( $changed ===
null ) {
157 throw new MWException(
'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
159 if ( $default[$key] !== $value ) {
160 $changed[$key] = $value;
185 foreach (
$args as $errors ) {
186 foreach ( $errors as $params ) {
187 $originalParams = $params;
190 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
192 # @todo FIXME: Sometimes get nested arrays for $params,
193 # which leads to E_NOTICEs
194 $spec = implode(
"\t", $params );
195 $out[$spec] = $originalParams;
198 return array_values( $out );
211 $keys = array_keys( $array );
212 $offsetByKey = array_flip(
$keys );
214 $offset = $offsetByKey[$after];
217 $before = array_slice( $array, 0, $offset + 1,
true );
218 $after = array_slice( $array, $offset + 1, count( $array ) - $offset,
true );
220 $output = $before + $insert + $after;
235 if ( is_object( $objOrArray ) ) {
236 $objOrArray = get_object_vars( $objOrArray );
238 foreach ( $objOrArray as $key => $value ) {
239 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
243 $array[$key] = $value;
262 $max = mt_getrandmax() + 1;
263 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12,
'.',
'' );
279 for ( $n = 0; $n < $length; $n += 7 ) {
280 $str .= sprintf(
'%07x', mt_rand() & 0xfffffff );
282 return substr( $str, 0, $length );
321 if ( $needle ===
null ) {
322 $needle = [
'%3B',
'%40',
'%24',
'%21',
'%2A',
'%28',
'%29',
'%2C',
'%2F',
'%7E' ];
323 if ( !isset( $_SERVER[
'SERVER_SOFTWARE'] ) ||
324 ( strpos( $_SERVER[
'SERVER_SOFTWARE'],
'Microsoft-IIS/7' ) ===
false )
330 $s = urlencode(
$s );
333 [
';',
'@',
'$',
'!',
'*',
'(',
')',
',',
'/',
'~',
':' ],
351 if ( $array2 !==
null ) {
356 foreach ( $array1 as $key => $value ) {
357 if ( $value !==
null && $value !==
false ) {
361 if ( $prefix !==
'' ) {
362 $key = $prefix .
"[$key]";
364 if ( is_array( $value ) ) {
366 foreach ( $value as $k => $v ) {
367 $cgi .= $firstTime ?
'' :
'&';
368 if ( is_array( $v ) ) {
371 $cgi .= urlencode( $key .
"[$k]" ) .
'=' . urlencode( $v );
376 if ( is_object( $value ) ) {
377 $value = $value->__toString();
379 $cgi .= urlencode( $key ) .
'=' . urlencode( $value );
396 if ( isset( $query[0] ) && $query[0] ==
'?' ) {
397 $query = substr( $query, 1 );
399 $bits = explode(
'&', $query );
401 foreach ( $bits as $bit ) {
405 if ( strpos( $bit,
'=' ) ===
false ) {
410 list( $key, $value ) = explode(
'=', $bit );
412 $key = urldecode( $key );
413 $value = urldecode( $value );
414 if ( strpos( $key,
'[' ) !==
false ) {
415 $keys = array_reverse( explode(
'[', $key ) );
416 $key = array_pop(
$keys );
418 foreach (
$keys as $k ) {
419 $k = substr( $k, 0, -1 );
420 $temp = [ $k => $temp ];
422 if ( isset( $ret[$key] ) ) {
423 $ret[$key] = array_merge( $ret[$key], $temp );
443 if ( is_array( $query ) ) {
446 if ( $query !=
'' ) {
449 $hashPos = strpos( $url,
'#' );
450 if ( $hashPos !==
false ) {
451 $fragment = substr( $url, $hashPos );
452 $url = substr( $url, 0, $hashPos );
456 if ( strpos( $url,
'?' ) ===
false ) {
464 if ( $fragment !==
false ) {
505 $defaultProto =
$wgRequest->getProtocol() .
'://';
511 $serverHasProto = $bits && $bits[
'scheme'] !=
'';
514 if ( $serverHasProto ) {
515 $defaultProto = $bits[
'scheme'] .
'://';
524 $defaultProtoWithoutSlashes = $defaultProto !==
null ? substr( $defaultProto, 0, -2 ) :
'';
526 if ( substr( $url, 0, 2 ) ==
'//' ) {
527 $url = $defaultProtoWithoutSlashes . $url;
528 } elseif ( substr( $url, 0, 1 ) ==
'/' ) {
531 if ( $serverHasProto ) {
532 $url = $serverUrl . $url;
537 if ( isset( $bits[
'port'] ) ) {
538 throw new Exception(
'A protocol-relative $wgServer may not contain a port number' );
540 $url = $defaultProtoWithoutSlashes . $serverUrl .
':' .
$wgHttpsPort . $url;
542 $url = $defaultProtoWithoutSlashes . $serverUrl . $url;
549 if ( $bits && isset( $bits[
'path'] ) ) {
555 } elseif ( substr( $url, 0, 1 ) !=
'/' ) {
556 # URL is a relative path
560 # Expanded URL is not valid.
574 return substr( $url, 0, -1 );
593 if ( isset( $urlParts[
'delimiter'] ) ) {
594 if ( isset( $urlParts[
'scheme'] ) ) {
595 $result .= $urlParts[
'scheme'];
598 $result .= $urlParts[
'delimiter'];
601 if ( isset( $urlParts[
'host'] ) ) {
602 if ( isset( $urlParts[
'user'] ) ) {
603 $result .= $urlParts[
'user'];
604 if ( isset( $urlParts[
'pass'] ) ) {
605 $result .=
':' . $urlParts[
'pass'];
610 $result .= $urlParts[
'host'];
612 if ( isset( $urlParts[
'port'] ) ) {
613 $result .=
':' . $urlParts[
'port'];
617 if ( isset( $urlParts[
'path'] ) ) {
618 $result .= $urlParts[
'path'];
621 if ( isset( $urlParts[
'query'] ) && $urlParts[
'query'] !==
'' ) {
622 $result .=
'?' . $urlParts[
'query'];
625 if ( isset( $urlParts[
'fragment'] ) ) {
626 $result .=
'#' . $urlParts[
'fragment'];
647 $inputLength = strlen( $urlPath );
649 while ( $inputOffset < $inputLength ) {
650 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
651 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
652 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
653 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
656 if ( $prefixLengthTwo ==
'./' ) {
657 # Step A, remove leading "./"
659 } elseif ( $prefixLengthThree ==
'../' ) {
660 # Step A, remove leading "../"
662 } elseif ( ( $prefixLengthTwo ==
'/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
663 # Step B, replace leading "/.$" with "/"
665 $urlPath[$inputOffset] =
'/';
666 } elseif ( $prefixLengthThree ==
'/./' ) {
667 # Step B, replace leading "/./" with "/"
669 } elseif ( $prefixLengthThree ==
'/..' && ( $inputOffset + 3 == $inputLength ) ) {
670 # Step C, replace leading "/..$" with "/" and
671 # remove last path component in output
673 $urlPath[$inputOffset] =
'/';
675 } elseif ( $prefixLengthFour ==
'/../' ) {
676 # Step C, replace leading "/../" with "/" and
677 # remove last path component in output
680 } elseif ( ( $prefixLengthOne ==
'.' ) && ( $inputOffset + 1 == $inputLength ) ) {
681 # Step D, remove "^.$"
683 } elseif ( ( $prefixLengthTwo ==
'..' ) && ( $inputOffset + 2 == $inputLength ) ) {
684 # Step D, remove "^..$"
687 # Step E, move leading path segment to output
688 if ( $prefixLengthOne ==
'/' ) {
689 $slashPos = strpos( $urlPath,
'/', $inputOffset + 1 );
691 $slashPos = strpos( $urlPath,
'/', $inputOffset );
693 if ( $slashPos ===
false ) {
694 $output .= substr( $urlPath, $inputOffset );
695 $inputOffset = $inputLength;
697 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
698 $inputOffset += $slashPos - $inputOffset;
703 $slashPos = strrpos( $output,
'/' );
704 if ( $slashPos ===
false ) {
707 $output = substr( $output, 0, $slashPos );
726 static $withProtRel =
null, $withoutProtRel =
null;
727 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
728 if ( $cachedValue !==
null ) {
738 if ( $includeProtocolRelative || $protocol !==
'//' ) {
739 $protocols[] = preg_quote( $protocol,
'/' );
743 $retval = implode(
'|', $protocols );
753 if ( $includeProtocolRelative ) {
754 $withProtRel = $retval;
756 $withoutProtRel = $retval;
802 $wasRelative = substr( $url, 0, 2 ) ==
'//';
803 if ( $wasRelative ) {
806 $bits = parse_url( $url );
809 if ( !$bits || !isset( $bits[
'scheme'] ) ) {
814 $bits[
'scheme'] = strtolower( $bits[
'scheme'] );
818 $bits[
'delimiter'] =
'://';
820 $bits[
'delimiter'] =
':';
823 if ( isset( $bits[
'path'] ) ) {
824 $bits[
'host'] = $bits[
'path'];
832 if ( !isset( $bits[
'host'] ) ) {
836 if ( isset( $bits[
'path'] ) ) {
838 if ( substr( $bits[
'path'], 0, 1 ) !==
'/' ) {
839 $bits[
'path'] =
'/' . $bits[
'path'];
847 if ( $wasRelative ) {
848 $bits[
'scheme'] =
'';
849 $bits[
'delimiter'] =
'//';
865 return preg_replace_callback(
866 '/((?:%[89A-F][0-9A-F])+)/i',
882 if ( is_array( $bits ) && isset( $bits[
'host'] ) ) {
883 $host =
'.' . $bits[
'host'];
884 foreach ( (array)$domains as $domain ) {
885 $domain =
'.' . $domain;
886 if ( substr( $host, -strlen( $domain ) ) === $domain ) {
914 function wfDebug( $text, $dest =
'all', array $context = [] ) {
921 $text = trim( $text );
926 $context[
'private'] = ( $dest ===
false || $dest ===
'private' );
928 $logger = LoggerFactory::getInstance(
'wfDebug' );
929 $logger->debug( $text, $context );
943 if ( ( isset( $_GET[
'action'] ) && $_GET[
'action'] ==
'raw' )
961 $mem = memory_get_usage();
963 $mem = floor( $mem / 1024 ) .
' KiB';
967 wfDebug(
"Memory usage: $mem" );
996 $logGroup, $text, $dest =
'all', array $context = []
998 $text = trim( $text );
1000 $logger = LoggerFactory::getInstance( $logGroup );
1001 $context[
'private'] = ( $dest ===
false || $dest ===
'private' );
1002 $logger->info( $text, $context );
1014 $logger = LoggerFactory::getInstance(
'wfLogDBError' );
1015 $logger->error( trim( $text ), $context );
1033 function wfDeprecated( $function, $version =
false, $component =
false, $callerOffset = 2 ) {
1034 if ( is_string( $version ) || $version ===
false ) {
1037 throw new Exception(
1038 "MediaWiki version must either be a string or false. " .
1039 "Example valid version: '1.33'"
1065 function wfDeprecatedMsg( $msg, $version =
false, $component =
false, $callerOffset = 2 ) {
1067 $callerOffset ===
false ?
false : $callerOffset + 1 );
1080 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1093 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1105 $profiler->setContext( $context );
1106 $profiler->logData();
1110 MediaWikiServices::getInstance()->getStatsdDataFactory(),
1111 $context->getConfig()
1125 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1126 $stats->updateCount( $key, $count );
1135 return MediaWikiServices::getInstance()->getReadOnlyMode()
1148 return MediaWikiServices::getInstance()->getReadOnlyMode()
1161 return MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1181 # Identify which language to get or create a language object for.
1182 # Using is_object here due to Stub objects.
1183 if ( is_object( $langcode ) ) {
1184 # Great, we already have the object (hopefully)!
1189 $services = MediaWikiServices::getInstance();
1191 # $langcode is the language code of the wikis content language object.
1192 # or it is a boolean and value is true
1193 return $services->getContentLanguage();
1197 if ( $langcode ===
false || $langcode ===
$wgLang->getCode() ) {
1198 # $langcode is the language code of user language object.
1199 # or it was a boolean and value is false
1203 $validCodes = array_keys( $services->getLanguageNameUtils()->getLanguageNames() );
1204 if ( in_array( $langcode, $validCodes ) ) {
1205 # $langcode corresponds to a valid language.
1206 return $services->getLanguageFactory()->getLanguage( $langcode );
1209 # $langcode is a string, but not a valid language code; use content language.
1210 wfDebug(
"Invalid language code passed to wfGetLangObj, falling back to content language." );
1211 return $services->getContentLanguage();
1231 $message =
new Message( $key );
1235 $message->params( ...$params );
1266 # Fix windows line-endings
1267 # Some messages are split with explode("\n", $msg)
1268 $message = str_replace(
"\r",
'', $message );
1272 if ( is_array(
$args[0] ) ) {
1275 $replacementKeys = [];
1276 foreach (
$args as $n => $param ) {
1277 $replacementKeys[
'$' . ( $n + 1 )] = $param;
1279 $message = strtr( $message, $replacementKeys );
1300 return php_uname(
'n' ) ?:
'unknown';
1316 $elapsed = ( microtime(
true ) - $_SERVER[
'REQUEST_TIME_FLOAT'] );
1318 $responseTime = round( $elapsed * 1000 );
1319 $reportVars = [
'wgBackendResponseTime' => $responseTime ];
1343 static $disabled =
null;
1345 if ( $disabled ===
null ) {
1346 $disabled = !function_exists(
'debug_backtrace' );
1348 wfDebug(
"debug_backtrace() is disabled" );
1356 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1358 return array_slice( debug_backtrace(), 1 );
1373 if ( $raw ===
null ) {
1378 $frameFormat =
"%s line %s calls %s()\n";
1379 $traceFormat =
"%s";
1381 $frameFormat =
"<li>%s line %s calls %s()</li>\n";
1382 $traceFormat =
"<ul>\n%s</ul>\n";
1385 $frames = array_map(
function ( $frame ) use ( $frameFormat ) {
1386 $file = !empty( $frame[
'file'] ) ? basename( $frame[
'file'] ) :
'-';
1387 $line = $frame[
'line'] ??
'-';
1388 $call = $frame[
'function'];
1389 if ( !empty( $frame[
'class'] ) ) {
1390 $call = $frame[
'class'] . $frame[
'type'] . $call;
1392 return sprintf( $frameFormat,
$file,
$line, $call );
1395 return sprintf( $traceFormat, implode(
'', $frames ) );
1409 if ( isset( $backtrace[$level] ) ) {
1425 if ( !$limit || $limit > count( $trace ) - 1 ) {
1426 $limit = count( $trace ) - 1;
1428 $trace = array_slice( $trace, -$limit - 1, $limit );
1429 return implode(
'/', array_map(
'wfFormatStackFrame', $trace ) );
1439 if ( !isset( $frame[
'function'] ) ) {
1440 return 'NO_FUNCTION_GIVEN';
1442 return isset( $frame[
'class'] ) && isset( $frame[
'type'] ) ?
1443 $frame[
'class'] . $frame[
'type'] . $frame[
'function'] :
1457 return wfMessage(
'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1470 static $result =
null;
1471 if ( $result ===
null || $force ) {
1473 if ( isset( $_SERVER[
'HTTP_ACCEPT_ENCODING'] ) ) {
1474 # @todo FIXME: We may want to blacklist some broken browsers
1477 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1478 $_SERVER[
'HTTP_ACCEPT_ENCODING'],
1482 if ( isset( $m[2] ) && ( $m[1] ==
'q' ) && ( $m[2] == 0 ) ) {
1486 wfDebug(
"wfClientAcceptsGzip: client accepts gzip." );
1506 static $repl =
null, $repl2 =
null;
1507 if ( $repl ===
null || defined(
'MW_PARSER_TEST' ) || defined(
'MW_PHPUNIT_TEST' ) ) {
1511 '"' =>
'"',
'&' =>
'&',
"'" =>
''',
'<' =>
'<',
1512 '=' =>
'=',
'>' =>
'>',
'[' =>
'[',
']' =>
']',
1513 '{' =>
'{',
'|' =>
'|',
'}' =>
'}',
';' =>
';',
1514 "\n#" =>
"\n#",
"\r#" =>
"\r#",
1515 "\n*" =>
"\n*",
"\r*" =>
"\r*",
1516 "\n:" =>
"\n:",
"\r:" =>
"\r:",
1517 "\n " =>
"\n ",
"\r " =>
"\r ",
1518 "\n\n" =>
"\n ",
"\r\n" =>
" \n",
1519 "\n\r" =>
"\n ",
"\r\r" =>
"\r ",
1520 "\n\t" =>
"\n	",
"\r\t" =>
"\r	",
1521 "\n----" =>
"\n----",
"\r----" =>
"\r----",
1522 '__' =>
'__',
'://' =>
'://',
1527 foreach ( $magicLinks as $magic ) {
1528 $repl[
"$magic "] =
"$magic ";
1529 $repl[
"$magic\t"] =
"$magic	";
1530 $repl[
"$magic\r"] =
"$magic ";
1531 $repl[
"$magic\n"] =
"$magic ";
1532 $repl[
"$magic\f"] =
"$magic";
1539 if ( substr( $prot, -1 ) ===
':' ) {
1540 $repl2[] = preg_quote( substr( $prot, 0, -1 ),
'/' );
1543 $repl2 = $repl2 ?
'/\b(' . implode(
'|', $repl2 ) .
'):/i' :
'/^(?!)/';
1545 $text = substr( strtr(
"\n$text", $repl ), 1 );
1546 $text = preg_replace( $repl2,
'$1:', $text );
1562 if (
$source !==
null || $force ) {
1578 $temp = (bool)( $dest & $bit );
1579 if ( $state !==
null ) {
1597 $s = str_replace(
"\n",
"<br />\n", var_export( $var,
true ) .
"\n" );
1598 if ( headers_sent() || !isset(
$wgOut ) || !is_object(
$wgOut ) ) {
1617 $wgOut->sendCacheControl();
1621 header(
'Content-type: text/html; charset=utf-8' );
1623 print
'<!DOCTYPE html>' .
1624 '<html><head><title>' .
1625 htmlspecialchars( $label ) .
1626 '</title></head><body><h1>' .
1627 htmlspecialchars( $label ) .
1629 nl2br( htmlspecialchars( $desc ) ) .
1630 "</p></body></html>\n";
1631 header(
'Content-Length: ' . ob_get_length() );
1653 if ( $resetGzipEncoding ) {
1659 while ( $status = ob_get_status() ) {
1660 if ( isset( $status[
'flags'] ) ) {
1661 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1662 $deleteable = ( $status[
'flags'] & $flags ) === $flags;
1663 } elseif ( isset( $status[
'del'] ) ) {
1664 $deleteable = $status[
'del'];
1667 $deleteable = $status[
'type'] !== 0;
1669 if ( !$deleteable ) {
1674 if ( $status[
'name'] ===
'MediaWikiIntegrationTestCase::wfResetOutputBuffersBarrier' ) {
1678 if ( !ob_end_clean() ) {
1683 if ( $resetGzipEncoding && $status[
'name'] ==
'ob_gzhandler' ) {
1686 header_remove(
'Content-Encoding' );
1722 # No arg means accept anything (per HTTP spec)
1724 return [ $def => 1.0 ];
1729 $parts = explode(
',', $accept );
1731 foreach ( $parts as $part ) {
1732 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1733 $values = explode(
';', trim( $part ) );
1735 if ( count( $values ) == 1 ) {
1736 $prefs[$values[0]] = 1.0;
1737 } elseif ( preg_match(
'/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1738 $prefs[$values[0]] = floatval( $match[1] );
1758 if ( array_key_exists(
$type, $avail ) ) {
1761 $mainType = explode(
'/',
$type )[0];
1762 if ( array_key_exists(
"$mainType/*", $avail ) ) {
1763 return "$mainType/*";
1764 } elseif ( array_key_exists(
'*/*', $avail ) ) {
1790 foreach ( array_keys( $sprefs ) as
$type ) {
1791 $subType = explode(
'/',
$type )[1];
1792 if ( $subType !=
'*' ) {
1795 $combine[
$type] = $sprefs[
$type] * $cprefs[$ckey];
1800 foreach ( array_keys( $cprefs ) as
$type ) {
1801 $subType = explode(
'/',
$type )[1];
1802 if ( $subType !=
'*' && !array_key_exists(
$type, $sprefs ) ) {
1805 $combine[
$type] = $sprefs[$skey] * $cprefs[
$type];
1813 foreach ( array_keys( $combine ) as
$type ) {
1814 if ( $combine[
$type] > $bestq ) {
1816 $bestq = $combine[
$type];
1832 $ret = MWTimestamp::convert( $outputtype, $ts );
1833 if ( $ret ===
false ) {
1834 wfDebug(
"wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts" );
1848 if ( $ts ===
null ) {
1861 return MWTimestamp::now( TS_MW );
1870 return PHP_OS_FAMILY ===
'Windows';
1880 return PHP_SAPI ===
'cli' || PHP_SAPI ===
'phpdbg';
1917 throw new MWException( __FUNCTION__ .
" given storage path '$dir'." );
1920 if ( $caller !==
null ) {
1921 wfDebug(
"$caller: called wfMkdirParents($dir)" );
1924 if ( strval( $dir ) ===
'' || is_dir( $dir ) ) {
1928 $dir = str_replace( [
'\\',
'/' ], DIRECTORY_SEPARATOR, $dir );
1930 if ( $mode ===
null ) {
1935 AtEase::suppressWarnings();
1936 $ok = mkdir( $dir, $mode,
true );
1937 AtEase::restoreWarnings();
1941 if ( is_dir( $dir ) ) {
1946 wfLogWarning( sprintf(
"failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
1957 wfDebug( __FUNCTION__ .
"( $dir )" );
1959 if ( is_dir( $dir ) ) {
1960 $objects = scandir( $dir );
1961 foreach ( $objects as $object ) {
1962 if ( $object !=
"." && $object !=
".." ) {
1963 if ( filetype( $dir .
'/' . $object ) ==
"dir" ) {
1966 unlink( $dir .
'/' . $object );
1981 function wfPercent( $nr,
int $acc = 2,
bool $round =
true ) {
1982 $accForFormat = $acc >= 0 ? $acc : 0;
1983 $ret = sprintf(
"%.${accForFormat}f", $nr );
1984 return $round ? round( (
float)$ret, $acc ) .
'%' :
"$ret%";
2027 $val = strtolower( $val );
2032 || preg_match(
"/^\s*[+-]?0*[1-9]/", $val );
2048 return Shell::escape( ...
$args );
2076 $limits = [], $options = []
2078 if ( Shell::isDisabled() ) {
2081 return 'Unable to run external programs, proc_open() is disabled.';
2084 if ( is_array( $cmd ) ) {
2085 $cmd = Shell::escape( $cmd );
2088 $includeStderr = isset( $options[
'duplicateStderr'] ) && $options[
'duplicateStderr'];
2089 $profileMethod = $options[
'profileMethod'] ??
wfGetCaller();
2092 $result = Shell::command( [] )
2093 ->unsafeParams( (array)$cmd )
2094 ->environment( $environ )
2096 ->includeStderr( $includeStderr )
2097 ->profileMethod( $profileMethod )
2099 ->restrict( Shell::RESTRICT_NONE )
2106 $retval = $result->getExitCode();
2108 return $result->getStdout();
2129 return wfShellExec( $cmd, $retval, $environ, $limits,
2130 [
'duplicateStderr' =>
true,
'profileMethod' =>
wfGetCaller() ] );
2152 Hooks::runner()->onWfShellWikiCmd( $script, $parameters, $options );
2153 $cmd = [ $options[
'php'] ??
$wgPhpCli ];
2154 if ( isset( $options[
'wrapper'] ) ) {
2155 $cmd[] = $options[
'wrapper'];
2159 return Shell::escape( array_merge( $cmd, $parameters ) );
2173 function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult =
null ) {
2176 # This check may also protect against code injection in
2177 # case of broken installations.
2178 AtEase::suppressWarnings();
2180 AtEase::restoreWarnings();
2182 if ( !$haveDiff3 ) {
2187 # Make temporary files
2189 $oldtextFile = fopen( $oldtextName = tempnam( $td,
'merge-old-' ),
'w' );
2190 $mytextFile = fopen( $mytextName = tempnam( $td,
'merge-mine-' ),
'w' );
2191 $yourtextFile = fopen( $yourtextName = tempnam( $td,
'merge-your-' ),
'w' );
2193 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2194 # a newline character. To avoid this, we normalize the trailing whitespace before
2195 # creating the diff.
2197 fwrite( $oldtextFile, rtrim( $old ) .
"\n" );
2198 fclose( $oldtextFile );
2199 fwrite( $mytextFile, rtrim( $mine ) .
"\n" );
2200 fclose( $mytextFile );
2201 fwrite( $yourtextFile, rtrim( $yours ) .
"\n" );
2202 fclose( $yourtextFile );
2204 # Check for a conflict
2205 $cmd = Shell::escape(
$wgDiff3,
'-a',
'--overlap-only', $mytextName,
2206 $oldtextName, $yourtextName );
2207 $handle = popen( $cmd,
'r' );
2209 $mergeAttemptResult =
'';
2211 $data = fread( $handle, 8192 );
2212 if ( strlen( $data ) == 0 ) {
2215 $mergeAttemptResult .= $data;
2219 $conflict = $mergeAttemptResult !==
'';
2222 $cmd = Shell::escape(
$wgDiff3,
'-a',
'-e',
'--merge', $mytextName,
2223 $oldtextName, $yourtextName );
2224 $handle = popen( $cmd,
'r' );
2227 $data = fread( $handle, 8192 );
2228 if ( strlen( $data ) == 0 ) {
2234 unlink( $mytextName );
2235 unlink( $oldtextName );
2236 unlink( $yourtextName );
2238 if ( $result ===
'' && $old !==
'' && !$conflict ) {
2239 wfDebug(
"Unexpected null result from diff3. Command: $cmd" );
2256 function wfDiff( $before, $after, $params =
'-u' ) {
2258 if ( $before == $after ) {
2263 AtEase::suppressWarnings();
2265 AtEase::restoreWarnings();
2267 # This check may also protect against code injection in
2268 # case of broken installations.
2270 wfDebug(
"diff executable not found" );
2271 $diffs =
new Diff( explode(
"\n", $before ), explode(
"\n", $after ) );
2273 return $format->format( $diffs );
2276 # Make temporary files
2278 $oldtextFile = fopen( $oldtextName = tempnam( $td,
'merge-old-' ),
'w' );
2279 $newtextFile = fopen( $newtextName = tempnam( $td,
'merge-your-' ),
'w' );
2281 fwrite( $oldtextFile, $before );
2282 fclose( $oldtextFile );
2283 fwrite( $newtextFile, $after );
2284 fclose( $newtextFile );
2287 $cmd =
"$wgDiff " . $params .
' ' . Shell::escape( $oldtextName, $newtextName );
2289 $h = popen( $cmd,
'r' );
2291 unlink( $oldtextName );
2292 unlink( $newtextName );
2293 throw new Exception( __FUNCTION__ .
'(): popen() failed' );
2299 $data = fread( $h, 8192 );
2300 if ( strlen( $data ) == 0 ) {
2308 unlink( $oldtextName );
2309 unlink( $newtextName );
2312 $diff_lines = explode(
"\n", $diff );
2313 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0],
'---' ) === 0 ) {
2314 unset( $diff_lines[0] );
2316 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1],
'+++' ) === 0 ) {
2317 unset( $diff_lines[1] );
2320 $diff = implode(
"\n", $diff_lines );
2338 if ( $suffix ==
'' ) {
2341 $encSuffix =
'(?:' . preg_quote( $suffix,
'#' ) .
')?';
2345 if ( preg_match(
"#([^/\\\\]*?){$encSuffix}[/\\\\]*$#",
$path,
$matches ) ) {
2363 $path = str_replace(
'/', DIRECTORY_SEPARATOR,
$path );
2364 $from = str_replace(
'/', DIRECTORY_SEPARATOR, $from );
2368 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2370 $pieces = explode( DIRECTORY_SEPARATOR, dirname(
$path ) );
2371 $against = explode( DIRECTORY_SEPARATOR, $from );
2373 if ( $pieces[0] !== $against[0] ) {
2380 while ( count( $pieces ) && count( $against )
2381 && $pieces[0] == $against[0] ) {
2382 array_shift( $pieces );
2383 array_shift( $against );
2387 while ( count( $against ) ) {
2388 array_unshift( $pieces,
'..' );
2389 array_shift( $against );
2394 return implode( DIRECTORY_SEPARATOR, $pieces );
2408 $file =
"$IP/serialized/$name";
2409 if ( file_exists(
$file ) ) {
2429 return "$wgDBname-$wgDBprefix";
2466 function wfGetDB( $db, $groups = [], $wiki =
false ) {
2467 return wfGetLB( $wiki )->getMaintenanceConnectionRef( $db, $groups, $wiki );
2480 if ( $wiki ===
false ) {
2481 return MediaWikiServices::getInstance()->getDBLoadBalancer();
2483 $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2484 return $factory->getMainLB( $wiki );
2496 return MediaWikiServices::getInstance()->getRepoGroup()->findFile(
$title, $options );
2508 return MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile(
$title );
2535 if ( $script ===
'index' ) {
2537 } elseif ( $script ===
'load' ) {
2540 return "{$wgScriptPath}/{$script}.php";
2552 if ( isset( $_SERVER[
'SCRIPT_NAME'] ) ) {
2563 return $_SERVER[
'SCRIPT_NAME'];
2565 return $_SERVER[
'URL'];
2577 return $value ?
'true' :
'false';
2600 $name = preg_replace(
2619 if ( $oldLimit != -1 ) {
2621 if ( $newLimit == -1 ) {
2622 wfDebug(
"Removing PHP's memory limit" );
2623 Wikimedia\suppressWarnings();
2624 ini_set(
'memory_limit', $newLimit );
2625 Wikimedia\restoreWarnings();
2626 } elseif ( $newLimit > $oldLimit ) {
2627 wfDebug(
"Raising PHP's memory limit to $newLimit bytes" );
2628 Wikimedia\suppressWarnings();
2629 ini_set(
'memory_limit', $newLimit );
2630 Wikimedia\restoreWarnings();
2644 $timeLimit = (int)ini_get(
'max_execution_time' );
2650 ignore_user_abort(
true );
2663 $string = trim( $string );
2664 if ( $string ===
'' ) {
2667 $last = $string[strlen( $string ) - 1];
2668 $val = intval( $string );
2722 if ( $length !==
false ) {
2723 $realLen = strlen( $data );
2724 if ( $realLen < $length ) {
2725 throw new MWException(
"Tried to use wfUnpack on a "
2726 .
"string of length $realLen, but needed one "
2727 .
"of at least length $length."
2732 Wikimedia\suppressWarnings();
2733 $result = unpack( $format, $data );
2734 Wikimedia\restoreWarnings();
2736 if ( $result ===
false ) {
2738 throw new MWException(
"unpack could not unpack binary data" );
2753 return (
bool)$canDo;
2765 return in_array( $str, ExpiryDef::INFINITY_VALS );
2785 $multipliers = [ 1 ];
2789 $multipliers[] = 1.5;
2793 $handler =
$file->getHandler();
2794 if ( !$handler || !isset( $params[
'width'] ) ) {
2799 if ( isset( $params[
'page'] ) ) {
2800 $basicParams[
'page'] = $params[
'page'];
2806 foreach ( $multipliers as $multiplier ) {
2807 $thumbLimits = array_merge( $thumbLimits, array_map(
2808 function ( $width ) use ( $multiplier ) {
2809 return round( $width * $multiplier );
2812 $imageLimits = array_merge( $imageLimits, array_map(
2813 function ( $pair ) use ( $multiplier ) {
2815 round( $pair[0] * $multiplier ),
2816 round( $pair[1] * $multiplier ),
2823 if ( in_array( $params[
'width'], $thumbLimits ) ) {
2824 $normalParams = $basicParams + [
'width' => $params[
'width'] ];
2826 $handler->normaliseParams(
$file, $normalParams );
2830 foreach ( $imageLimits as $pair ) {
2831 $normalParams = $basicParams + [
'width' => $pair[0],
'height' => $pair[1] ];
2834 $handler->normaliseParams(
$file, $normalParams );
2836 if ( $normalParams[
'width'] == $params[
'width'] ) {
2847 foreach ( $params as $key => $value ) {
2848 if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
2870 foreach ( $baseArray as $name => &$groupVal ) {
2871 if ( isset( $newValues[$name] ) ) {
2872 $groupVal += $newValues[$name];
2876 $baseArray += $newValues;
2892 return getrusage( 0 );