MediaWiki REL1_34
GlobalFunctions.php
Go to the documentation of this file.
1<?php
23if ( !defined( 'MEDIAWIKI' ) ) {
24 die( "This file is part of MediaWiki, it is not a valid entry point" );
25}
26
34use Wikimedia\AtEase\AtEase;
35use Wikimedia\WrappedString;
36
47function wfLoadExtension( $ext, $path = null ) {
48 if ( !$path ) {
50 $path = "$wgExtensionDirectory/$ext/extension.json";
51 }
52 ExtensionRegistry::getInstance()->queue( $path );
53}
54
68function wfLoadExtensions( array $exts ) {
70 $registry = ExtensionRegistry::getInstance();
71 foreach ( $exts as $ext ) {
72 $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
73 }
74}
75
84function wfLoadSkin( $skin, $path = null ) {
85 if ( !$path ) {
86 global $wgStyleDirectory;
87 $path = "$wgStyleDirectory/$skin/skin.json";
88 }
89 ExtensionRegistry::getInstance()->queue( $path );
90}
91
99function wfLoadSkins( array $skins ) {
100 global $wgStyleDirectory;
101 $registry = ExtensionRegistry::getInstance();
102 foreach ( $skins as $skin ) {
103 $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
104 }
105}
106
113function wfArrayDiff2( $a, $b ) {
114 return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
115}
116
122function wfArrayDiff2_cmp( $a, $b ) {
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 );
127 } else {
128 reset( $a );
129 reset( $b );
130 while ( key( $a ) !== null && key( $b ) !== null ) {
131 $valueA = current( $a );
132 $valueB = current( $b );
133 $cmp = strcmp( $valueA, $valueB );
134 if ( $cmp !== 0 ) {
135 return $cmp;
136 }
137 next( $a );
138 next( $b );
139 }
140 return 0;
141 }
142}
143
153function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
154 if ( is_null( $changed ) ) {
155 throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
156 }
157 if ( $default[$key] !== $value ) {
158 $changed[$key] = $value;
159 }
160}
161
181function wfMergeErrorArrays( ...$args ) {
182 $out = [];
183 foreach ( $args as $errors ) {
184 foreach ( $errors as $params ) {
185 $originalParams = $params;
186 if ( $params[0] instanceof MessageSpecifier ) {
187 $msg = $params[0];
188 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
189 }
190 # @todo FIXME: Sometimes get nested arrays for $params,
191 # which leads to E_NOTICEs
192 $spec = implode( "\t", $params );
193 $out[$spec] = $originalParams;
194 }
195 }
196 return array_values( $out );
197}
198
207function wfArrayInsertAfter( array $array, array $insert, $after ) {
208 // Find the offset of the element to insert after.
209 $keys = array_keys( $array );
210 $offsetByKey = array_flip( $keys );
211
212 $offset = $offsetByKey[$after];
213
214 // Insert at the specified offset
215 $before = array_slice( $array, 0, $offset + 1, true );
216 $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
217
218 $output = $before + $insert + $after;
219
220 return $output;
221}
222
230function wfObjectToArray( $objOrArray, $recursive = true ) {
231 $array = [];
232 if ( is_object( $objOrArray ) ) {
233 $objOrArray = get_object_vars( $objOrArray );
234 }
235 foreach ( $objOrArray as $key => $value ) {
236 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
237 $value = wfObjectToArray( $value );
238 }
239
240 $array[$key] = $value;
241 }
242
243 return $array;
244}
245
256function wfRandom() {
257 // The maximum random value is "only" 2^31-1, so get two random
258 // values to reduce the chance of dupes
259 $max = mt_getrandmax() + 1;
260 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
261 return $rand;
262}
263
274function wfRandomString( $length = 32 ) {
275 $str = '';
276 for ( $n = 0; $n < $length; $n += 7 ) {
277 $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
278 }
279 return substr( $str, 0, $length );
280}
281
309function wfUrlencode( $s ) {
310 static $needle;
311
312 if ( is_null( $s ) ) {
313 // Reset $needle for testing.
314 $needle = null;
315 return '';
316 }
317
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 )
322 ) {
323 $needle[] = '%3A';
324 }
325 }
326
327 $s = urlencode( $s );
328 $s = str_ireplace(
329 $needle,
330 [ ';', '@', '$', '!', '*', '(', ')', ',', '/', '~', ':' ],
331 $s
332 );
333
334 return $s;
335}
336
347function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) {
348 if ( !is_null( $array2 ) ) {
349 $array1 = $array1 + $array2;
350 }
351
352 $cgi = '';
353 foreach ( $array1 as $key => $value ) {
354 if ( !is_null( $value ) && $value !== false ) {
355 if ( $cgi != '' ) {
356 $cgi .= '&';
357 }
358 if ( $prefix !== '' ) {
359 $key = $prefix . "[$key]";
360 }
361 if ( is_array( $value ) ) {
362 $firstTime = true;
363 foreach ( $value as $k => $v ) {
364 $cgi .= $firstTime ? '' : '&';
365 if ( is_array( $v ) ) {
366 $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" );
367 } else {
368 $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
369 }
370 $firstTime = false;
371 }
372 } else {
373 if ( is_object( $value ) ) {
374 $value = $value->__toString();
375 }
376 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
377 }
378 }
379 }
380 return $cgi;
381}
382
392function wfCgiToArray( $query ) {
393 if ( isset( $query[0] ) && $query[0] == '?' ) {
394 $query = substr( $query, 1 );
395 }
396 $bits = explode( '&', $query );
397 $ret = [];
398 foreach ( $bits as $bit ) {
399 if ( $bit === '' ) {
400 continue;
401 }
402 if ( strpos( $bit, '=' ) === false ) {
403 // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
404 $key = $bit;
405 $value = '';
406 } else {
407 list( $key, $value ) = explode( '=', $bit );
408 }
409 $key = urldecode( $key );
410 $value = urldecode( $value );
411 if ( strpos( $key, '[' ) !== false ) {
412 $keys = array_reverse( explode( '[', $key ) );
413 $key = array_pop( $keys );
414 $temp = $value;
415 foreach ( $keys as $k ) {
416 $k = substr( $k, 0, -1 );
417 $temp = [ $k => $temp ];
418 }
419 if ( isset( $ret[$key] ) ) {
420 $ret[$key] = array_merge( $ret[$key], $temp );
421 } else {
422 $ret[$key] = $temp;
423 }
424 } else {
425 $ret[$key] = $value;
426 }
427 }
428 return $ret;
429}
430
439function wfAppendQuery( $url, $query ) {
440 if ( is_array( $query ) ) {
441 $query = wfArrayToCgi( $query );
442 }
443 if ( $query != '' ) {
444 // Remove the fragment, if there is one
445 $fragment = false;
446 $hashPos = strpos( $url, '#' );
447 if ( $hashPos !== false ) {
448 $fragment = substr( $url, $hashPos );
449 $url = substr( $url, 0, $hashPos );
450 }
451
452 // Add parameter
453 if ( strpos( $url, '?' ) === false ) {
454 $url .= '?';
455 } else {
456 $url .= '&';
457 }
458 $url .= $query;
459
460 // Put the fragment back
461 if ( $fragment !== false ) {
462 $url .= $fragment;
463 }
464 }
465 return $url;
466}
467
491function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
494 if ( $defaultProto === PROTO_CANONICAL ) {
495 $serverUrl = $wgCanonicalServer;
496 } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
497 // Make $wgInternalServer fall back to $wgServer if not set
498 $serverUrl = $wgInternalServer;
499 } else {
500 $serverUrl = $wgServer;
501 if ( $defaultProto === PROTO_CURRENT ) {
502 $defaultProto = $wgRequest->getProtocol() . '://';
503 }
504 }
505
506 // Analyze $serverUrl to obtain its protocol
507 $bits = wfParseUrl( $serverUrl );
508 $serverHasProto = $bits && $bits['scheme'] != '';
509
510 if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
511 if ( $serverHasProto ) {
512 $defaultProto = $bits['scheme'] . '://';
513 } else {
514 // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
515 // This really isn't supposed to happen. Fall back to HTTP in this
516 // ridiculous case.
517 $defaultProto = PROTO_HTTP;
518 }
519 }
520
521 $defaultProtoWithoutSlashes = $defaultProto !== null ? substr( $defaultProto, 0, -2 ) : '';
522
523 if ( substr( $url, 0, 2 ) == '//' ) {
524 $url = $defaultProtoWithoutSlashes . $url;
525 } elseif ( substr( $url, 0, 1 ) == '/' ) {
526 // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
527 // otherwise leave it alone.
528 if ( $serverHasProto ) {
529 $url = $serverUrl . $url;
530 } else {
531 // If an HTTPS URL is synthesized from a protocol-relative $wgServer, allow the
532 // user to override the port number (T67184)
533 if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
534 if ( isset( $bits['port'] ) ) {
535 throw new Exception( 'A protocol-relative $wgServer may not contain a port number' );
536 }
537 $url = $defaultProtoWithoutSlashes . $serverUrl . ':' . $wgHttpsPort . $url;
538 } else {
539 $url = $defaultProtoWithoutSlashes . $serverUrl . $url;
540 }
541 }
542 }
543
544 $bits = wfParseUrl( $url );
545
546 if ( $bits && isset( $bits['path'] ) ) {
547 $bits['path'] = wfRemoveDotSegments( $bits['path'] );
548 return wfAssembleUrl( $bits );
549 } elseif ( $bits ) {
550 # No path to expand
551 return $url;
552 } elseif ( substr( $url, 0, 1 ) != '/' ) {
553 # URL is a relative path
554 return wfRemoveDotSegments( $url );
555 }
556
557 # Expanded URL is not valid.
558 return false;
559}
560
569function wfGetServerUrl( $proto ) {
570 $url = wfExpandUrl( '/', $proto );
571 return substr( $url, 0, -1 );
572}
573
587function wfAssembleUrl( $urlParts ) {
588 $result = '';
589
590 if ( isset( $urlParts['delimiter'] ) ) {
591 if ( isset( $urlParts['scheme'] ) ) {
592 $result .= $urlParts['scheme'];
593 }
594
595 $result .= $urlParts['delimiter'];
596 }
597
598 if ( isset( $urlParts['host'] ) ) {
599 if ( isset( $urlParts['user'] ) ) {
600 $result .= $urlParts['user'];
601 if ( isset( $urlParts['pass'] ) ) {
602 $result .= ':' . $urlParts['pass'];
603 }
604 $result .= '@';
605 }
606
607 $result .= $urlParts['host'];
608
609 if ( isset( $urlParts['port'] ) ) {
610 $result .= ':' . $urlParts['port'];
611 }
612 }
613
614 if ( isset( $urlParts['path'] ) ) {
615 $result .= $urlParts['path'];
616 }
617
618 if ( isset( $urlParts['query'] ) ) {
619 $result .= '?' . $urlParts['query'];
620 }
621
622 if ( isset( $urlParts['fragment'] ) ) {
623 $result .= '#' . $urlParts['fragment'];
624 }
625
626 return $result;
627}
628
641function wfRemoveDotSegments( $urlPath ) {
642 $output = '';
643 $inputOffset = 0;
644 $inputLength = strlen( $urlPath );
645
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 );
651 $trimOutput = false;
652
653 if ( $prefixLengthTwo == './' ) {
654 # Step A, remove leading "./"
655 $inputOffset += 2;
656 } elseif ( $prefixLengthThree == '../' ) {
657 # Step A, remove leading "../"
658 $inputOffset += 3;
659 } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
660 # Step B, replace leading "/.$" with "/"
661 $inputOffset += 1;
662 $urlPath[$inputOffset] = '/';
663 } elseif ( $prefixLengthThree == '/./' ) {
664 # Step B, replace leading "/./" with "/"
665 $inputOffset += 2;
666 } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
667 # Step C, replace leading "/..$" with "/" and
668 # remove last path component in output
669 $inputOffset += 2;
670 $urlPath[$inputOffset] = '/';
671 $trimOutput = true;
672 } elseif ( $prefixLengthFour == '/../' ) {
673 # Step C, replace leading "/../" with "/" and
674 # remove last path component in output
675 $inputOffset += 3;
676 $trimOutput = true;
677 } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
678 # Step D, remove "^.$"
679 $inputOffset += 1;
680 } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
681 # Step D, remove "^..$"
682 $inputOffset += 2;
683 } else {
684 # Step E, move leading path segment to output
685 if ( $prefixLengthOne == '/' ) {
686 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
687 } else {
688 $slashPos = strpos( $urlPath, '/', $inputOffset );
689 }
690 if ( $slashPos === false ) {
691 $output .= substr( $urlPath, $inputOffset );
692 $inputOffset = $inputLength;
693 } else {
694 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
695 $inputOffset += $slashPos - $inputOffset;
696 }
697 }
698
699 if ( $trimOutput ) {
700 $slashPos = strrpos( $output, '/' );
701 if ( $slashPos === false ) {
702 $output = '';
703 } else {
704 $output = substr( $output, 0, $slashPos );
705 }
706 }
707 }
708
709 return $output;
710}
711
719function wfUrlProtocols( $includeProtocolRelative = true ) {
720 global $wgUrlProtocols;
721
722 // Cache return values separately based on $includeProtocolRelative
723 static $withProtRel = null, $withoutProtRel = null;
724 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
725 if ( !is_null( $cachedValue ) ) {
726 return $cachedValue;
727 }
728
729 // Support old-style $wgUrlProtocols strings, for backwards compatibility
730 // with LocalSettings files from 1.5
731 if ( is_array( $wgUrlProtocols ) ) {
732 $protocols = [];
733 foreach ( $wgUrlProtocols as $protocol ) {
734 // Filter out '//' if !$includeProtocolRelative
735 if ( $includeProtocolRelative || $protocol !== '//' ) {
736 $protocols[] = preg_quote( $protocol, '/' );
737 }
738 }
739
740 $retval = implode( '|', $protocols );
741 } else {
742 // Ignore $includeProtocolRelative in this case
743 // This case exists for pre-1.6 compatibility, and we can safely assume
744 // that '//' won't appear in a pre-1.6 config because protocol-relative
745 // URLs weren't supported until 1.18
746 $retval = $wgUrlProtocols;
747 }
748
749 // Cache return value
750 if ( $includeProtocolRelative ) {
751 $withProtRel = $retval;
752 } else {
753 $withoutProtRel = $retval;
754 }
755 return $retval;
756}
757
765 return wfUrlProtocols( false );
766}
767
793function wfParseUrl( $url ) {
794 global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
795
796 // Protocol-relative URLs are handled really badly by parse_url(). It's so
797 // bad that the easiest way to handle them is to just prepend 'http:' and
798 // strip the protocol out later.
799 $wasRelative = substr( $url, 0, 2 ) == '//';
800 if ( $wasRelative ) {
801 $url = "http:$url";
802 }
803 AtEase::suppressWarnings();
804 $bits = parse_url( $url );
805 AtEase::restoreWarnings();
806 // parse_url() returns an array without scheme for some invalid URLs, e.g.
807 // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
808 if ( !$bits || !isset( $bits['scheme'] ) ) {
809 return false;
810 }
811
812 // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
813 $bits['scheme'] = strtolower( $bits['scheme'] );
814
815 // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
816 if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
817 $bits['delimiter'] = '://';
818 } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
819 $bits['delimiter'] = ':';
820 // parse_url detects for news: and mailto: the host part of an url as path
821 // We have to correct this wrong detection
822 if ( isset( $bits['path'] ) ) {
823 $bits['host'] = $bits['path'];
824 $bits['path'] = '';
825 }
826 } else {
827 return false;
828 }
829
830 /* Provide an empty host for eg. file:/// urls (see T30627) */
831 if ( !isset( $bits['host'] ) ) {
832 $bits['host'] = '';
833
834 // See T47069
835 if ( isset( $bits['path'] ) ) {
836 /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
837 if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
838 $bits['path'] = '/' . $bits['path'];
839 }
840 } else {
841 $bits['path'] = '';
842 }
843 }
844
845 // If the URL was protocol-relative, fix scheme and delimiter
846 if ( $wasRelative ) {
847 $bits['scheme'] = '';
848 $bits['delimiter'] = '//';
849 }
850 return $bits;
851}
852
863function wfExpandIRI( $url ) {
864 return preg_replace_callback(
865 '/((?:%[89A-F][0-9A-F])+)/i',
866 function ( array $matches ) {
867 return urldecode( $matches[1] );
868 },
869 wfExpandUrl( $url )
870 );
871}
872
879function wfMatchesDomainList( $url, $domains ) {
880 $bits = wfParseUrl( $url );
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 ) {
886 return true;
887 }
888 }
889 }
890 return false;
891}
892
913function wfDebug( $text, $dest = 'all', array $context = [] ) {
915 global $wgDebugTimestamps;
916
917 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
918 return;
919 }
920
921 $text = trim( $text );
922
923 if ( $wgDebugTimestamps ) {
924 $context['seconds_elapsed'] = sprintf(
925 '%6.4f',
926 microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT']
927 );
928 $context['memory_used'] = sprintf(
929 '%5.1fM',
930 ( memory_get_usage( true ) / ( 1024 * 1024 ) )
931 );
932 }
933
934 if ( $wgDebugLogPrefix !== '' ) {
935 $context['prefix'] = $wgDebugLogPrefix;
936 }
937 $context['private'] = ( $dest === false || $dest === 'private' );
938
939 $logger = LoggerFactory::getInstance( 'wfDebug' );
940 $logger->debug( $text, $context );
941}
942
948 static $cache;
949 if ( $cache !== null ) {
950 return $cache;
951 }
952 // Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
953 // phpcs:ignore MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
954 if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
955 || (
956 isset( $_SERVER['SCRIPT_NAME'] )
957 && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
958 )
959 ) {
960 $cache = true;
961 } else {
962 $cache = false;
963 }
964 return $cache;
965}
966
972function wfDebugMem( $exact = false ) {
973 $mem = memory_get_usage();
974 if ( !$exact ) {
975 $mem = floor( $mem / 1024 ) . ' KiB';
976 } else {
977 $mem .= ' B';
978 }
979 wfDebug( "Memory usage: $mem\n" );
980}
981
1007function wfDebugLog(
1008 $logGroup, $text, $dest = 'all', array $context = []
1009) {
1010 $text = trim( $text );
1011
1012 $logger = LoggerFactory::getInstance( $logGroup );
1013 $context['private'] = ( $dest === false || $dest === 'private' );
1014 $logger->info( $text, $context );
1015}
1016
1025function wfLogDBError( $text, array $context = [] ) {
1026 $logger = LoggerFactory::getInstance( 'wfLogDBError' );
1027 $logger->error( trim( $text ), $context );
1028}
1029
1044function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
1045 if ( is_string( $version ) || is_bool( $version ) ) {
1046 MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
1047 } else {
1048 throw new Exception(
1049 "MediaWiki version must either be a string or a boolean. " .
1050 "Example valid version: '1.33'"
1051 );
1052 }
1053}
1054
1065function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1066 MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
1067}
1068
1078function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1079 MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
1080}
1081
1088
1089 $context = RequestContext::getMain();
1090 $request = $context->getRequest();
1091
1092 $profiler = Profiler::instance();
1093 $profiler->setContext( $context );
1094 $profiler->logData();
1095
1096 // Send out any buffered statsd metrics as needed
1097 MediaWiki::emitBufferedStatsdData(
1098 MediaWikiServices::getInstance()->getStatsdDataFactory(),
1099 $context->getConfig()
1100 );
1101
1102 // Profiling must actually be enabled...
1103 if ( $profiler instanceof ProfilerStub ) {
1104 return;
1105 }
1106
1107 if ( isset( $wgDebugLogGroups['profileoutput'] )
1108 && $wgDebugLogGroups['profileoutput'] === false
1109 ) {
1110 // Explicitly disabled
1111 return;
1112 }
1113 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1114 return;
1115 }
1116
1117 $ctx = [ 'elapsed' => $request->getElapsedTime() ];
1118 if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1119 $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
1120 }
1121 if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
1122 $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
1123 }
1124 if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
1125 $ctx['from'] = $_SERVER['HTTP_FROM'];
1126 }
1127 if ( isset( $ctx['forwarded_for'] ) ||
1128 isset( $ctx['client_ip'] ) ||
1129 isset( $ctx['from'] ) ) {
1130 $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
1131 }
1132
1133 // Don't load $wgUser at this late stage just for statistics purposes
1134 // @todo FIXME: We can detect some anons even if it is not loaded.
1135 // See User::getId()
1136 $user = $context->getUser();
1137 $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
1138
1139 // Command line script uses a FauxRequest object which does not have
1140 // any knowledge about an URL and throw an exception instead.
1141 try {
1142 $ctx['url'] = urldecode( $request->getRequestURL() );
1143 } catch ( Exception $ignored ) {
1144 // no-op
1145 }
1146
1147 $ctx['output'] = $profiler->getOutput();
1148
1149 $log = LoggerFactory::getInstance( 'profileoutput' );
1150 $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1151}
1152
1160function wfIncrStats( $key, $count = 1 ) {
1161 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1162 $stats->updateCount( $key, $count );
1163}
1164
1170function wfReadOnly() {
1171 return MediaWikiServices::getInstance()->getReadOnlyMode()
1172 ->isReadOnly();
1173}
1174
1184 return MediaWikiServices::getInstance()->getReadOnlyMode()
1185 ->getReason();
1186}
1187
1195 return MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1196 ->getReason();
1197}
1198
1214function wfGetLangObj( $langcode = false ) {
1215 # Identify which language to get or create a language object for.
1216 # Using is_object here due to Stub objects.
1217 if ( is_object( $langcode ) ) {
1218 # Great, we already have the object (hopefully)!
1219 return $langcode;
1220 }
1221
1222 global $wgLanguageCode;
1223 if ( $langcode === true || $langcode === $wgLanguageCode ) {
1224 # $langcode is the language code of the wikis content language object.
1225 # or it is a boolean and value is true
1226 return MediaWikiServices::getInstance()->getContentLanguage();
1227 }
1228
1229 global $wgLang;
1230 if ( $langcode === false || $langcode === $wgLang->getCode() ) {
1231 # $langcode is the language code of user language object.
1232 # or it was a boolean and value is false
1233 return $wgLang;
1234 }
1235
1236 $validCodes = array_keys( Language::fetchLanguageNames() );
1237 if ( in_array( $langcode, $validCodes ) ) {
1238 # $langcode corresponds to a valid language.
1239 return Language::factory( $langcode );
1240 }
1241
1242 # $langcode is a string, but not a valid language code; use content language.
1243 wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1244 return MediaWikiServices::getInstance()->getContentLanguage();
1245}
1246
1263function wfMessage( $key, ...$params ) {
1264 $message = new Message( $key );
1265
1266 // We call Message::params() to reduce code duplication
1267 if ( $params ) {
1268 $message->params( ...$params );
1269 }
1270
1271 return $message;
1272}
1273
1286function wfMessageFallback( ...$keys ) {
1288}
1289
1298function wfMsgReplaceArgs( $message, $args ) {
1299 # Fix windows line-endings
1300 # Some messages are split with explode("\n", $msg)
1301 $message = str_replace( "\r", '', $message );
1302
1303 // Replace arguments
1304 if ( is_array( $args ) && $args ) {
1305 if ( is_array( $args[0] ) ) {
1306 $args = array_values( $args[0] );
1307 }
1308 $replacementKeys = [];
1309 foreach ( $args as $n => $param ) {
1310 $replacementKeys['$' . ( $n + 1 )] = $param;
1311 }
1312 $message = strtr( $message, $replacementKeys );
1313 }
1314
1315 return $message;
1316}
1317
1326function wfHostname() {
1327 // Hostname overriding
1328 global $wgOverrideHostname;
1329 if ( $wgOverrideHostname !== false ) {
1330 return $wgOverrideHostname;
1331 }
1332
1333 return php_uname( 'n' ) ?: 'unknown';
1334}
1335
1346function wfReportTime( $nonce = null ) {
1347 global $wgShowHostnames;
1348
1349 $elapsed = ( microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'] );
1350 // seconds to milliseconds
1351 $responseTime = round( $elapsed * 1000 );
1352 $reportVars = [ 'wgBackendResponseTime' => $responseTime ];
1353 if ( $wgShowHostnames ) {
1354 $reportVars['wgHostname'] = wfHostname();
1355 }
1356 return Skin::makeVariablesScript( $reportVars, $nonce );
1357}
1358
1369function wfDebugBacktrace( $limit = 0 ) {
1370 static $disabled = null;
1371
1372 if ( is_null( $disabled ) ) {
1373 $disabled = !function_exists( 'debug_backtrace' );
1374 if ( $disabled ) {
1375 wfDebug( "debug_backtrace() is disabled\n" );
1376 }
1377 }
1378 if ( $disabled ) {
1379 return [];
1380 }
1381
1382 if ( $limit ) {
1383 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1384 } else {
1385 return array_slice( debug_backtrace(), 1 );
1386 }
1387}
1388
1397function wfBacktrace( $raw = null ) {
1398 global $wgCommandLineMode;
1399
1400 if ( $raw === null ) {
1401 $raw = $wgCommandLineMode;
1402 }
1403
1404 if ( $raw ) {
1405 $frameFormat = "%s line %s calls %s()\n";
1406 $traceFormat = "%s";
1407 } else {
1408 $frameFormat = "<li>%s line %s calls %s()</li>\n";
1409 $traceFormat = "<ul>\n%s</ul>\n";
1410 }
1411
1412 $frames = array_map( function ( $frame ) use ( $frameFormat ) {
1413 $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
1414 $line = $frame['line'] ?? '-';
1415 $call = $frame['function'];
1416 if ( !empty( $frame['class'] ) ) {
1417 $call = $frame['class'] . $frame['type'] . $call;
1418 }
1419 return sprintf( $frameFormat, $file, $line, $call );
1420 }, wfDebugBacktrace() );
1421
1422 return sprintf( $traceFormat, implode( '', $frames ) );
1423}
1424
1434function wfGetCaller( $level = 2 ) {
1435 $backtrace = wfDebugBacktrace( $level + 1 );
1436 if ( isset( $backtrace[$level] ) ) {
1437 return wfFormatStackFrame( $backtrace[$level] );
1438 } else {
1439 return 'unknown';
1440 }
1441}
1442
1450function wfGetAllCallers( $limit = 3 ) {
1451 $trace = array_reverse( wfDebugBacktrace() );
1452 if ( !$limit || $limit > count( $trace ) - 1 ) {
1453 $limit = count( $trace ) - 1;
1454 }
1455 $trace = array_slice( $trace, -$limit - 1, $limit );
1456 return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
1457}
1458
1465function wfFormatStackFrame( $frame ) {
1466 if ( !isset( $frame['function'] ) ) {
1467 return 'NO_FUNCTION_GIVEN';
1468 }
1469 return isset( $frame['class'] ) && isset( $frame['type'] ) ?
1470 $frame['class'] . $frame['type'] . $frame['function'] :
1471 $frame['function'];
1472}
1473
1474/* Some generic result counters, pulled out of SearchEngine */
1475
1483function wfShowingResults( $offset, $limit ) {
1484 return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1485}
1486
1496function wfClientAcceptsGzip( $force = false ) {
1497 static $result = null;
1498 if ( $result === null || $force ) {
1499 $result = false;
1500 if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
1501 # @todo FIXME: We may want to blacklist some broken browsers
1502 $m = [];
1503 if ( preg_match(
1504 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1505 $_SERVER['HTTP_ACCEPT_ENCODING'],
1506 $m
1507 )
1508 ) {
1509 if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
1510 $result = false;
1511 return $result;
1512 }
1513 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
1514 $result = true;
1515 }
1516 }
1517 }
1518 return $result;
1519}
1520
1531function wfEscapeWikiText( $text ) {
1532 global $wgEnableMagicLinks;
1533 static $repl = null, $repl2 = null;
1534 if ( $repl === null || defined( 'MW_PARSER_TEST' ) || defined( 'MW_PHPUNIT_TEST' ) ) {
1535 // Tests depend upon being able to change $wgEnableMagicLinks, so don't cache
1536 // in those situations
1537 $repl = [
1538 '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
1539 '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
1540 '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
1541 "\n#" => "\n&#35;", "\r#" => "\r&#35;",
1542 "\n*" => "\n&#42;", "\r*" => "\r&#42;",
1543 "\n:" => "\n&#58;", "\r:" => "\r&#58;",
1544 "\n " => "\n&#32;", "\r " => "\r&#32;",
1545 "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
1546 "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
1547 "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
1548 "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
1549 '__' => '_&#95;', '://' => '&#58;//',
1550 ];
1551
1552 $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
1553 // We have to catch everything "\s" matches in PCRE
1554 foreach ( $magicLinks as $magic ) {
1555 $repl["$magic "] = "$magic&#32;";
1556 $repl["$magic\t"] = "$magic&#9;";
1557 $repl["$magic\r"] = "$magic&#13;";
1558 $repl["$magic\n"] = "$magic&#10;";
1559 $repl["$magic\f"] = "$magic&#12;";
1560 }
1561
1562 // And handle protocols that don't use "://"
1563 global $wgUrlProtocols;
1564 $repl2 = [];
1565 foreach ( $wgUrlProtocols as $prot ) {
1566 if ( substr( $prot, -1 ) === ':' ) {
1567 $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
1568 }
1569 }
1570 $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/';
1571 }
1572 $text = substr( strtr( "\n$text", $repl ), 1 );
1573 $text = preg_replace( $repl2, '$1&#58;', $text );
1574 return $text;
1575}
1576
1587function wfSetVar( &$dest, $source, $force = false ) {
1588 $temp = $dest;
1589 if ( !is_null( $source ) || $force ) {
1590 $dest = $source;
1591 }
1592 return $temp;
1593}
1594
1604function wfSetBit( &$dest, $bit, $state = true ) {
1605 $temp = (bool)( $dest & $bit );
1606 if ( !is_null( $state ) ) {
1607 if ( $state ) {
1608 $dest |= $bit;
1609 } else {
1610 $dest &= ~$bit;
1611 }
1612 }
1613 return $temp;
1614}
1615
1622function wfVarDump( $var ) {
1623 global $wgOut;
1624 $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
1625 if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
1626 print $s;
1627 } else {
1628 $wgOut->addHTML( $s );
1629 }
1630}
1631
1639function wfHttpError( $code, $label, $desc ) {
1640 global $wgOut;
1641 HttpStatus::header( $code );
1642 if ( $wgOut ) {
1643 $wgOut->disable();
1644 $wgOut->sendCacheControl();
1645 }
1646
1647 MediaWiki\HeaderCallback::warnIfHeadersSent();
1648 header( 'Content-type: text/html; charset=utf-8' );
1649 print '<!DOCTYPE html>' .
1650 '<html><head><title>' .
1651 htmlspecialchars( $label ) .
1652 '</title></head><body><h1>' .
1653 htmlspecialchars( $label ) .
1654 '</h1><p>' .
1655 nl2br( htmlspecialchars( $desc ) ) .
1656 "</p></body></html>\n";
1657}
1658
1676function wfResetOutputBuffers( $resetGzipEncoding = true ) {
1677 if ( $resetGzipEncoding ) {
1678 // Suppress Content-Encoding and Content-Length
1679 // headers from OutputHandler::handle.
1682 }
1683 while ( $status = ob_get_status() ) {
1684 if ( isset( $status['flags'] ) ) {
1685 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1686 $deleteable = ( $status['flags'] & $flags ) === $flags;
1687 } elseif ( isset( $status['del'] ) ) {
1688 $deleteable = $status['del'];
1689 } else {
1690 // Guess that any PHP-internal setting can't be removed.
1691 $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
1692 }
1693 if ( !$deleteable ) {
1694 // Give up, and hope the result doesn't break
1695 // output behavior.
1696 break;
1697 }
1698 if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1699 // Unit testing barrier to prevent this function from breaking PHPUnit.
1700 break;
1701 }
1702 if ( !ob_end_clean() ) {
1703 // Could not remove output buffer handler; abort now
1704 // to avoid getting in some kind of infinite loop.
1705 break;
1706 }
1707 if ( $resetGzipEncoding && $status['name'] == 'ob_gzhandler' ) {
1708 // Reset the 'Content-Encoding' field set by this handler
1709 // so we can start fresh.
1710 header_remove( 'Content-Encoding' );
1711 break;
1712 }
1713 }
1714}
1715
1729 wfResetOutputBuffers( false );
1730}
1731
1740function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1741 # No arg means accept anything (per HTTP spec)
1742 if ( !$accept ) {
1743 return [ $def => 1.0 ];
1744 }
1745
1746 $prefs = [];
1747
1748 $parts = explode( ',', $accept );
1749
1750 foreach ( $parts as $part ) {
1751 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1752 $values = explode( ';', trim( $part ) );
1753 $match = [];
1754 if ( count( $values ) == 1 ) {
1755 $prefs[$values[0]] = 1.0;
1756 } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1757 $prefs[$values[0]] = floatval( $match[1] );
1758 }
1759 }
1760
1761 return $prefs;
1762}
1763
1776function mimeTypeMatch( $type, $avail ) {
1777 if ( array_key_exists( $type, $avail ) ) {
1778 return $type;
1779 } else {
1780 $mainType = explode( '/', $type )[0];
1781 if ( array_key_exists( "$mainType/*", $avail ) ) {
1782 return "$mainType/*";
1783 } elseif ( array_key_exists( '*/*', $avail ) ) {
1784 return '*/*';
1785 } else {
1786 return null;
1787 }
1788 }
1789}
1790
1805function wfNegotiateType( $cprefs, $sprefs ) {
1806 $combine = [];
1807
1808 foreach ( array_keys( $sprefs ) as $type ) {
1809 $subType = explode( '/', $type )[1];
1810 if ( $subType != '*' ) {
1811 $ckey = mimeTypeMatch( $type, $cprefs );
1812 if ( $ckey ) {
1813 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1814 }
1815 }
1816 }
1817
1818 foreach ( array_keys( $cprefs ) as $type ) {
1819 $subType = explode( '/', $type )[1];
1820 if ( $subType != '*' && !array_key_exists( $type, $sprefs ) ) {
1821 $skey = mimeTypeMatch( $type, $sprefs );
1822 if ( $skey ) {
1823 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1824 }
1825 }
1826 }
1827
1828 $bestq = 0;
1829 $besttype = null;
1830
1831 foreach ( array_keys( $combine ) as $type ) {
1832 if ( $combine[$type] > $bestq ) {
1833 $besttype = $type;
1834 $bestq = $combine[$type];
1835 }
1836 }
1837
1838 return $besttype;
1839}
1840
1849function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
1850 $ret = MWTimestamp::convert( $outputtype, $ts );
1851 if ( $ret === false ) {
1852 wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
1853 }
1854 return $ret;
1855}
1856
1865function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
1866 if ( is_null( $ts ) ) {
1867 return null;
1868 } else {
1869 return wfTimestamp( $outputtype, $ts );
1870 }
1871}
1872
1878function wfTimestampNow() {
1879 return MWTimestamp::now( TS_MW );
1880}
1881
1887function wfIsWindows() {
1888 static $isWindows = null;
1889 if ( $isWindows === null ) {
1890 $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
1891 }
1892 return $isWindows;
1893}
1894
1901function wfIsHHVM() {
1902 // wfDeprecated( __FUNCTION__, '1.34' );
1903 return false;
1904}
1905
1912function wfIsCLI() {
1913 return PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg';
1914}
1915
1927function wfTempDir() {
1928 global $wgTmpDirectory;
1929
1930 if ( $wgTmpDirectory !== false ) {
1931 return $wgTmpDirectory;
1932 }
1933
1934 return TempFSFile::getUsableTempDirectory();
1935}
1936
1946function wfMkdirParents( $dir, $mode = null, $caller = null ) {
1947 global $wgDirectoryMode;
1948
1949 if ( FileBackend::isStoragePath( $dir ) ) { // sanity
1950 throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
1951 }
1952
1953 if ( !is_null( $caller ) ) {
1954 wfDebug( "$caller: called wfMkdirParents($dir)\n" );
1955 }
1956
1957 if ( strval( $dir ) === '' || is_dir( $dir ) ) {
1958 return true;
1959 }
1960
1961 $dir = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $dir );
1962
1963 if ( is_null( $mode ) ) {
1964 $mode = $wgDirectoryMode;
1965 }
1966
1967 // Turn off the normal warning, we're doing our own below
1968 AtEase::suppressWarnings();
1969 $ok = mkdir( $dir, $mode, true ); // PHP5 <3
1970 AtEase::restoreWarnings();
1971
1972 if ( !$ok ) {
1973 // directory may have been created on another request since we last checked
1974 if ( is_dir( $dir ) ) {
1975 return true;
1976 }
1977
1978 // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
1979 wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
1980 }
1981 return $ok;
1982}
1983
1989function wfRecursiveRemoveDir( $dir ) {
1990 wfDebug( __FUNCTION__ . "( $dir )\n" );
1991 // taken from https://www.php.net/manual/en/function.rmdir.php#98622
1992 if ( is_dir( $dir ) ) {
1993 $objects = scandir( $dir );
1994 foreach ( $objects as $object ) {
1995 if ( $object != "." && $object != ".." ) {
1996 if ( filetype( $dir . '/' . $object ) == "dir" ) {
1997 wfRecursiveRemoveDir( $dir . '/' . $object );
1998 } else {
1999 unlink( $dir . '/' . $object );
2000 }
2001 }
2002 }
2003 reset( $objects );
2004 rmdir( $dir );
2005 }
2006}
2007
2014function wfPercent( $nr, $acc = 2, $round = true ) {
2015 $ret = sprintf( "%.${acc}f", $nr );
2016 return $round ? round( (float)$ret, $acc ) . '%' : "$ret%";
2017}
2018
2042function wfIniGetBool( $setting ) {
2043 return wfStringToBool( ini_get( $setting ) );
2044}
2045
2058function wfStringToBool( $val ) {
2059 $val = strtolower( $val );
2060 // 'on' and 'true' can't have whitespace around them, but '1' can.
2061 return $val == 'on'
2062 || $val == 'true'
2063 || $val == 'yes'
2064 || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
2065}
2066
2079function wfEscapeShellArg( ...$args ) {
2080 return Shell::escape( ...$args );
2081}
2082
2107function wfShellExec( $cmd, &$retval = null, $environ = [],
2108 $limits = [], $options = []
2109) {
2110 if ( Shell::isDisabled() ) {
2111 $retval = 1;
2112 // Backwards compatibility be upon us...
2113 return 'Unable to run external programs, proc_open() is disabled.';
2114 }
2115
2116 if ( is_array( $cmd ) ) {
2117 $cmd = Shell::escape( $cmd );
2118 }
2119
2120 $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
2121 $profileMethod = $options['profileMethod'] ?? wfGetCaller();
2122
2123 try {
2124 $result = Shell::command( [] )
2125 ->unsafeParams( (array)$cmd )
2126 ->environment( $environ )
2127 ->limits( $limits )
2128 ->includeStderr( $includeStderr )
2129 ->profileMethod( $profileMethod )
2130 // For b/c
2131 ->restrict( Shell::RESTRICT_NONE )
2132 ->execute();
2133 } catch ( ProcOpenError $ex ) {
2134 $retval = -1;
2135 return '';
2136 }
2137
2138 $retval = $result->getExitCode();
2139
2140 return $result->getStdout();
2141}
2142
2160function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
2161 return wfShellExec( $cmd, $retval, $environ, $limits,
2162 [ 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ] );
2163}
2164
2180function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
2181 global $wgPhpCli;
2182 // Give site config file a chance to run the script in a wrapper.
2183 // The caller may likely want to call wfBasename() on $script.
2184 Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2185 $cmd = [ $options['php'] ?? $wgPhpCli ];
2186 if ( isset( $options['wrapper'] ) ) {
2187 $cmd[] = $options['wrapper'];
2188 }
2189 $cmd[] = $script;
2190 // Escape each parameter for shell
2191 return Shell::escape( array_merge( $cmd, $parameters ) );
2192}
2193
2205function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult = null ) {
2206 global $wgDiff3;
2207
2208 # This check may also protect against code injection in
2209 # case of broken installations.
2210 AtEase::suppressWarnings();
2211 $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2212 AtEase::restoreWarnings();
2213
2214 if ( !$haveDiff3 ) {
2215 wfDebug( "diff3 not found\n" );
2216 return false;
2217 }
2218
2219 # Make temporary files
2220 $td = wfTempDir();
2221 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2222 $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
2223 $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
2224
2225 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2226 # a newline character. To avoid this, we normalize the trailing whitespace before
2227 # creating the diff.
2228
2229 fwrite( $oldtextFile, rtrim( $old ) . "\n" );
2230 fclose( $oldtextFile );
2231 fwrite( $mytextFile, rtrim( $mine ) . "\n" );
2232 fclose( $mytextFile );
2233 fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
2234 fclose( $yourtextFile );
2235
2236 # Check for a conflict
2237 $cmd = Shell::escape( $wgDiff3, '-a', '--overlap-only', $mytextName,
2238 $oldtextName, $yourtextName );
2239 $handle = popen( $cmd, 'r' );
2240
2241 $mergeAttemptResult = '';
2242 do {
2243 $data = fread( $handle, 8192 );
2244 if ( strlen( $data ) == 0 ) {
2245 break;
2246 }
2247 $mergeAttemptResult .= $data;
2248 } while ( true );
2249 pclose( $handle );
2250
2251 $conflict = $mergeAttemptResult !== '';
2252
2253 # Merge differences
2254 $cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName,
2255 $oldtextName, $yourtextName );
2256 $handle = popen( $cmd, 'r' );
2257 $result = '';
2258 do {
2259 $data = fread( $handle, 8192 );
2260 if ( strlen( $data ) == 0 ) {
2261 break;
2262 }
2263 $result .= $data;
2264 } while ( true );
2265 pclose( $handle );
2266 unlink( $mytextName );
2267 unlink( $oldtextName );
2268 unlink( $yourtextName );
2269
2270 if ( $result === '' && $old !== '' && !$conflict ) {
2271 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
2272 $conflict = true;
2273 }
2274 return !$conflict;
2275}
2276
2288function wfDiff( $before, $after, $params = '-u' ) {
2289 if ( $before == $after ) {
2290 return '';
2291 }
2292
2293 global $wgDiff;
2294 AtEase::suppressWarnings();
2295 $haveDiff = $wgDiff && file_exists( $wgDiff );
2296 AtEase::restoreWarnings();
2297
2298 # This check may also protect against code injection in
2299 # case of broken installations.
2300 if ( !$haveDiff ) {
2301 wfDebug( "diff executable not found\n" );
2302 $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
2303 $format = new UnifiedDiffFormatter();
2304 return $format->format( $diffs );
2305 }
2306
2307 # Make temporary files
2308 $td = wfTempDir();
2309 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2310 $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
2311
2312 fwrite( $oldtextFile, $before );
2313 fclose( $oldtextFile );
2314 fwrite( $newtextFile, $after );
2315 fclose( $newtextFile );
2316
2317 // Get the diff of the two files
2318 $cmd = "$wgDiff " . $params . ' ' . Shell::escape( $oldtextName, $newtextName );
2319
2320 $h = popen( $cmd, 'r' );
2321 if ( !$h ) {
2322 unlink( $oldtextName );
2323 unlink( $newtextName );
2324 throw new Exception( __METHOD__ . '(): popen() failed' );
2325 }
2326
2327 $diff = '';
2328
2329 do {
2330 $data = fread( $h, 8192 );
2331 if ( strlen( $data ) == 0 ) {
2332 break;
2333 }
2334 $diff .= $data;
2335 } while ( true );
2336
2337 // Clean up
2338 pclose( $h );
2339 unlink( $oldtextName );
2340 unlink( $newtextName );
2341
2342 // Kill the --- and +++ lines. They're not useful.
2343 $diff_lines = explode( "\n", $diff );
2344 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
2345 unset( $diff_lines[0] );
2346 }
2347 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
2348 unset( $diff_lines[1] );
2349 }
2350
2351 $diff = implode( "\n", $diff_lines );
2352
2353 return $diff;
2354}
2355
2368function wfBaseName( $path, $suffix = '' ) {
2369 if ( $suffix == '' ) {
2370 $encSuffix = '';
2371 } else {
2372 $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
2373 }
2374
2375 $matches = [];
2376 if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
2377 return $matches[1];
2378 } else {
2379 return '';
2380 }
2381}
2382
2392function wfRelativePath( $path, $from ) {
2393 // Normalize mixed input on Windows...
2394 $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
2395 $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
2396
2397 // Trim trailing slashes -- fix for drive root
2398 $path = rtrim( $path, DIRECTORY_SEPARATOR );
2399 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2400
2401 $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
2402 $against = explode( DIRECTORY_SEPARATOR, $from );
2403
2404 if ( $pieces[0] !== $against[0] ) {
2405 // Non-matching Windows drive letters?
2406 // Return a full path.
2407 return $path;
2408 }
2409
2410 // Trim off common prefix
2411 while ( count( $pieces ) && count( $against )
2412 && $pieces[0] == $against[0] ) {
2413 array_shift( $pieces );
2414 array_shift( $against );
2415 }
2416
2417 // relative dots to bump us to the parent
2418 while ( count( $against ) ) {
2419 array_unshift( $pieces, '..' );
2420 array_shift( $against );
2421 }
2422
2423 array_push( $pieces, wfBaseName( $path ) );
2424
2425 return implode( DIRECTORY_SEPARATOR, $pieces );
2426}
2427
2437function wfSetupSession( $sessionId = false ) {
2438 wfDeprecated( __FUNCTION__, '1.27' );
2439
2440 if ( $sessionId ) {
2441 session_id( $sessionId );
2442 }
2443
2444 $session = SessionManager::getGlobalSession();
2445 $session->persist();
2446
2447 if ( session_id() !== $session->getId() ) {
2448 session_id( $session->getId() );
2449 }
2450 AtEase::quietCall( 'session_start' );
2451}
2452
2459function wfGetPrecompiledData( $name ) {
2460 global $IP;
2461
2462 $file = "$IP/serialized/$name";
2463 if ( file_exists( $file ) ) {
2464 $blob = file_get_contents( $file );
2465 if ( $blob ) {
2466 return unserialize( $blob );
2467 }
2468 }
2469 return false;
2470}
2471
2479function wfMemcKey( ...$args ) {
2480 return ObjectCache::getLocalClusterInstance()->makeKey( ...$args );
2481}
2482
2493function wfForeignMemcKey( $db, $prefix, ...$args ) {
2494 $keyspace = $prefix ? "$db-$prefix" : $db;
2495 return ObjectCache::getLocalClusterInstance()->makeKeyInternal( $keyspace, $args );
2496}
2497
2510function wfGlobalCacheKey( ...$args ) {
2511 wfDeprecated( __METHOD__, '1.30' );
2512 return ObjectCache::getLocalClusterInstance()->makeGlobalKey( ...$args );
2513}
2514
2521function wfWikiID() {
2522 global $wgDBprefix, $wgDBname;
2523 if ( $wgDBprefix ) {
2524 return "$wgDBname-$wgDBprefix";
2525 } else {
2526 return $wgDBname;
2527 }
2528}
2529
2555function wfGetDB( $db, $groups = [], $wiki = false ) {
2556 return wfGetLB( $wiki )->getMaintenanceConnectionRef( $db, $groups, $wiki );
2557}
2558
2568function wfGetLB( $wiki = false ) {
2569 if ( $wiki === false ) {
2570 return MediaWikiServices::getInstance()->getDBLoadBalancer();
2571 } else {
2572 $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2573 return $factory->getMainLB( $wiki );
2574 }
2575}
2576
2584function wfFindFile( $title, $options = [] ) {
2585 return MediaWikiServices::getInstance()->getRepoGroup()->findFile( $title, $options );
2586}
2587
2596function wfLocalFile( $title ) {
2597 return MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $title );
2598}
2599
2607 global $wgMiserMode;
2608 return $wgMiserMode
2609 || ( SiteStats::pages() > 100000
2610 && SiteStats::edits() > 1000000
2611 && SiteStats::users() > 10000 );
2612}
2613
2622function wfScript( $script = 'index' ) {
2624 if ( $script === 'index' ) {
2625 return $wgScript;
2626 } elseif ( $script === 'load' ) {
2627 return $wgLoadScript;
2628 } else {
2629 return "{$wgScriptPath}/{$script}.php";
2630 }
2631}
2632
2638function wfGetScriptUrl() {
2639 if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
2640 /* as it was called, minus the query string.
2641 *
2642 * Some sites use Apache rewrite rules to handle subdomains,
2643 * and have PHP set up in a weird way that causes PHP_SELF
2644 * to contain the rewritten URL instead of the one that the
2645 * outside world sees.
2646 *
2647 * If in this mode, use SCRIPT_URL instead, which mod_rewrite
2648 * provides containing the "before" URL.
2649 */
2650 return $_SERVER['SCRIPT_NAME'];
2651 } else {
2652 return $_SERVER['URL'];
2653 }
2654}
2655
2663function wfBoolToStr( $value ) {
2664 return $value ? 'true' : 'false';
2665}
2666
2672function wfGetNull() {
2673 return wfIsWindows() ? 'NUL' : '/dev/null';
2674}
2675
2699 $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
2700) {
2701 $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2702
2703 if ( $cluster === '*' ) {
2704 $cluster = false;
2705 $domain = false;
2706 } elseif ( $wiki === false ) {
2707 $domain = $lbFactory->getLocalDomainID();
2708 } else {
2709 $domain = $wiki;
2710 }
2711
2712 $opts = [
2713 'domain' => $domain,
2714 'cluster' => $cluster,
2715 // B/C: first argument used to be "max seconds of lag"; ignore such values
2716 'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
2717 ];
2718 if ( $timeout !== null ) {
2719 $opts['timeout'] = $timeout;
2720 }
2721
2722 return $lbFactory->waitForReplication( $opts );
2723}
2724
2734 global $wgIllegalFileChars;
2735 $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
2736 $name = preg_replace(
2737 "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
2738 '-',
2739 $name
2740 );
2741 // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
2742 $name = wfBaseName( $name );
2743 return $name;
2744}
2745
2751function wfMemoryLimit( $newLimit ) {
2752 $oldLimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
2753 // If the INI config is already unlimited, there is nothing larger
2754 if ( $oldLimit != -1 ) {
2755 $newLimit = wfShorthandToInteger( $newLimit );
2756 if ( $newLimit == -1 ) {
2757 wfDebug( "Removing PHP's memory limit\n" );
2758 Wikimedia\suppressWarnings();
2759 ini_set( 'memory_limit', $newLimit );
2760 Wikimedia\restoreWarnings();
2761 } elseif ( $newLimit > $oldLimit ) {
2762 wfDebug( "Raising PHP's memory limit to $newLimit bytes\n" );
2763 Wikimedia\suppressWarnings();
2764 ini_set( 'memory_limit', $newLimit );
2765 Wikimedia\restoreWarnings();
2766 }
2767 }
2768}
2769
2778
2779 $timeLimit = (int)ini_get( 'max_execution_time' );
2780 // Note that CLI scripts use 0
2781 if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
2782 set_time_limit( $wgTransactionalTimeLimit );
2783 }
2784
2785 ignore_user_abort( true ); // ignore client disconnects
2786
2787 return $timeLimit;
2788}
2789
2797function wfShorthandToInteger( $string = '', $default = -1 ) {
2798 $string = trim( $string );
2799 if ( $string === '' ) {
2800 return $default;
2801 }
2802 $last = $string[strlen( $string ) - 1];
2803 $val = intval( $string );
2804 switch ( $last ) {
2805 case 'g':
2806 case 'G':
2807 $val *= 1024;
2808 // break intentionally missing
2809 case 'm':
2810 case 'M':
2811 $val *= 1024;
2812 // break intentionally missing
2813 case 'k':
2814 case 'K':
2815 $val *= 1024;
2816 }
2817
2818 return $val;
2819}
2820
2828function wfGetCache( $cacheType ) {
2829 return ObjectCache::getInstance( $cacheType );
2830}
2831
2838function wfGetMainCache() {
2839 return ObjectCache::getLocalClusterInstance();
2840}
2841
2848 global $wgMessageCacheType;
2849 return ObjectCache::getInstance( $wgMessageCacheType );
2850}
2851
2866function wfUnpack( $format, $data, $length = false ) {
2867 if ( $length !== false ) {
2868 $realLen = strlen( $data );
2869 if ( $realLen < $length ) {
2870 throw new MWException( "Tried to use wfUnpack on a "
2871 . "string of length $realLen, but needed one "
2872 . "of at least length $length."
2873 );
2874 }
2875 }
2876
2877 Wikimedia\suppressWarnings();
2878 $result = unpack( $format, $data );
2879 Wikimedia\restoreWarnings();
2880
2881 if ( $result === false ) {
2882 // If it cannot extract the packed data.
2883 throw new MWException( "unpack could not unpack binary data" );
2884 }
2885 return $result;
2886}
2887
2904function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
2905 $services = MediaWikiServices::getInstance();
2906 if ( $blacklist !== null ) {
2907 wfDeprecated( __METHOD__ . ' with $blacklist parameter', '1.34' );
2908 return ( new BadFileLookup(
2909 function () use ( $blacklist ) {
2910 return $blacklist;
2911 },
2912 $services->getLocalServerObjectCache(),
2913 $services->getRepoGroup(),
2914 $services->getTitleParser()
2915 ) )->isBadFile( $name, $contextTitle ?: null );
2916 }
2917 return $services->getBadFileLookup()->isBadFile( $name, $contextTitle ?: null );
2918}
2919
2927function wfCanIPUseHTTPS( $ip ) {
2928 $canDo = true;
2929 Hooks::run( 'CanIPUseHTTPS', [ $ip, &$canDo ] );
2930 return (bool)$canDo;
2931}
2932
2940function wfIsInfinity( $str ) {
2941 // These are hardcoded elsewhere in MediaWiki (e.g. mediawiki.special.block.js).
2942 $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
2943 return in_array( $str, $infinityValues );
2944}
2945
2960function wfThumbIsStandard( File $file, array $params ) {
2962
2963 $multipliers = [ 1 ];
2964 if ( $wgResponsiveImages ) {
2965 // These available sizes are hardcoded currently elsewhere in MediaWiki.
2966 // @see Linker::processResponsiveImages
2967 $multipliers[] = 1.5;
2968 $multipliers[] = 2;
2969 }
2970
2971 $handler = $file->getHandler();
2972 if ( !$handler || !isset( $params['width'] ) ) {
2973 return false;
2974 }
2975
2976 $basicParams = [];
2977 if ( isset( $params['page'] ) ) {
2978 $basicParams['page'] = $params['page'];
2979 }
2980
2981 $thumbLimits = [];
2982 $imageLimits = [];
2983 // Expand limits to account for multipliers
2984 foreach ( $multipliers as $multiplier ) {
2985 $thumbLimits = array_merge( $thumbLimits, array_map(
2986 function ( $width ) use ( $multiplier ) {
2987 return round( $width * $multiplier );
2988 }, $wgThumbLimits )
2989 );
2990 $imageLimits = array_merge( $imageLimits, array_map(
2991 function ( $pair ) use ( $multiplier ) {
2992 return [
2993 round( $pair[0] * $multiplier ),
2994 round( $pair[1] * $multiplier ),
2995 ];
2996 }, $wgImageLimits )
2997 );
2998 }
2999
3000 // Check if the width matches one of $wgThumbLimits
3001 if ( in_array( $params['width'], $thumbLimits ) ) {
3002 $normalParams = $basicParams + [ 'width' => $params['width'] ];
3003 // Append any default values to the map (e.g. "lossy", "lossless", ...)
3004 $handler->normaliseParams( $file, $normalParams );
3005 } else {
3006 // If not, then check if the width matchs one of $wgImageLimits
3007 $match = false;
3008 foreach ( $imageLimits as $pair ) {
3009 $normalParams = $basicParams + [ 'width' => $pair[0], 'height' => $pair[1] ];
3010 // Decide whether the thumbnail should be scaled on width or height.
3011 // Also append any default values to the map (e.g. "lossy", "lossless", ...)
3012 $handler->normaliseParams( $file, $normalParams );
3013 // Check if this standard thumbnail size maps to the given width
3014 if ( $normalParams['width'] == $params['width'] ) {
3015 $match = true;
3016 break;
3017 }
3018 }
3019 if ( !$match ) {
3020 return false; // not standard for description pages
3021 }
3022 }
3023
3024 // Check that the given values for non-page, non-width, params are just defaults
3025 foreach ( $params as $key => $value ) {
3026 if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3027 return false;
3028 }
3029 }
3030
3031 return true;
3032}
3033
3046function wfArrayPlus2d( array $baseArray, array $newValues ) {
3047 // First merge items that are in both arrays
3048 foreach ( $baseArray as $name => &$groupVal ) {
3049 if ( isset( $newValues[$name] ) ) {
3050 $groupVal += $newValues[$name];
3051 }
3052 }
3053 // Now add items that didn't exist yet
3054 $baseArray += $newValues;
3055
3056 return $baseArray;
3057}
3058
3067function wfGetRusage() {
3068 if ( !function_exists( 'getrusage' ) ) {
3069 return false;
3070 } elseif ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
3071 return getrusage( 2 /* RUSAGE_THREAD */ );
3072 } else {
3073 return getrusage( 0 /* RUSAGE_SELF */ );
3074 }
3075}
unserialize( $serialized)
$wgLanguageCode
Site language code.
$wgDBprefix
Current wiki database table name prefix.
$wgScript
The URL path to index.php.
$wgInternalServer
Internal server name as known to CDN, if different.
$wgThumbLimits
Adjust thumbnails on image pages according to a user setting.
$wgDisableOutputCompression
Disable output compression (enabled by default if zlib is available)
$wgDebugLogPrefix
Prefix for debug log lines.
$wgPhpCli
Executable path of the PHP cli binary.
$wgOverrideHostname
Override server hostname detection with a hardcoded value.
$wgImageLimits
Limit images on image description pages to a user-selectable limit.
$wgShowHostnames
Expose backend server host names through the API and various HTML comments.
$wgTmpDirectory
The local filesystem path to a temporary directory.
$wgStyleDirectory
Filesystem stylesheets directory.
$wgTransactionalTimeLimit
The minimum amount of time that MediaWiki needs for "slow" write request, particularly ones with mult...
$wgDBname
Current wiki database name.
$wgIllegalFileChars
Additional characters that are not allowed in filenames.
$wgDirectoryMode
Default value for chmoding of new directories.
$wgMessageCacheType
The cache type for storing the contents of the MediaWiki namespace.
$wgDiff3
Path to the GNU diff3 utility.
$wgUrlProtocols
URL schemes that should be recognized as valid by wfParseUrl().
$wgResponsiveImages
Generate and use thumbnails suitable for screens with 1.5 and 2.0 pixel densities.
$wgDebugRawPage
If true, log debugging data from action=raw and load.php.
$wgEnableMagicLinks
Enable the magic links feature of automatically turning ISBN xxx, PMID xxx, RFC xxx into links.
$wgScriptPath
The path we should point to.
$wgDebugTimestamps
Prefix debug messages with relative timestamp.
$wgDebugLogGroups
Map of string log group names to log destinations.
$wgExtensionDirectory
Filesystem extensions directory.
$wgLoadScript
The URL path to load.php.
$wgCanonicalServer
Canonical URL of the server, to use in IRC feeds and notification e-mails.
$wgMiserMode
Disable database-intensive features.
$wgServer
URL of the server.
$wgHttpsPort
For installations where the canonical server is HTTP but HTTPS is optionally supported,...
$wgDiff
Path to the GNU diff utility.
global $wgCommandLineMode
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
wfThumbIsStandard(File $file, array $params)
Returns true if these thumbnail parameters match one that MediaWiki requests from file description pa...
wfConfiguredReadOnlyReason()
Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
wfVarDump( $var)
A wrapper around the PHP function var_export().
wfWaitForSlaves( $ifWritesSince=null, $wiki=false, $cluster=false, $timeout=null)
Waits for the replica DBs to catch up to the master position.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfNegotiateType( $cprefs, $sprefs)
Returns the 'best' match between a client's requested internet media types and the server's list of a...
wfRandom()
Get a random decimal value in the domain of [0, 1), in a way not likely to give duplicate values for ...
wfUrlencode( $s)
We want some things to be included as literal characters in our title URLs for prettiness,...
wfParseUrl( $url)
parse_url() work-alike, but non-broken.
wfTempDir()
Tries to get the system directory for temporary files.
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfRandomString( $length=32)
Get a random string containing a number of pseudo-random hex characters.
wfTimestampOrNull( $outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
wfBaseName( $path, $suffix='')
Return the final portion of a pathname.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfClientAcceptsGzip( $force=false)
Whether the client accept gzip encoding.
wfEscapeShellArg(... $args)
Version of escapeshellarg() that works better on Windows.
wfIncrStats( $key, $count=1)
Increment a statistics counter.
wfLogDBError( $text, array $context=[])
Log for database errors.
wfLoadSkins(array $skins)
Load multiple skins at once.
wfGetRusage()
Get system resource usage of current request context.
wfGetLB( $wiki=false)
Get a load balancer object.
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
wfRecursiveRemoveDir( $dir)
Remove a directory and all its content.
wfLoadExtension( $ext, $path=null)
Load an extension.
wfMemoryLimit( $newLimit)
Raise PHP's memory limit (if needed).
wfReadOnly()
Check whether the wiki is in read-only mode.
wfSetBit(&$dest, $bit, $state=true)
As for wfSetVar except setting a bit.
wfIniGetBool( $setting)
Safety wrapper around ini_get() for boolean settings.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfBacktrace( $raw=null)
Get a debug backtrace as a string.
wfGetCaller( $level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
wfLocalFile( $title)
Get an object referring to a locally registered file.
wfExpandIRI( $url)
Take a URL, make sure it's expanded to fully qualified, and replace any encoded non-ASCII Unicode cha...
wfMergeErrorArrays(... $args)
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
wfPercent( $nr, $acc=2, $round=true)
wfShellExec( $cmd, &$retval=null, $environ=[], $limits=[], $options=[])
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
wfGetMessageCacheStorage()
Get the cache object used by the message cache.
wfIsDebugRawPage()
Returns true if debug logging should be suppressed if $wgDebugRawPage = false.
wfHostname()
Get host name of the current machine, for use in error reporting.
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfGetMainCache()
Get the main cache object.
wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult=null)
wfMerge attempts to merge differences between three texts.
wfShellWikiCmd( $script, array $parameters=[], array $options=[])
Generate a shell-escaped command line string to run a MediaWiki cli script.
wfGetPrecompiledData( $name)
Get an object from the precompiled serialized directory.
wfFindFile( $title, $options=[])
Find a file.
wfReportTime( $nonce=null)
Returns a script tag that stores the amount of time it took MediaWiki to handle the request in millis...
wfSetVar(&$dest, $source, $force=false)
Sets dest to source and returns the original value of dest If source is NULL, it just returns the val...
wfArrayDiff2( $a, $b)
Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
wfShellExecWithStderr( $cmd, &$retval=null, $environ=[], $limits=[])
Execute a shell command, returning both stdout and stderr.
wfCanIPUseHTTPS( $ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS.
wfGetNull()
Get a platform-independent path to the null file, e.g.
wfAcceptToPrefs( $accept, $def=' */*')
Converts an Accept-* header into an array mapping string values to quality factors.
wfSetupSession( $sessionId=false)
Initialise php session.
wfDiff( $before, $after, $params='-u')
Returns unified plain-text diff of two texts.
wfRelativePath( $path, $from)
Generate a relative path name to the given file.
wfHttpError( $code, $label, $desc)
Provide a simple HTTP error.
wfUrlProtocols( $includeProtocolRelative=true)
Returns a regular expression of url protocols.
wfUnpack( $format, $data, $length=false)
Wrapper around php's unpack.
wfMessageFallback(... $keys)
This function accepts multiple message keys and returns a message instance for the first message whic...
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
wfShowingResults( $offset, $limit)
wfGetAllCallers( $limit=3)
Return a string consisting of callers in the stack.
wfRemoveDotSegments( $urlPath)
Remove all dot-segments in the provided URL path.
wfArrayPlus2d(array $baseArray, array $newValues)
Merges two (possibly) 2 dimensional arrays into the target array ($baseArray).
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfTransactionalTimeLimit()
Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit.
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfObjectToArray( $objOrArray, $recursive=true)
Recursively converts the parameter (an object) to an array with the same data.
wfGetScriptUrl()
Get the script URL.
wfGlobalCacheKey(... $args)
Make a cache key with database-agnostic prefix.
wfClearOutputBuffers()
More legible than passing a 'false' parameter to wfResetOutputBuffers():
wfDebugMem( $exact=false)
Send a line giving PHP memory usage.
wfLoadSkin( $skin, $path=null)
Load a skin.
wfMsgReplaceArgs( $message, $args)
Replace message parameter keys on the given formatted output.
wfGetServerUrl( $proto)
Get the wiki's "server", i.e.
wfStringToBool( $val)
Convert string value to boolean, when the following are interpreted as true:
wfGetCache( $cacheType)
Get a specific cache object.
wfIsBadImage( $name, $contextTitle=false, $blacklist=null)
Determine if an image exists on the 'bad image list'.
wfMemcKey(... $args)
Make a cache key for the local wiki.
wfDebugBacktrace( $limit=0)
Safety wrapper for debug_backtrace().
wfAppendQuery( $url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
wfStripIllegalFilenameChars( $name)
Replace all invalid characters with '-'.
wfFormatStackFrame( $frame)
Return a string representation of frame.
wfArrayToCgi( $array1, $array2=null, $prefix='')
This function takes one or two arrays as input, and returns a CGI-style string, e....
wfScript( $script='index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
wfCgiToArray( $query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
wfArrayDiff2_cmp( $a, $b)
wfIsWindows()
Check if the operating system is Windows.
wfShorthandToInteger( $string='', $default=-1)
Converts shorthand byte notation to integer form.
wfMatchesDomainList( $url, $domains)
Check whether a given URL has a domain that occurs in a given set of domains.
wfForeignMemcKey( $db, $prefix,... $args)
Make a cache key for a foreign DB.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfQueriesMustScale()
Should low-performance queries be disabled?
mimeTypeMatch( $type, $avail)
Checks if a given MIME type matches any of the keys in the given array.
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed)
Appends to second array if $value differs from that in $default.
wfLoadExtensions(array $exts)
Load multiple extensions at once.
wfBoolToStr( $value)
Convenience function converts boolean values into "true" or "false" (string) values.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
wfLogProfilingData()
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
wfArrayInsertAfter(array $array, array $insert, $after)
Insert array into another array after the specified KEY
wfAssembleUrl( $urlParts)
This function will reassemble a URL parsed with wfParseURL.
wfIsHHVM()
Check if we are running under HHVM.
wfResetOutputBuffers( $resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
wfIsCLI()
Check if we are running from the commandline.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
$wgOut
Definition Setup.php:885
if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:751
$wgLang
Definition Setup.php:880
$IP
Definition WebStart.php:41
$line
Definition cdb.php:59
if( $line===false) $args
Definition cdb.php:64
Class representing a 'diff' between two sequences of strings.
Definition Diff.php:32
static isStoragePath( $path)
Check if a given path is a "mwstore://" path.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:61
MediaWiki exception.
PSR-3 logger instance factory.
MediaWikiServices is the service locator for the application scope of MediaWiki.
This serves as the entry point to the MediaWiki session handling system.
Executes shell commands.
Definition Shell.php:44
The Message class provides methods which fulfil two basic services:
Definition Message.php:162
static newFallbackSequence(... $keys)
Factory function accepting multiple message keys and returning a message instance for the first messa...
Definition Message.php:460
Stub profiler that does nothing.
static edits()
Definition SiteStats.php:94
static users()
static pages()
A formatter that outputs unified diffs.
while(( $__line=Maintenance::readconsole()) !==false) print
Definition eval.php:64
const PROTO_CANONICAL
Definition Defines.php:212
const PROTO_HTTPS
Definition Defines.php:209
const PROTO_CURRENT
Definition Defines.php:211
const PROTO_INTERNAL
Definition Defines.php:213
const PROTO_HTTP
Definition Defines.php:208
$context
Definition load.php:45
$cache
Definition mcc.php:33
$source
$last
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition router.php:42
if(!is_readable( $file)) $ext
Definition router.php:48