23 if ( !defined(
'MEDIAWIKI' ) ) {
24 die(
"This file is part of MediaWiki, it is not a valid entry point" );
27 use Liuggio\StatsdClient\Sender\SocketSender;
31 use Wikimedia\ScopedCallback;
46 if ( !function_exists(
'hash_equals' ) ) {
72 function hash_equals( $known_string, $user_string ) {
74 if ( !is_string( $known_string ) ) {
75 trigger_error(
'hash_equals(): Expected known_string to be a string, ' .
76 gettype( $known_string ) .
' given', E_USER_WARNING );
81 if ( !is_string( $user_string ) ) {
82 trigger_error(
'hash_equals(): Expected user_string to be a string, ' .
83 gettype( $user_string ) .
' given', E_USER_WARNING );
88 $known_string_len = strlen( $known_string );
89 if ( $known_string_len !== strlen( $user_string ) ) {
94 for ( $i = 0; $i < $known_string_len; $i++ ) {
95 $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] );
116 $path =
"$wgExtensionDirectory/$ext/extension.json";
137 foreach ( $exts
as $ext ) {
138 $registry->queue(
"$wgExtensionDirectory/$ext/extension.json" );
153 $path =
"$wgStyleDirectory/$skin/skin.json";
169 $registry->queue(
"$wgStyleDirectory/$skin/skin.json" );
180 return array_udiff( $a, $b,
'wfArrayDiff2_cmp' );
189 if ( is_string( $a ) && is_string( $b ) ) {
190 return strcmp( $a, $b );
196 while ( (
list( , $valueA ) = each( $a ) ) && (
list( , $valueB ) = each( $b ) ) ) {
197 $cmp = strcmp( $valueA, $valueB );
216 if ( is_null( $changed ) ) {
217 throw new MWException(
'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
219 if ( $default[$key] !==
$value ) {
244 $args = func_get_args();
251 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
253 # @todo FIXME: Sometimes get nested arrays for $params,
254 # which leads to E_NOTICEs
255 $spec = implode(
"\t",
$params );
256 $out[$spec] = $originalParams;
259 return array_values(
$out );
272 $keys = array_keys( $array );
273 $offsetByKey = array_flip(
$keys );
275 $offset = $offsetByKey[$after];
278 $before = array_slice( $array, 0, $offset + 1,
true );
279 $after = array_slice( $array, $offset + 1,
count( $array ) - $offset,
true );
281 $output = $before + $insert + $after;
295 if ( is_object( $objOrArray ) ) {
296 $objOrArray = get_object_vars( $objOrArray );
298 foreach ( $objOrArray
as $key =>
$value ) {
299 if ( $recursive && ( is_object(
$value ) || is_array(
$value ) ) ) {
322 $max = mt_getrandmax() + 1;
323 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12,
'.',
'' );
339 for ( $n = 0; $n < $length; $n += 7 ) {
340 $str .= sprintf(
'%07x', mt_rand() & 0xfffffff );
342 return substr( $str, 0, $length );
375 if ( is_null(
$s ) ) {
380 if ( is_null( $needle ) ) {
381 $needle = [
'%3B',
'%40',
'%24',
'%21',
'%2A',
'%28',
'%29',
'%2C',
'%2F',
'%7E' ];
382 if ( !isset( $_SERVER[
'SERVER_SOFTWARE'] ) ||
383 ( strpos( $_SERVER[
'SERVER_SOFTWARE'],
'Microsoft-IIS/7' ) ===
false )
389 $s = urlencode(
$s );
392 [
';',
'@',
'$',
'!',
'*',
'(',
')',
',',
'/',
'~',
':' ],
409 function wfArrayToCgi( $array1, $array2 =
null, $prefix =
'' ) {
410 if ( !is_null( $array2 ) ) {
411 $array1 = $array1 + $array2;
415 foreach ( $array1
as $key =>
$value ) {
420 if ( $prefix !==
'' ) {
421 $key = $prefix .
"[$key]";
423 if ( is_array(
$value ) ) {
426 $cgi .= $firstTime ?
'' :
'&';
427 if ( is_array( $v ) ) {
430 $cgi .= urlencode( $key .
"[$k]" ) .
'=' . urlencode( $v );
435 if ( is_object(
$value ) ) {
438 $cgi .= urlencode( $key ) .
'=' . urlencode(
$value );
458 $bits = explode(
'&',
$query );
460 foreach ( $bits
as $bit ) {
464 if ( strpos( $bit,
'=' ) ===
false ) {
471 $key = urldecode( $key );
473 if ( strpos( $key,
'[' ) !==
false ) {
474 $keys = array_reverse( explode(
'[', $key ) );
475 $key = array_pop(
$keys );
478 $k = substr( $k, 0, -1 );
479 $temp = [ $k => $temp ];
481 if ( isset(
$ret[$key] ) ) {
482 $ret[$key] = array_merge(
$ret[$key], $temp );
502 if ( is_array(
$query ) ) {
508 $hashPos = strpos( $url,
'#' );
509 if ( $hashPos !==
false ) {
510 $fragment = substr( $url, $hashPos );
511 $url = substr( $url, 0, $hashPos );
515 if (
false === strpos( $url,
'?' ) ) {
523 if ( $fragment !==
false ) {
564 $defaultProto =
$wgRequest->getProtocol() .
'://';
570 $serverHasProto = $bits && $bits[
'scheme'] !=
'';
573 if ( $serverHasProto ) {
574 $defaultProto = $bits[
'scheme'] .
'://';
583 $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
585 if ( substr( $url, 0, 2 ) ==
'//' ) {
586 $url = $defaultProtoWithoutSlashes . $url;
587 } elseif ( substr( $url, 0, 1 ) ==
'/' ) {
590 $url = ( $serverHasProto ?
'' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
597 if ( $defaultProto ===
PROTO_HTTPS && $wgHttpsPort != 443 ) {
598 $bits[
'port'] = $wgHttpsPort;
601 if ( $bits && isset( $bits[
'path'] ) ) {
607 } elseif ( substr( $url, 0, 1 ) !=
'/' ) {
608 # URL is a relative path
612 # Expanded URL is not valid.
632 if ( isset( $urlParts[
'delimiter'] ) ) {
633 if ( isset( $urlParts[
'scheme'] ) ) {
634 $result .= $urlParts[
'scheme'];
637 $result .= $urlParts[
'delimiter'];
640 if ( isset( $urlParts[
'host'] ) ) {
641 if ( isset( $urlParts[
'user'] ) ) {
643 if ( isset( $urlParts[
'pass'] ) ) {
644 $result .=
':' . $urlParts[
'pass'];
651 if ( isset( $urlParts[
'port'] ) ) {
652 $result .=
':' . $urlParts[
'port'];
656 if ( isset( $urlParts[
'path'] ) ) {
660 if ( isset( $urlParts[
'query'] ) ) {
661 $result .=
'?' . $urlParts[
'query'];
664 if ( isset( $urlParts[
'fragment'] ) ) {
665 $result .=
'#' . $urlParts[
'fragment'];
684 $inputLength = strlen( $urlPath );
686 while ( $inputOffset < $inputLength ) {
687 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
688 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
689 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
690 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
693 if ( $prefixLengthTwo ==
'./' ) {
694 # Step A, remove leading "./"
696 } elseif ( $prefixLengthThree ==
'../' ) {
697 # Step A, remove leading "../"
699 } elseif ( ( $prefixLengthTwo ==
'/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
700 # Step B, replace leading "/.$" with "/"
702 $urlPath[$inputOffset] =
'/';
703 } elseif ( $prefixLengthThree ==
'/./' ) {
704 # Step B, replace leading "/./" with "/"
706 } elseif ( $prefixLengthThree ==
'/..' && ( $inputOffset + 3 == $inputLength ) ) {
707 # Step C, replace leading "/..$" with "/" and
708 # remove last path component in output
710 $urlPath[$inputOffset] =
'/';
712 } elseif ( $prefixLengthFour ==
'/../' ) {
713 # Step C, replace leading "/../" with "/" and
714 # remove last path component in output
717 } elseif ( ( $prefixLengthOne ==
'.' ) && ( $inputOffset + 1 == $inputLength ) ) {
718 # Step D, remove "^.$"
720 } elseif ( ( $prefixLengthTwo ==
'..' ) && ( $inputOffset + 2 == $inputLength ) ) {
721 # Step D, remove "^..$"
724 # Step E, move leading path segment to output
725 if ( $prefixLengthOne ==
'/' ) {
726 $slashPos = strpos( $urlPath,
'/', $inputOffset + 1 );
728 $slashPos = strpos( $urlPath,
'/', $inputOffset );
730 if ( $slashPos ===
false ) {
731 $output .= substr( $urlPath, $inputOffset );
732 $inputOffset = $inputLength;
734 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
735 $inputOffset += $slashPos - $inputOffset;
740 $slashPos = strrpos(
$output,
'/' );
741 if ( $slashPos ===
false ) {
763 static $withProtRel =
null, $withoutProtRel =
null;
764 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
765 if ( !is_null( $cachedValue ) ) {
771 if ( is_array( $wgUrlProtocols ) ) {
773 foreach ( $wgUrlProtocols
as $protocol ) {
775 if ( $includeProtocolRelative || $protocol !==
'//' ) {
776 $protocols[] = preg_quote( $protocol,
'/' );
780 $retval = implode(
'|', $protocols );
790 if ( $includeProtocolRelative ) {
825 $wasRelative = substr( $url, 0, 2 ) ==
'//';
826 if ( $wasRelative ) {
829 MediaWiki\suppressWarnings();
830 $bits = parse_url( $url );
831 MediaWiki\restoreWarnings();
834 if ( !$bits || !isset( $bits[
'scheme'] ) ) {
839 $bits[
'scheme'] = strtolower( $bits[
'scheme'] );
842 if ( in_array( $bits[
'scheme'] .
'://', $wgUrlProtocols ) ) {
843 $bits[
'delimiter'] =
'://';
844 } elseif ( in_array( $bits[
'scheme'] .
':', $wgUrlProtocols ) ) {
845 $bits[
'delimiter'] =
':';
848 if ( isset( $bits[
'path'] ) ) {
849 $bits[
'host'] = $bits[
'path'];
857 if ( !isset( $bits[
'host'] ) ) {
861 if ( isset( $bits[
'path'] ) ) {
863 if ( substr( $bits[
'path'], 0, 1 ) !==
'/' ) {
864 $bits[
'path'] =
'/' . $bits[
'path'];
872 if ( $wasRelative ) {
873 $bits[
'scheme'] =
'';
874 $bits[
'delimiter'] =
'//';
890 return preg_replace_callback(
891 '/((?:%[89A-F][0-9A-F])+)/i',
892 'wfExpandIRI_callback',
917 if ( $bits[
'scheme'] ==
'mailto' ) {
918 $mailparts = explode(
'@', $bits[
'host'], 2 );
919 if (
count( $mailparts ) === 2 ) {
920 $domainpart = strtolower( implode(
'.', array_reverse( explode(
'.', $mailparts[1] ) ) ) );
925 $reversedHost = $domainpart .
'@' . $mailparts[0];
927 $reversedHost = strtolower( implode(
'.', array_reverse( explode(
'.', $bits[
'host'] ) ) ) );
931 if ( substr( $reversedHost, -1, 1 ) !==
'.' ) {
932 $reversedHost .=
'.';
935 $prot = $bits[
'scheme'];
936 $index = $prot . $bits[
'delimiter'] . $reversedHost;
938 if ( isset( $bits[
'port'] ) ) {
939 $index .=
':' . $bits[
'port'];
941 if ( isset( $bits[
'path'] ) ) {
942 $index .= $bits[
'path'];
946 if ( isset( $bits[
'query'] ) ) {
947 $index .=
'?' . $bits[
'query'];
949 if ( isset( $bits[
'fragment'] ) ) {
950 $index .=
'#' . $bits[
'fragment'];
954 return [
"http:$index",
"https:$index" ];
968 if ( is_array( $bits ) && isset( $bits[
'host'] ) ) {
969 $host =
'.' . $bits[
'host'];
970 foreach ( (
array)$domains
as $domain ) {
971 $domain =
'.' . $domain;
972 if ( substr( $host, -strlen( $domain ) ) === $domain ) {
1001 global $wgDebugRawPage, $wgDebugLogPrefix;
1008 $text = trim( $text );
1010 if ( $wgDebugTimestamps ) {
1011 $context[
'seconds_elapsed'] = sprintf(
1017 ( memory_get_usage(
true ) / ( 1024 * 1024 ) )
1021 if ( $wgDebugLogPrefix !==
'' ) {
1022 $context[
'prefix'] = $wgDebugLogPrefix;
1024 $context[
'private'] = ( $dest ===
false || $dest ===
'private' );
1026 $logger = LoggerFactory::getInstance(
'wfDebug' );
1039 # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
1040 if ( ( isset( $_GET[
'action'] ) && $_GET[
'action'] ==
'raw' )
1042 isset( $_SERVER[
'SCRIPT_NAME'] )
1043 && substr( $_SERVER[
'SCRIPT_NAME'], -8 ) ==
'load.php'
1059 $mem = memory_get_usage();
1061 $mem = floor( $mem / 1024 ) .
' KiB';
1065 wfDebug(
"Memory usage: $mem\n" );
1096 $text = trim( $text );
1098 $logger = LoggerFactory::getInstance( $logGroup );
1099 $context[
'private'] = ( $dest ===
false || $dest ===
'private' );
1112 $logger = LoggerFactory::getInstance(
'wfLogDBError' );
1113 $logger->error( trim( $text ),
$context );
1129 function wfDeprecated( $function, $version =
false, $component =
false, $callerOffset = 2 ) {
1143 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1156 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1175 $logger = LoggerFactory::getInstance(
'wfErrorLog' );
1177 $logger->info( trim( $text ),
$context );
1184 global $wgDebugLogGroups, $wgDebugRawPage;
1191 $profiler->logData();
1194 if ( $config->get(
'StatsdServer' ) ) {
1196 $statsdServer = explode(
':', $config->get(
'StatsdServer' ) );
1197 $statsdHost = $statsdServer[0];
1198 $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
1199 $statsdSender =
new SocketSender( $statsdHost, $statsdPort );
1201 $statsdClient->setSamplingRates( $config->get(
'StatsdSamplingRates' ) );
1202 $statsdClient->send(
1203 MediaWikiServices::getInstance()->getStatsdDataFactory()->getBuffer()
1205 }
catch ( Exception $ex ) {
1210 # Profiling must actually be enabled...
1215 if ( isset( $wgDebugLogGroups[
'profileoutput'] )
1216 && $wgDebugLogGroups[
'profileoutput'] ===
false
1225 $ctx = [
'elapsed' =>
$request->getElapsedTime() ];
1226 if ( !empty( $_SERVER[
'HTTP_X_FORWARDED_FOR'] ) ) {
1227 $ctx[
'forwarded_for'] = $_SERVER[
'HTTP_X_FORWARDED_FOR'];
1229 if ( !empty( $_SERVER[
'HTTP_CLIENT_IP'] ) ) {
1230 $ctx[
'client_ip'] = $_SERVER[
'HTTP_CLIENT_IP'];
1232 if ( !empty( $_SERVER[
'HTTP_FROM'] ) ) {
1233 $ctx[
'from'] = $_SERVER[
'HTTP_FROM'];
1235 if ( isset( $ctx[
'forwarded_for'] ) ||
1236 isset( $ctx[
'client_ip'] ) ||
1237 isset( $ctx[
'from'] ) ) {
1238 $ctx[
'proxy'] = $_SERVER[
'REMOTE_ADDR'];
1245 $ctx[
'anon'] =
$user->isItemLoaded(
'id' ) &&
$user->isAnon();
1250 $ctx[
'url'] = urldecode(
$request->getRequestURL() );
1251 }
catch ( Exception $ignored ) {
1255 $ctx[
'output'] = $profiler->getOutput();
1257 $log = LoggerFactory::getInstance(
'profileoutput' );
1258 $log->info(
"Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1269 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1270 $stats->updateCount( $key, $count );
1279 return \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
1292 return \MediaWiki\MediaWikiServices::getInstance()->getReadOnlyMode()
1303 return \MediaWiki\MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1323 # Identify which language to get or create a language object for.
1324 # Using is_object here due to Stub objects.
1325 if ( is_object( $langcode ) ) {
1326 # Great, we already have the object (hopefully)!
1332 # $langcode is the language code of the wikis content language object.
1333 # or it is a boolean and value is true
1338 if ( $langcode ===
false || $langcode ===
$wgLang->getCode() ) {
1339 # $langcode is the language code of user language object.
1340 # or it was a boolean and value is false
1345 if ( in_array( $langcode, $validCodes ) ) {
1346 # $langcode corresponds to a valid language.
1350 # $langcode is a string, but not a valid language code; use content language.
1351 wfDebug(
"Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1372 $message =
new Message( $key );
1378 call_user_func_array( [ $message,
'params' ],
$params );
1397 $args = func_get_args();
1398 return call_user_func_array(
'Message::newFallbackSequence',
$args );
1410 # Fix windows line-endings
1411 # Some messages are split with explode("\n", $msg)
1412 $message = str_replace(
"\r",
'', $message );
1416 if ( is_array(
$args[0] ) ) {
1419 $replacementKeys = [];
1420 foreach (
$args as $n => $param ) {
1421 $replacementKeys[
'$' . ( $n + 1 )] = $param;
1423 $message = strtr( $message, $replacementKeys );
1438 if ( is_null( $host ) ) {
1440 # Hostname overriding
1441 global $wgOverrideHostname;
1442 if ( $wgOverrideHostname !==
false ) {
1443 # Set static and skip any detection
1444 $host = $wgOverrideHostname;
1448 if ( function_exists(
'posix_uname' ) ) {
1450 $uname = posix_uname();
1454 if ( is_array( $uname ) && isset( $uname[
'nodename'] ) ) {
1455 $host = $uname[
'nodename'];
1456 } elseif ( getenv(
'COMPUTERNAME' ) ) {
1457 # Windows computer name
1458 $host = getenv(
'COMPUTERNAME' );
1460 # This may be a virtual server.
1461 $host = $_SERVER[
'SERVER_NAME'];
1479 $responseTime = round( ( microtime(
true ) -
$wgRequestTime ) * 1000 );
1480 $reportVars = [
'wgBackendResponseTime' => $responseTime ];
1481 if ( $wgShowHostnames ) {
1498 static $disabled =
null;
1500 if ( is_null( $disabled ) ) {
1501 $disabled = !function_exists(
'debug_backtrace' );
1503 wfDebug(
"debug_backtrace() is disabled\n" );
1511 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT,
$limit + 1 ), 1 );
1513 return array_slice( debug_backtrace(), 1 );
1528 if ( $raw ===
null ) {
1533 $frameFormat =
"%s line %s calls %s()\n";
1534 $traceFormat =
"%s";
1536 $frameFormat =
"<li>%s line %s calls %s()</li>\n";
1537 $traceFormat =
"<ul>\n%s</ul>\n";
1540 $frames = array_map(
function ( $frame )
use ( $frameFormat ) {
1541 $file = !empty( $frame[
'file'] ) ? basename( $frame[
'file'] ) :
'-';
1542 $line = isset( $frame[
'line'] ) ? $frame[
'line'] :
'-';
1543 $call = $frame[
'function'];
1544 if ( !empty( $frame[
'class'] ) ) {
1545 $call = $frame[
'class'] . $frame[
'type'] . $call;
1547 return sprintf( $frameFormat, $file,
$line, $call );
1550 return sprintf( $traceFormat, implode(
'', $frames ) );
1564 if ( isset( $backtrace[$level] ) ) {
1584 return implode(
'/', array_map(
'wfFormatStackFrame', $trace ) );
1594 if ( !isset( $frame[
'function'] ) ) {
1595 return 'NO_FUNCTION_GIVEN';
1597 return isset( $frame[
'class'] ) && isset( $frame[
'type'] ) ?
1598 $frame[
'class'] . $frame[
'type'] . $frame[
'function'] :
1612 return wfMessage(
'showingresults' )->numParams(
$limit, $offset + 1 )->parse();
1626 if (
$result ===
null || $force ) {
1628 if ( isset( $_SERVER[
'HTTP_ACCEPT_ENCODING'] ) ) {
1629 # @todo FIXME: We may want to blacklist some broken browsers
1632 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1633 $_SERVER[
'HTTP_ACCEPT_ENCODING'],
1637 if ( isset( $m[2] ) && ( $m[1] ==
'q' ) && ( $m[2] == 0 ) ) {
1641 wfDebug(
"wfClientAcceptsGzip: client accepts gzip.\n" );
1659 global $wgEnableMagicLinks;
1660 static $repl =
null, $repl2 =
null;
1661 if ( $repl ===
null || defined(
'MW_PARSER_TEST' ) || defined(
'MW_PHPUNIT_TEST' ) ) {
1665 '"' =>
'"',
'&' =>
'&',
"'" =>
''',
'<' =>
'<',
1666 '=' =>
'=',
'>' =>
'>',
'[' =>
'[',
']' =>
']',
1667 '{' =>
'{',
'|' =>
'|',
'}' =>
'}',
';' =>
';',
1668 "\n#" =>
"\n#",
"\r#" =>
"\r#",
1669 "\n*" =>
"\n*",
"\r*" =>
"\r*",
1670 "\n:" =>
"\n:",
"\r:" =>
"\r:",
1671 "\n " =>
"\n ",
"\r " =>
"\r ",
1672 "\n\n" =>
"\n ",
"\r\n" =>
" \n",
1673 "\n\r" =>
"\n ",
"\r\r" =>
"\r ",
1674 "\n\t" =>
"\n	",
"\r\t" =>
"\r	",
1675 "\n----" =>
"\n----",
"\r----" =>
"\r----",
1676 '__' =>
'__',
'://' =>
'://',
1679 $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
1681 foreach ( $magicLinks
as $magic ) {
1682 $repl[
"$magic "] =
"$magic ";
1683 $repl[
"$magic\t"] =
"$magic	";
1684 $repl[
"$magic\r"] =
"$magic ";
1685 $repl[
"$magic\n"] =
"$magic ";
1686 $repl[
"$magic\f"] =
"$magic";
1692 foreach ( $wgUrlProtocols
as $prot ) {
1693 if ( substr( $prot, -1 ) ===
':' ) {
1694 $repl2[] = preg_quote( substr( $prot, 0, -1 ),
'/' );
1697 $repl2 = $repl2 ?
'/\b(' . implode(
'|', $repl2 ) .
'):/i' :
'/^(?!)/';
1699 $text = substr( strtr(
"\n$text", $repl ), 1 );
1700 $text = preg_replace( $repl2,
'$1:', $text );
1716 if ( !is_null(
$source ) || $force ) {
1731 function wfSetBit( &$dest, $bit, $state =
true ) {
1732 $temp = (bool)( $dest & $bit );
1733 if ( !is_null( $state ) ) {
1751 $s = str_replace(
"\n",
"<br />\n", var_export( $var,
true ) .
"\n" );
1752 if ( headers_sent() || !isset(
$wgOut ) || !is_object(
$wgOut ) ) {
1771 $wgOut->sendCacheControl();
1775 header(
'Content-type: text/html; charset=utf-8' );
1776 print
'<!DOCTYPE html>' .
1777 '<html><head><title>' .
1778 htmlspecialchars( $label ) .
1779 '</title></head><body><h1>' .
1780 htmlspecialchars( $label ) .
1782 nl2br( htmlspecialchars( $desc ) ) .
1783 "</p></body></html>\n";
1804 if ( $resetGzipEncoding ) {
1807 global $wgDisableOutputCompression;
1808 $wgDisableOutputCompression =
true;
1810 while (
$status = ob_get_status() ) {
1811 if ( isset(
$status[
'flags'] ) ) {
1812 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1814 } elseif ( isset(
$status[
'del'] ) ) {
1818 $deleteable =
$status[
'type'] !== 0;
1820 if ( !$deleteable ) {
1825 if (
$status[
'name'] ===
'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1829 if ( !ob_end_clean() ) {
1834 if ( $resetGzipEncoding ) {
1835 if (
$status[
'name'] ==
'ob_gzhandler' ) {
1838 header_remove(
'Content-Encoding' );
1870 # No arg means accept anything (per HTTP spec)
1872 return [ $def => 1.0 ];
1877 $parts = explode(
',', $accept );
1879 foreach ( $parts
as $part ) {
1880 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1881 $values = explode(
';', trim( $part ) );
1883 if (
count( $values ) == 1 ) {
1884 $prefs[$values[0]] = 1.0;
1885 } elseif ( preg_match(
'/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1886 $prefs[$values[0]] = floatval( $match[1] );
1906 if ( array_key_exists(
$type, $avail ) ) {
1909 $mainType = explode(
'/',
$type )[0];
1910 if ( array_key_exists(
"$mainType/*", $avail ) ) {
1911 return "$mainType/*";
1912 } elseif ( array_key_exists(
'*/*', $avail ) ) {
1936 foreach ( array_keys( $sprefs )
as $type ) {
1937 $subType = explode(
'/',
$type )[1];
1938 if ( $subType !=
'*' ) {
1941 $combine[
$type] = $sprefs[
$type] * $cprefs[$ckey];
1946 foreach ( array_keys( $cprefs )
as $type ) {
1947 $subType = explode(
'/',
$type )[1];
1948 if ( $subType !=
'*' && !array_key_exists(
$type, $sprefs ) ) {
1951 $combine[
$type] = $sprefs[$skey] * $cprefs[
$type];
1959 foreach ( array_keys( $combine )
as $type ) {
1960 if ( $combine[
$type] > $bestq ) {
1962 $bestq = $combine[
$type];
1976 MediaWiki\suppressWarnings( $end );
1984 MediaWiki\suppressWarnings(
true );
1995 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
1996 $ret = MWTimestamp::convert( $outputtype, $ts );
1997 if (
$ret ===
false ) {
1998 wfDebug(
"wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
2012 if ( is_null( $ts ) ) {
2026 return MWTimestamp::now( TS_MW );
2035 static $isWindows =
null;
2036 if ( $isWindows ===
null ) {
2037 $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) ===
'WIN';
2048 return defined(
'HHVM_VERSION' );
2085 throw new MWException( __FUNCTION__ .
" given storage path '$dir'." );
2088 if ( !is_null( $caller ) ) {
2089 wfDebug(
"$caller: called wfMkdirParents($dir)\n" );
2092 if ( strval(
$dir ) ===
'' || is_dir(
$dir ) ) {
2096 $dir = str_replace( [
'\\',
'/' ], DIRECTORY_SEPARATOR,
$dir );
2098 if ( is_null( $mode ) ) {
2103 MediaWiki\suppressWarnings();
2104 $ok = mkdir(
$dir, $mode,
true );
2105 MediaWiki\restoreWarnings();
2109 if ( is_dir(
$dir ) ) {
2125 wfDebug( __FUNCTION__ .
"( $dir )\n" );
2127 if ( is_dir(
$dir ) ) {
2128 $objects = scandir(
$dir );
2129 foreach ( $objects
as $object ) {
2130 if ( $object !=
"." && $object !=
".." ) {
2131 if ( filetype(
$dir .
'/' . $object ) ==
"dir" ) {
2134 unlink(
$dir .
'/' . $object );
2149 function wfPercent( $nr, $acc = 2, $round =
true ) {
2150 $ret = sprintf(
"%.${acc}f", $nr );
2151 return $round ? round(
$ret, $acc ) .
'%' :
"$ret%";
2178 $val = strtolower( ini_get( $setting ) );
2183 || preg_match(
"/^\s*[+-]?0*[1-9]/", $val );
2199 $args = func_get_args();
2225 $tokens = preg_split(
'/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
2229 if ( $iteration % 2 == 1 ) {
2231 $arg .= str_replace(
'\\',
'\\\\', substr( $token, 0, -1 ) ) .
'\\"';
2232 } elseif ( $iteration % 4 == 2 ) {
2234 $arg .= str_replace(
'^',
'^^', $token );
2244 if ( preg_match(
'/^(.*?)(\\\\+)$/', $arg, $m ) ) {
2245 $arg = $m[1] . str_replace(
'\\',
'\\\\', $m[2] );
2249 $retVal .=
'"' . $arg .
'"';
2251 $retVal .= escapeshellarg( $arg );
2264 static $disabled =
null;
2265 if ( is_null( $disabled ) ) {
2266 if ( !function_exists(
'proc_open' ) ) {
2267 wfDebug(
"proc_open() is disabled\n" );
2268 $disabled =
'disabled';
2301 global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
2302 $wgMaxShellWallClockTime, $wgShellCgroup;
2307 return 'Unable to run external programs, proc_open() is disabled.';
2310 $includeStderr = isset(
$options[
'duplicateStderr'] ) &&
$options[
'duplicateStderr'];
2316 foreach ( $environ
as $k => $v ) {
2324 $envcmd .=
"set $k=" . preg_replace(
'/([&|()<>^"])/',
'^\\1', $v ) .
'&& ';
2329 $envcmd .=
"$k=" . escapeshellarg( $v ) .
' ';
2332 if ( is_array( $cmd ) ) {
2336 $cmd = $envcmd . $cmd;
2338 $useLogPipe =
false;
2339 if ( is_executable(
'/bin/bash' ) ) {
2340 $time = intval( isset( $limits[
'time'] ) ? $limits[
'time'] : $wgMaxShellTime );
2341 if ( isset( $limits[
'walltime'] ) ) {
2342 $wallTime = intval( $limits[
'walltime'] );
2343 } elseif ( isset( $limits[
'time'] ) ) {
2346 $wallTime = intval( $wgMaxShellWallClockTime );
2348 $mem = intval( isset( $limits[
'memory'] ) ? $limits[
'memory'] : $wgMaxShellMemory );
2349 $filesize = intval( isset( $limits[
'filesize'] ) ? $limits[
'filesize'] : $wgMaxShellFileSize );
2351 if (
$time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
2352 $cmd =
'/bin/bash ' . escapeshellarg(
"$IP/includes/limit.sh" ) .
' ' .
2353 escapeshellarg( $cmd ) .
' ' .
2355 "MW_INCLUDE_STDERR=" . ( $includeStderr ?
'1' :
'' ) .
';' .
2356 "MW_CPU_LIMIT=$time; " .
2357 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) .
'; ' .
2358 "MW_MEM_LIMIT=$mem; " .
2359 "MW_FILE_SIZE_LIMIT=$filesize; " .
2360 "MW_WALL_CLOCK_LIMIT=$wallTime; " .
2361 "MW_USE_LOG_PIPE=yes"
2364 } elseif ( $includeStderr ) {
2367 } elseif ( $includeStderr ) {
2370 wfDebug(
"wfShellExec: $cmd\n" );
2377 throw new Exception( __METHOD__ .
2378 '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
2382 0 => [
'file',
'php://stdin',
'r' ],
2383 1 => [
'pipe',
'w' ],
2384 2 => [
'file',
'php://stderr',
'w' ] ];
2385 if ( $useLogPipe ) {
2386 $desc[3] = [
'pipe',
'w' ];
2389 $scoped =
Profiler::instance()->scopedProfileIn( __FUNCTION__ .
'-' . $profileMethod );
2390 $proc = proc_open( $cmd, $desc, $pipes );
2392 wfDebugLog(
'exec',
"proc_open() failed: $cmd" );
2396 $outBuffer = $logBuffer =
'';
2412 $eintr = defined(
'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
2413 $eintrMessage =
"stream_select(): unable to select [$eintr]";
2419 while ( $running ===
true || $numReadyPipes !== 0 ) {
2421 $status = proc_get_status( $proc );
2430 $readyPipes = $pipes;
2434 @trigger_error(
'' );
2435 $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout );
2436 if ( $numReadyPipes ===
false ) {
2438 $error = error_get_last();
2439 if ( strncmp( $error[
'message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
2442 trigger_error( $error[
'message'], E_USER_WARNING );
2443 $logMsg = $error[
'message'];
2447 foreach ( $readyPipes
as $fd => $pipe ) {
2448 $block = fread( $pipe, 65536 );
2449 if ( $block ===
'' ) {
2451 fclose( $pipes[$fd] );
2452 unset( $pipes[$fd] );
2456 } elseif ( $block ===
false ) {
2458 $logMsg =
"Error reading from pipe";
2460 } elseif ( $fd == 1 ) {
2462 $outBuffer .= $block;
2463 } elseif ( $fd == 3 ) {
2465 $logBuffer .= $block;
2466 if ( strpos( $block,
"\n" ) !==
false ) {
2467 $lines = explode(
"\n", $logBuffer );
2468 $logBuffer = array_pop(
$lines );
2477 foreach ( $pipes
as $pipe ) {
2484 $status = proc_get_status( $proc );
2487 if ( $logMsg !==
false ) {
2490 proc_close( $proc );
2491 } elseif (
$status[
'signaled'] ) {
2492 $logMsg =
"Exited with signal {$status['termsig']}";
2494 proc_close( $proc );
2497 $retval = proc_close( $proc );
2500 proc_close( $proc );
2503 $logMsg =
"Possibly missing executable file";
2505 $logMsg =
"Probably exited with signal " . (
$retval - 128 );
2509 if ( $logMsg !==
false ) {
2534 [
'duplicateStderr' =>
true,
'profileMethod' =>
wfGetCaller() ] );
2542 static $done =
false;
2548 putenv(
"LC_CTYPE=$wgShellLocale" );
2549 setlocale( LC_CTYPE, $wgShellLocale );
2570 if ( isset(
$options[
'wrapper'] ) ) {
2591 # This check may also protect against code injection in
2592 # case of broken installations.
2593 MediaWiki\suppressWarnings();
2594 $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2595 MediaWiki\restoreWarnings();
2597 if ( !$haveDiff3 ) {
2598 wfDebug(
"diff3 not found\n" );
2602 # Make temporary files
2604 $oldtextFile = fopen( $oldtextName = tempnam( $td,
'merge-old-' ),
'w' );
2605 $mytextFile = fopen( $mytextName = tempnam( $td,
'merge-mine-' ),
'w' );
2606 $yourtextFile = fopen( $yourtextName = tempnam( $td,
'merge-your-' ),
'w' );
2608 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2609 # a newline character. To avoid this, we normalize the trailing whitespace before
2610 # creating the diff.
2612 fwrite( $oldtextFile, rtrim( $old ) .
"\n" );
2613 fclose( $oldtextFile );
2614 fwrite( $mytextFile, rtrim( $mine ) .
"\n" );
2615 fclose( $mytextFile );
2616 fwrite( $yourtextFile, rtrim( $yours ) .
"\n" );
2617 fclose( $yourtextFile );
2619 # Check for a conflict
2621 $oldtextName, $yourtextName );
2622 $handle = popen( $cmd,
'r' );
2624 if ( fgets( $handle, 1024 ) ) {
2633 $oldtextName, $yourtextName );
2634 $handle = popen( $cmd,
'r' );
2637 $data = fread( $handle, 8192 );
2638 if ( strlen( $data ) == 0 ) {
2644 unlink( $mytextName );
2645 unlink( $oldtextName );
2646 unlink( $yourtextName );
2648 if (
$result ===
'' && $old !==
'' && !$conflict ) {
2649 wfDebug(
"Unexpected null result from diff3. Command: $cmd\n" );
2667 if ( $before == $after ) {
2672 MediaWiki\suppressWarnings();
2673 $haveDiff = $wgDiff && file_exists( $wgDiff );
2674 MediaWiki\restoreWarnings();
2676 # This check may also protect against code injection in
2677 # case of broken installations.
2679 wfDebug(
"diff executable not found\n" );
2680 $diffs =
new Diff( explode(
"\n", $before ), explode(
"\n", $after ) );
2682 return $format->format( $diffs );
2685 # Make temporary files
2687 $oldtextFile = fopen( $oldtextName = tempnam( $td,
'merge-old-' ),
'w' );
2688 $newtextFile = fopen( $newtextName = tempnam( $td,
'merge-your-' ),
'w' );
2690 fwrite( $oldtextFile, $before );
2691 fclose( $oldtextFile );
2692 fwrite( $newtextFile, $after );
2693 fclose( $newtextFile );
2698 $h = popen( $cmd,
'r' );
2700 unlink( $oldtextName );
2701 unlink( $newtextName );
2702 throw new Exception( __METHOD__ .
'(): popen() failed' );
2708 $data = fread( $h, 8192 );
2709 if ( strlen( $data ) == 0 ) {
2717 unlink( $oldtextName );
2718 unlink( $newtextName );
2721 $diff_lines = explode(
"\n", $diff );
2722 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0],
'---' ) === 0 ) {
2723 unset( $diff_lines[0] );
2725 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1],
'+++' ) === 0 ) {
2726 unset( $diff_lines[1] );
2729 $diff = implode(
"\n", $diff_lines );
2750 $php_ver = PHP_VERSION;
2752 if ( version_compare( $php_ver, (
string)$req_ver,
'<' ) ) {
2753 throw new MWException(
"PHP $req_ver required--this is only $php_ver" );
2779 function wfUseMW( $req_ver ) {
2782 if ( version_compare(
$wgVersion, (
string)$req_ver,
'<' ) ) {
2783 throw new MWException(
"MediaWiki $req_ver required--this is only $wgVersion" );
2800 if ( $suffix ==
'' ) {
2803 $encSuffix =
'(?:' . preg_quote( $suffix,
'#' ) .
')?';
2807 if ( preg_match(
"#([^/\\\\]*?){$encSuffix}[/\\\\]*$#",
$path,
$matches ) ) {
2825 $path = str_replace(
'/', DIRECTORY_SEPARATOR,
$path );
2826 $from = str_replace(
'/', DIRECTORY_SEPARATOR, $from );
2830 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2832 $pieces = explode( DIRECTORY_SEPARATOR, dirname(
$path ) );
2833 $against = explode( DIRECTORY_SEPARATOR, $from );
2835 if ( $pieces[0] !== $against[0] ) {
2842 while (
count( $pieces ) &&
count( $against )
2843 && $pieces[0] == $against[0] ) {
2844 array_shift( $pieces );
2845 array_shift( $against );
2849 while (
count( $against ) ) {
2850 array_unshift( $pieces,
'..' );
2851 array_shift( $against );
2856 return implode( DIRECTORY_SEPARATOR, $pieces );
2877 $lowercase =
true,
$engine =
'auto'
2879 return Wikimedia\base_convert(
$input, $sourceBase, $destBase, $pad, $lowercase,
$engine );
2898 $session = SessionManager::getGlobalSession();
2899 $delay = $session->delaySave();
2901 $session->resetId();
2905 if ( session_id() !== $session->getId() ) {
2909 ScopedCallback::consume( $delay );
2925 session_id( $sessionId );
2928 $session = SessionManager::getGlobalSession();
2929 $session->persist();
2931 if ( session_id() !== $session->getId() ) {
2932 session_id( $session->getId() );
2934 MediaWiki\quietCall(
'session_start' );
2946 $file =
"$IP/serialized/$name";
2947 if ( file_exists( $file ) ) {
2948 $blob = file_get_contents( $file );
2963 return call_user_func_array(
2980 $args = array_slice( func_get_args(), 2 );
2981 $keyspace = $prefix ?
"$db-$prefix" : $db;
2982 return call_user_func_array(
2984 [ $keyspace,
$args ]
3000 return call_user_func_array(
3015 return "$wgDBname-$wgDBprefix";
3029 $bits = explode(
'-', $wiki, 2 );
3030 if (
count( $bits ) < 2 ) {
3061 function wfGetDB( $db, $groups = [], $wiki =
false ) {
3062 return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
3074 function wfGetLB( $wiki =
false ) {
3075 if ( $wiki ===
false ) {
3076 return \MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancer();
3078 $factory = \MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3079 return $factory->getMainLB( $wiki );
3091 return \MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3139 function wfScript( $script =
'index' ) {
3141 if ( $script ===
'index' ) {
3143 } elseif ( $script ===
'load' ) {
3146 return "{$wgScriptPath}/{$script}.php";
3156 if ( isset( $_SERVER[
'SCRIPT_NAME'] ) ) {
3167 return $_SERVER[
'SCRIPT_NAME'];
3169 return $_SERVER[
'URL'];
3181 return $value ?
'true' :
'false';
3216 $ifWritesSince =
null, $wiki =
false, $cluster =
false, $timeout =
null
3218 if ( $timeout ===
null ) {
3219 $timeout = ( PHP_SAPI ===
'cli' ) ? 86400 : 10;
3222 if ( $cluster ===
'*' ) {
3225 } elseif ( $wiki ===
false ) {
3232 'cluster' => $cluster,
3233 'timeout' => $timeout,
3235 'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince :
null
3252 for ( $i = $seconds; $i >= 0; $i-- ) {
3253 if ( $i != $seconds ) {
3254 echo str_repeat(
"\x08", strlen( $i + 1 ) );
3276 $name = preg_replace(
3294 if ( $memlimit != -1 ) {
3296 if ( $conflimit == -1 ) {
3297 wfDebug(
"Removing PHP's memory limit\n" );
3298 MediaWiki\suppressWarnings();
3299 ini_set(
'memory_limit', $conflimit );
3300 MediaWiki\restoreWarnings();
3302 } elseif ( $conflimit > $memlimit ) {
3303 wfDebug(
"Raising PHP's memory limit to $conflimit bytes\n" );
3304 MediaWiki\suppressWarnings();
3305 ini_set(
'memory_limit', $conflimit );
3306 MediaWiki\restoreWarnings();
3322 $timeLimit = ini_get(
'max_execution_time' );
3328 ignore_user_abort(
true );
3341 $string = trim( $string );
3342 if ( $string ===
'' ) {
3345 $last = $string[strlen( $string ) - 1];
3346 $val = intval( $string );
3372 $codeSegment = explode(
'-',
$code );
3374 foreach ( $codeSegment
as $segNo => $seg ) {
3376 if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) ==
'x' ) {
3377 $codeBCP[$segNo] = strtolower( $seg );
3379 } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
3380 $codeBCP[$segNo] = strtoupper( $seg );
3382 } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
3383 $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
3386 $codeBCP[$segNo] = strtolower( $seg );
3389 $langCode = implode(
'-', $codeBCP );
3461 function wfUnpack( $format, $data, $length =
false ) {
3462 if ( $length !==
false ) {
3463 $realLen = strlen( $data );
3464 if ( $realLen < $length ) {
3465 throw new MWException(
"Tried to use wfUnpack on a "
3466 .
"string of length $realLen, but needed one "
3467 .
"of at least length $length."
3472 MediaWiki\suppressWarnings();
3473 $result = unpack( $format, $data );
3474 MediaWiki\restoreWarnings();
3478 throw new MWException(
"unpack could not unpack binary data" );
3498 # Handle redirects; callers almost always hit wfFindFile() anyway,
3499 # so just use that method because it has a fast process cache.
3503 # Run the extension hook
3510 $key =
wfMemcKey(
'bad-image-list', ( $blacklist ===
null ) ?
'default' : md5( $blacklist ) );
3511 $badImages =
$cache->get( $key );
3513 if ( $badImages ===
false ) {
3514 if ( $blacklist ===
null ) {
3515 $blacklist =
wfMessage(
'bad_image_list' )->inContentLanguage()->plain();
3517 # Build the list now
3519 $lines = explode(
"\n", $blacklist );
3522 if ( substr(
$line, 0, 1 ) !==
'*' ) {
3528 if ( !preg_match_all(
'/\[\[:?(.*?)\]\]/',
$line, $m ) ) {
3533 $imageDBkey =
false;
3534 foreach ( $m[1]
as $i => $titleText ) {
3536 if ( !is_null(
$title ) ) {
3538 $imageDBkey =
$title->getDBkey();
3540 $exceptions[
$title->getPrefixedDBkey()] =
true;
3545 if ( $imageDBkey !==
false ) {
3546 $badImages[$imageDBkey] = $exceptions;
3549 $cache->set( $key, $badImages, 60 );
3552 $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() :
false;
3553 $bad = isset( $badImages[
$name] ) && !isset( $badImages[
$name][$contextKey] );
3567 Hooks::run(
'CanIPUseHTTPS', [ $ip, &$canDo ] );
3579 $infinityValues = [
'infinite',
'indefinite',
'infinity',
'never' ];
3580 return in_array( $str, $infinityValues );
3600 $multipliers = [ 1 ];
3604 $multipliers[] = 1.5;
3614 if ( isset(
$params[
'page'] ) ) {
3615 $basicParams[
'page'] =
$params[
'page'];
3621 foreach ( $multipliers
as $multiplier ) {
3622 $thumbLimits = array_merge( $thumbLimits, array_map(
3623 function ( $width )
use ( $multiplier ) {
3624 return round( $width * $multiplier );
3627 $imageLimits = array_merge( $imageLimits, array_map(
3628 function ( $pair )
use ( $multiplier ) {
3630 round( $pair[0] * $multiplier ),
3631 round( $pair[1] * $multiplier ),
3638 if ( in_array(
$params[
'width'], $thumbLimits ) ) {
3639 $normalParams = $basicParams + [
'width' =>
$params[
'width'] ];
3641 $handler->normaliseParams( $file, $normalParams );
3645 foreach ( $imageLimits
as $pair ) {
3646 $normalParams = $basicParams + [
'width' => $pair[0],
'height' => $pair[1] ];
3649 $handler->normaliseParams( $file, $normalParams );
3651 if ( $normalParams[
'width'] ==
$params[
'width'] ) {
3663 if ( !isset( $normalParams[$key] ) || $normalParams[$key] !=
$value ) {
3685 foreach ( $baseArray
as $name => &$groupVal ) {
3686 if ( isset( $newValues[
$name] ) ) {
3687 $groupVal += $newValues[
$name];
3691 $baseArray += $newValues;