MediaWiki REL1_30
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
27use Liuggio\StatsdClient\Sender\SocketSender;
33use Wikimedia\ScopedCallback;
35
36// Hide compatibility functions from Doxygen
38
46// hash_equals function only exists in PHP >= 5.6.0
47// https://secure.php.net/hash_equals
48if ( !function_exists( 'hash_equals' ) ) {
74 function hash_equals( $known_string, $user_string ) {
75 // Strict type checking as in PHP's native implementation
76 if ( !is_string( $known_string ) ) {
77 trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
78 gettype( $known_string ) . ' given', E_USER_WARNING );
79
80 return false;
81 }
82
83 if ( !is_string( $user_string ) ) {
84 trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
85 gettype( $user_string ) . ' given', E_USER_WARNING );
86
87 return false;
88 }
89
90 $known_string_len = strlen( $known_string );
91 if ( $known_string_len !== strlen( $user_string ) ) {
92 return false;
93 }
94
95 $result = 0;
96 for ( $i = 0; $i < $known_string_len; $i++ ) {
97 $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] );
98 }
99
100 return ( $result === 0 );
101 }
102}
104
115function wfLoadExtension( $ext, $path = null ) {
116 if ( !$path ) {
118 $path = "$wgExtensionDirectory/$ext/extension.json";
119 }
121}
122
136function wfLoadExtensions( array $exts ) {
138 $registry = ExtensionRegistry::getInstance();
139 foreach ( $exts as $ext ) {
140 $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
141 }
142}
143
152function wfLoadSkin( $skin, $path = null ) {
153 if ( !$path ) {
155 $path = "$wgStyleDirectory/$skin/skin.json";
156 }
158}
159
167function wfLoadSkins( array $skins ) {
169 $registry = ExtensionRegistry::getInstance();
170 foreach ( $skins as $skin ) {
171 $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
172 }
173}
174
181function wfArrayDiff2( $a, $b ) {
182 return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
183}
184
190function wfArrayDiff2_cmp( $a, $b ) {
191 if ( is_string( $a ) && is_string( $b ) ) {
192 return strcmp( $a, $b );
193 } elseif ( count( $a ) !== count( $b ) ) {
194 return count( $a ) < count( $b ) ? -1 : 1;
195 } else {
196 reset( $a );
197 reset( $b );
198 while ( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
199 $cmp = strcmp( $valueA, $valueB );
200 if ( $cmp !== 0 ) {
201 return $cmp;
202 }
203 }
204 return 0;
205 }
206}
207
216function wfArrayFilter( array $arr, callable $callback ) {
217 if ( defined( 'ARRAY_FILTER_USE_BOTH' ) ) {
218 return array_filter( $arr, $callback, ARRAY_FILTER_USE_BOTH );
219 }
220 $filteredKeys = array_filter( array_keys( $arr ), function ( $key ) use ( $arr, $callback ) {
221 return call_user_func( $callback, $arr[$key], $key );
222 } );
223 return array_intersect_key( $arr, array_fill_keys( $filteredKeys, true ) );
224}
225
234function wfArrayFilterByKey( array $arr, callable $callback ) {
235 return wfArrayFilter( $arr, function ( $val, $key ) use ( $callback ) {
236 return call_user_func( $callback, $key );
237 } );
238}
239
249function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
250 if ( is_null( $changed ) ) {
251 throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
252 }
253 if ( $default[$key] !== $value ) {
254 $changed[$key] = $value;
255 }
256}
257
277function wfMergeErrorArrays( /*...*/ ) {
278 $args = func_get_args();
279 $out = [];
280 foreach ( $args as $errors ) {
281 foreach ( $errors as $params ) {
282 $originalParams = $params;
283 if ( $params[0] instanceof MessageSpecifier ) {
284 $msg = $params[0];
285 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
286 }
287 # @todo FIXME: Sometimes get nested arrays for $params,
288 # which leads to E_NOTICEs
289 $spec = implode( "\t", $params );
290 $out[$spec] = $originalParams;
291 }
292 }
293 return array_values( $out );
294}
295
304function wfArrayInsertAfter( array $array, array $insert, $after ) {
305 // Find the offset of the element to insert after.
306 $keys = array_keys( $array );
307 $offsetByKey = array_flip( $keys );
308
309 $offset = $offsetByKey[$after];
310
311 // Insert at the specified offset
312 $before = array_slice( $array, 0, $offset + 1, true );
313 $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
314
315 $output = $before + $insert + $after;
316
317 return $output;
318}
319
327function wfObjectToArray( $objOrArray, $recursive = true ) {
328 $array = [];
329 if ( is_object( $objOrArray ) ) {
330 $objOrArray = get_object_vars( $objOrArray );
331 }
332 foreach ( $objOrArray as $key => $value ) {
333 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
335 }
336
337 $array[$key] = $value;
338 }
339
340 return $array;
341}
342
353function wfRandom() {
354 // The maximum random value is "only" 2^31-1, so get two random
355 // values to reduce the chance of dupes
356 $max = mt_getrandmax() + 1;
357 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
358 return $rand;
359}
360
371function wfRandomString( $length = 32 ) {
372 $str = '';
373 for ( $n = 0; $n < $length; $n += 7 ) {
374 $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
375 }
376 return substr( $str, 0, $length );
377}
378
406function wfUrlencode( $s ) {
407 static $needle;
408
409 if ( is_null( $s ) ) {
410 $needle = null;
411 return '';
412 }
413
414 if ( is_null( $needle ) ) {
415 $needle = [ '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F', '%7E' ];
416 if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
417 ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
418 ) {
419 $needle[] = '%3A';
420 }
421 }
422
423 $s = urlencode( $s );
424 $s = str_ireplace(
425 $needle,
426 [ ';', '@', '$', '!', '*', '(', ')', ',', '/', '~', ':' ],
427 $s
428 );
429
430 return $s;
431}
432
443function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) {
444 if ( !is_null( $array2 ) ) {
445 $array1 = $array1 + $array2;
446 }
447
448 $cgi = '';
449 foreach ( $array1 as $key => $value ) {
450 if ( !is_null( $value ) && $value !== false ) {
451 if ( $cgi != '' ) {
452 $cgi .= '&';
453 }
454 if ( $prefix !== '' ) {
455 $key = $prefix . "[$key]";
456 }
457 if ( is_array( $value ) ) {
458 $firstTime = true;
459 foreach ( $value as $k => $v ) {
460 $cgi .= $firstTime ? '' : '&';
461 if ( is_array( $v ) ) {
462 $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" );
463 } else {
464 $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
465 }
466 $firstTime = false;
467 }
468 } else {
469 if ( is_object( $value ) ) {
470 $value = $value->__toString();
471 }
472 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
473 }
474 }
475 }
476 return $cgi;
477}
478
488function wfCgiToArray( $query ) {
489 if ( isset( $query[0] ) && $query[0] == '?' ) {
490 $query = substr( $query, 1 );
491 }
492 $bits = explode( '&', $query );
493 $ret = [];
494 foreach ( $bits as $bit ) {
495 if ( $bit === '' ) {
496 continue;
497 }
498 if ( strpos( $bit, '=' ) === false ) {
499 // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
500 $key = $bit;
501 $value = '';
502 } else {
503 list( $key, $value ) = explode( '=', $bit );
504 }
505 $key = urldecode( $key );
506 $value = urldecode( $value );
507 if ( strpos( $key, '[' ) !== false ) {
508 $keys = array_reverse( explode( '[', $key ) );
509 $key = array_pop( $keys );
510 $temp = $value;
511 foreach ( $keys as $k ) {
512 $k = substr( $k, 0, -1 );
513 $temp = [ $k => $temp ];
514 }
515 if ( isset( $ret[$key] ) ) {
516 $ret[$key] = array_merge( $ret[$key], $temp );
517 } else {
518 $ret[$key] = $temp;
519 }
520 } else {
521 $ret[$key] = $value;
522 }
523 }
524 return $ret;
525}
526
535function wfAppendQuery( $url, $query ) {
536 if ( is_array( $query ) ) {
538 }
539 if ( $query != '' ) {
540 // Remove the fragment, if there is one
541 $fragment = false;
542 $hashPos = strpos( $url, '#' );
543 if ( $hashPos !== false ) {
544 $fragment = substr( $url, $hashPos );
545 $url = substr( $url, 0, $hashPos );
546 }
547
548 // Add parameter
549 if ( false === strpos( $url, '?' ) ) {
550 $url .= '?';
551 } else {
552 $url .= '&';
553 }
554 $url .= $query;
555
556 // Put the fragment back
557 if ( $fragment !== false ) {
558 $url .= $fragment;
559 }
560 }
561 return $url;
562}
563
587function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
590 if ( $defaultProto === PROTO_CANONICAL ) {
591 $serverUrl = $wgCanonicalServer;
592 } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
593 // Make $wgInternalServer fall back to $wgServer if not set
594 $serverUrl = $wgInternalServer;
595 } else {
596 $serverUrl = $wgServer;
597 if ( $defaultProto === PROTO_CURRENT ) {
598 $defaultProto = $wgRequest->getProtocol() . '://';
599 }
600 }
601
602 // Analyze $serverUrl to obtain its protocol
603 $bits = wfParseUrl( $serverUrl );
604 $serverHasProto = $bits && $bits['scheme'] != '';
605
606 if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
607 if ( $serverHasProto ) {
608 $defaultProto = $bits['scheme'] . '://';
609 } else {
610 // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
611 // This really isn't supposed to happen. Fall back to HTTP in this
612 // ridiculous case.
613 $defaultProto = PROTO_HTTP;
614 }
615 }
616
617 $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
618
619 if ( substr( $url, 0, 2 ) == '//' ) {
620 $url = $defaultProtoWithoutSlashes . $url;
621 } elseif ( substr( $url, 0, 1 ) == '/' ) {
622 // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
623 // otherwise leave it alone.
624 $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
625 }
626
627 $bits = wfParseUrl( $url );
628
629 // ensure proper port for HTTPS arrives in URL
630 // https://phabricator.wikimedia.org/T67184
631 if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
632 $bits['port'] = $wgHttpsPort;
633 }
634
635 if ( $bits && isset( $bits['path'] ) ) {
636 $bits['path'] = wfRemoveDotSegments( $bits['path'] );
637 return wfAssembleUrl( $bits );
638 } elseif ( $bits ) {
639 # No path to expand
640 return $url;
641 } elseif ( substr( $url, 0, 1 ) != '/' ) {
642 # URL is a relative path
643 return wfRemoveDotSegments( $url );
644 }
645
646 # Expanded URL is not valid.
647 return false;
648}
649
663function wfAssembleUrl( $urlParts ) {
664 $result = '';
665
666 if ( isset( $urlParts['delimiter'] ) ) {
667 if ( isset( $urlParts['scheme'] ) ) {
668 $result .= $urlParts['scheme'];
669 }
670
671 $result .= $urlParts['delimiter'];
672 }
673
674 if ( isset( $urlParts['host'] ) ) {
675 if ( isset( $urlParts['user'] ) ) {
676 $result .= $urlParts['user'];
677 if ( isset( $urlParts['pass'] ) ) {
678 $result .= ':' . $urlParts['pass'];
679 }
680 $result .= '@';
681 }
682
683 $result .= $urlParts['host'];
684
685 if ( isset( $urlParts['port'] ) ) {
686 $result .= ':' . $urlParts['port'];
687 }
688 }
689
690 if ( isset( $urlParts['path'] ) ) {
691 $result .= $urlParts['path'];
692 }
693
694 if ( isset( $urlParts['query'] ) ) {
695 $result .= '?' . $urlParts['query'];
696 }
697
698 if ( isset( $urlParts['fragment'] ) ) {
699 $result .= '#' . $urlParts['fragment'];
700 }
701
702 return $result;
703}
704
715function wfRemoveDotSegments( $urlPath ) {
716 $output = '';
717 $inputOffset = 0;
718 $inputLength = strlen( $urlPath );
719
720 while ( $inputOffset < $inputLength ) {
721 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
722 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
723 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
724 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
725 $trimOutput = false;
726
727 if ( $prefixLengthTwo == './' ) {
728 # Step A, remove leading "./"
729 $inputOffset += 2;
730 } elseif ( $prefixLengthThree == '../' ) {
731 # Step A, remove leading "../"
732 $inputOffset += 3;
733 } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
734 # Step B, replace leading "/.$" with "/"
735 $inputOffset += 1;
736 $urlPath[$inputOffset] = '/';
737 } elseif ( $prefixLengthThree == '/./' ) {
738 # Step B, replace leading "/./" with "/"
739 $inputOffset += 2;
740 } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
741 # Step C, replace leading "/..$" with "/" and
742 # remove last path component in output
743 $inputOffset += 2;
744 $urlPath[$inputOffset] = '/';
745 $trimOutput = true;
746 } elseif ( $prefixLengthFour == '/../' ) {
747 # Step C, replace leading "/../" with "/" and
748 # remove last path component in output
749 $inputOffset += 3;
750 $trimOutput = true;
751 } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
752 # Step D, remove "^.$"
753 $inputOffset += 1;
754 } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
755 # Step D, remove "^..$"
756 $inputOffset += 2;
757 } else {
758 # Step E, move leading path segment to output
759 if ( $prefixLengthOne == '/' ) {
760 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
761 } else {
762 $slashPos = strpos( $urlPath, '/', $inputOffset );
763 }
764 if ( $slashPos === false ) {
765 $output .= substr( $urlPath, $inputOffset );
766 $inputOffset = $inputLength;
767 } else {
768 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
769 $inputOffset += $slashPos - $inputOffset;
770 }
771 }
772
773 if ( $trimOutput ) {
774 $slashPos = strrpos( $output, '/' );
775 if ( $slashPos === false ) {
776 $output = '';
777 } else {
778 $output = substr( $output, 0, $slashPos );
779 }
780 }
781 }
782
783 return $output;
784}
785
793function wfUrlProtocols( $includeProtocolRelative = true ) {
795
796 // Cache return values separately based on $includeProtocolRelative
797 static $withProtRel = null, $withoutProtRel = null;
798 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
799 if ( !is_null( $cachedValue ) ) {
800 return $cachedValue;
801 }
802
803 // Support old-style $wgUrlProtocols strings, for backwards compatibility
804 // with LocalSettings files from 1.5
805 if ( is_array( $wgUrlProtocols ) ) {
806 $protocols = [];
807 foreach ( $wgUrlProtocols as $protocol ) {
808 // Filter out '//' if !$includeProtocolRelative
809 if ( $includeProtocolRelative || $protocol !== '//' ) {
810 $protocols[] = preg_quote( $protocol, '/' );
811 }
812 }
813
814 $retval = implode( '|', $protocols );
815 } else {
816 // Ignore $includeProtocolRelative in this case
817 // This case exists for pre-1.6 compatibility, and we can safely assume
818 // that '//' won't appear in a pre-1.6 config because protocol-relative
819 // URLs weren't supported until 1.18
821 }
822
823 // Cache return value
824 if ( $includeProtocolRelative ) {
825 $withProtRel = $retval;
826 } else {
827 $withoutProtRel = $retval;
828 }
829 return $retval;
830}
831
839 return wfUrlProtocols( false );
840}
841
867function wfParseUrl( $url ) {
868 global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
869
870 // Protocol-relative URLs are handled really badly by parse_url(). It's so
871 // bad that the easiest way to handle them is to just prepend 'http:' and
872 // strip the protocol out later.
873 $wasRelative = substr( $url, 0, 2 ) == '//';
874 if ( $wasRelative ) {
875 $url = "http:$url";
876 }
877 MediaWiki\suppressWarnings();
878 $bits = parse_url( $url );
879 MediaWiki\restoreWarnings();
880 // parse_url() returns an array without scheme for some invalid URLs, e.g.
881 // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
882 if ( !$bits || !isset( $bits['scheme'] ) ) {
883 return false;
884 }
885
886 // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
887 $bits['scheme'] = strtolower( $bits['scheme'] );
888
889 // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
890 if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
891 $bits['delimiter'] = '://';
892 } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
893 $bits['delimiter'] = ':';
894 // parse_url detects for news: and mailto: the host part of an url as path
895 // We have to correct this wrong detection
896 if ( isset( $bits['path'] ) ) {
897 $bits['host'] = $bits['path'];
898 $bits['path'] = '';
899 }
900 } else {
901 return false;
902 }
903
904 /* Provide an empty host for eg. file:/// urls (see T30627) */
905 if ( !isset( $bits['host'] ) ) {
906 $bits['host'] = '';
907
908 // See T47069
909 if ( isset( $bits['path'] ) ) {
910 /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
911 if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
912 $bits['path'] = '/' . $bits['path'];
913 }
914 } else {
915 $bits['path'] = '';
916 }
917 }
918
919 // If the URL was protocol-relative, fix scheme and delimiter
920 if ( $wasRelative ) {
921 $bits['scheme'] = '';
922 $bits['delimiter'] = '//';
923 }
924 return $bits;
925}
926
937function wfExpandIRI( $url ) {
938 return preg_replace_callback(
939 '/((?:%[89A-F][0-9A-F])+)/i',
940 'wfExpandIRI_callback',
941 wfExpandUrl( $url )
942 );
943}
944
951 return urldecode( $matches[1] );
952}
953
960function wfMakeUrlIndexes( $url ) {
961 $bits = wfParseUrl( $url );
962
963 // Reverse the labels in the hostname, convert to lower case
964 // For emails reverse domainpart only
965 if ( $bits['scheme'] == 'mailto' ) {
966 $mailparts = explode( '@', $bits['host'], 2 );
967 if ( count( $mailparts ) === 2 ) {
968 $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
969 } else {
970 // No domain specified, don't mangle it
971 $domainpart = '';
972 }
973 $reversedHost = $domainpart . '@' . $mailparts[0];
974 } else {
975 $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
976 }
977 // Add an extra dot to the end
978 // Why? Is it in wrong place in mailto links?
979 if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
980 $reversedHost .= '.';
981 }
982 // Reconstruct the pseudo-URL
983 $prot = $bits['scheme'];
984 $index = $prot . $bits['delimiter'] . $reversedHost;
985 // Leave out user and password. Add the port, path, query and fragment
986 if ( isset( $bits['port'] ) ) {
987 $index .= ':' . $bits['port'];
988 }
989 if ( isset( $bits['path'] ) ) {
990 $index .= $bits['path'];
991 } else {
992 $index .= '/';
993 }
994 if ( isset( $bits['query'] ) ) {
995 $index .= '?' . $bits['query'];
996 }
997 if ( isset( $bits['fragment'] ) ) {
998 $index .= '#' . $bits['fragment'];
999 }
1000
1001 if ( $prot == '' ) {
1002 return [ "http:$index", "https:$index" ];
1003 } else {
1004 return [ $index ];
1005 }
1006}
1007
1014function wfMatchesDomainList( $url, $domains ) {
1015 $bits = wfParseUrl( $url );
1016 if ( is_array( $bits ) && isset( $bits['host'] ) ) {
1017 $host = '.' . $bits['host'];
1018 foreach ( (array)$domains as $domain ) {
1019 $domain = '.' . $domain;
1020 if ( substr( $host, -strlen( $domain ) ) === $domain ) {
1021 return true;
1022 }
1023 }
1024 }
1025 return false;
1026}
1027
1048function wfDebug( $text, $dest = 'all', array $context = [] ) {
1051
1052 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1053 return;
1054 }
1055
1056 $text = trim( $text );
1057
1058 if ( $wgDebugTimestamps ) {
1059 $context['seconds_elapsed'] = sprintf(
1060 '%6.4f',
1061 microtime( true ) - $wgRequestTime
1062 );
1063 $context['memory_used'] = sprintf(
1064 '%5.1fM',
1065 ( memory_get_usage( true ) / ( 1024 * 1024 ) )
1066 );
1067 }
1068
1069 if ( $wgDebugLogPrefix !== '' ) {
1070 $context['prefix'] = $wgDebugLogPrefix;
1071 }
1072 $context['private'] = ( $dest === false || $dest === 'private' );
1073
1074 $logger = LoggerFactory::getInstance( 'wfDebug' );
1075 $logger->debug( $text, $context );
1076}
1077
1083 static $cache;
1084 if ( $cache !== null ) {
1085 return $cache;
1086 }
1087 # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
1088 if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
1089 || (
1090 isset( $_SERVER['SCRIPT_NAME'] )
1091 && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
1092 )
1093 ) {
1094 $cache = true;
1095 } else {
1096 $cache = false;
1097 }
1098 return $cache;
1099}
1100
1106function wfDebugMem( $exact = false ) {
1107 $mem = memory_get_usage();
1108 if ( !$exact ) {
1109 $mem = floor( $mem / 1024 ) . ' KiB';
1110 } else {
1111 $mem .= ' B';
1112 }
1113 wfDebug( "Memory usage: $mem\n" );
1114}
1115
1141function wfDebugLog(
1142 $logGroup, $text, $dest = 'all', array $context = []
1143) {
1144 $text = trim( $text );
1145
1146 $logger = LoggerFactory::getInstance( $logGroup );
1147 $context['private'] = ( $dest === false || $dest === 'private' );
1148 $logger->info( $text, $context );
1149}
1150
1159function wfLogDBError( $text, array $context = [] ) {
1160 $logger = LoggerFactory::getInstance( 'wfLogDBError' );
1161 $logger->error( trim( $text ), $context );
1162}
1163
1177function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
1178 MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
1179}
1180
1191function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1192 MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
1193}
1194
1204function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1205 MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
1206}
1207
1221function wfErrorLog( $text, $file, array $context = [] ) {
1222 wfDeprecated( __METHOD__, '1.25' );
1223 $logger = LoggerFactory::getInstance( 'wfErrorLog' );
1224 $context['destination'] = $file;
1225 $logger->info( trim( $text ), $context );
1226}
1227
1233
1235 $request = $context->getRequest();
1236
1237 $profiler = Profiler::instance();
1238 $profiler->setContext( $context );
1239 $profiler->logData();
1240
1241 $config = $context->getConfig();
1242 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1243 if ( $config->get( 'StatsdServer' ) && $stats->hasData() ) {
1244 try {
1245 $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
1246 $statsdHost = $statsdServer[0];
1247 $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
1248 $statsdSender = new SocketSender( $statsdHost, $statsdPort );
1249 $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
1250 $statsdClient->setSamplingRates( $config->get( 'StatsdSamplingRates' ) );
1251 $statsdClient->send( $stats->getData() );
1252 } catch ( Exception $ex ) {
1253 MWExceptionHandler::logException( $ex );
1254 }
1255 }
1256
1257 # Profiling must actually be enabled...
1258 if ( $profiler instanceof ProfilerStub ) {
1259 return;
1260 }
1261
1262 if ( isset( $wgDebugLogGroups['profileoutput'] )
1263 && $wgDebugLogGroups['profileoutput'] === false
1264 ) {
1265 // Explicitly disabled
1266 return;
1267 }
1268 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1269 return;
1270 }
1271
1272 $ctx = [ 'elapsed' => $request->getElapsedTime() ];
1273 if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1274 $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
1275 }
1276 if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
1277 $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
1278 }
1279 if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
1280 $ctx['from'] = $_SERVER['HTTP_FROM'];
1281 }
1282 if ( isset( $ctx['forwarded_for'] ) ||
1283 isset( $ctx['client_ip'] ) ||
1284 isset( $ctx['from'] ) ) {
1285 $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
1286 }
1287
1288 // Don't load $wgUser at this late stage just for statistics purposes
1289 // @todo FIXME: We can detect some anons even if it is not loaded.
1290 // See User::getId()
1291 $user = $context->getUser();
1292 $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
1293
1294 // Command line script uses a FauxRequest object which does not have
1295 // any knowledge about an URL and throw an exception instead.
1296 try {
1297 $ctx['url'] = urldecode( $request->getRequestURL() );
1298 } catch ( Exception $ignored ) {
1299 // no-op
1300 }
1301
1302 $ctx['output'] = $profiler->getOutput();
1303
1304 $log = LoggerFactory::getInstance( 'profileoutput' );
1305 $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1306}
1307
1315function wfIncrStats( $key, $count = 1 ) {
1316 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1317 $stats->updateCount( $key, $count );
1318}
1319
1325function wfReadOnly() {
1326 return MediaWikiServices::getInstance()->getReadOnlyMode()
1327 ->isReadOnly();
1328}
1329
1339 return MediaWikiServices::getInstance()->getReadOnlyMode()
1340 ->getReason();
1341}
1342
1350 return MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1351 ->getReason();
1352}
1353
1369function wfGetLangObj( $langcode = false ) {
1370 # Identify which language to get or create a language object for.
1371 # Using is_object here due to Stub objects.
1372 if ( is_object( $langcode ) ) {
1373 # Great, we already have the object (hopefully)!
1374 return $langcode;
1375 }
1376
1378 if ( $langcode === true || $langcode === $wgLanguageCode ) {
1379 # $langcode is the language code of the wikis content language object.
1380 # or it is a boolean and value is true
1381 return $wgContLang;
1382 }
1383
1385 if ( $langcode === false || $langcode === $wgLang->getCode() ) {
1386 # $langcode is the language code of user language object.
1387 # or it was a boolean and value is false
1388 return $wgLang;
1389 }
1390
1391 $validCodes = array_keys( Language::fetchLanguageNames() );
1392 if ( in_array( $langcode, $validCodes ) ) {
1393 # $langcode corresponds to a valid language.
1394 return Language::factory( $langcode );
1395 }
1396
1397 # $langcode is a string, but not a valid language code; use content language.
1398 wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1399 return $wgContLang;
1400}
1401
1418function wfMessage( $key /*...*/ ) {
1419 $message = new Message( $key );
1420
1421 // We call Message::params() to reduce code duplication
1422 $params = func_get_args();
1423 array_shift( $params );
1424 if ( $params ) {
1425 call_user_func_array( [ $message, 'params' ], $params );
1426 }
1427
1428 return $message;
1429}
1430
1443function wfMessageFallback( /*...*/ ) {
1444 $args = func_get_args();
1445 return call_user_func_array( 'Message::newFallbackSequence', $args );
1446}
1447
1456function wfMsgReplaceArgs( $message, $args ) {
1457 # Fix windows line-endings
1458 # Some messages are split with explode("\n", $msg)
1459 $message = str_replace( "\r", '', $message );
1460
1461 // Replace arguments
1462 if ( is_array( $args ) && $args ) {
1463 if ( is_array( $args[0] ) ) {
1464 $args = array_values( $args[0] );
1465 }
1466 $replacementKeys = [];
1467 foreach ( $args as $n => $param ) {
1468 $replacementKeys['$' . ( $n + 1 )] = $param;
1469 }
1470 $message = strtr( $message, $replacementKeys );
1471 }
1472
1473 return $message;
1474}
1475
1483function wfHostname() {
1484 static $host;
1485 if ( is_null( $host ) ) {
1486 # Hostname overriding
1488 if ( $wgOverrideHostname !== false ) {
1489 # Set static and skip any detection
1490 $host = $wgOverrideHostname;
1491 return $host;
1492 }
1493
1494 if ( function_exists( 'posix_uname' ) ) {
1495 // This function not present on Windows
1496 $uname = posix_uname();
1497 } else {
1498 $uname = false;
1499 }
1500 if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
1501 $host = $uname['nodename'];
1502 } elseif ( getenv( 'COMPUTERNAME' ) ) {
1503 # Windows computer name
1504 $host = getenv( 'COMPUTERNAME' );
1505 } else {
1506 # This may be a virtual server.
1507 $host = $_SERVER['SERVER_NAME'];
1508 }
1509 }
1510 return $host;
1511}
1512
1522function wfReportTime() {
1524
1525 $responseTime = round( ( microtime( true ) - $wgRequestTime ) * 1000 );
1526 $reportVars = [ 'wgBackendResponseTime' => $responseTime ];
1527 if ( $wgShowHostnames ) {
1528 $reportVars['wgHostname'] = wfHostname();
1529 }
1530 return Skin::makeVariablesScript( $reportVars );
1531}
1532
1543function wfDebugBacktrace( $limit = 0 ) {
1544 static $disabled = null;
1545
1546 if ( is_null( $disabled ) ) {
1547 $disabled = !function_exists( 'debug_backtrace' );
1548 if ( $disabled ) {
1549 wfDebug( "debug_backtrace() is disabled\n" );
1550 }
1551 }
1552 if ( $disabled ) {
1553 return [];
1554 }
1555
1556 if ( $limit ) {
1557 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1558 } else {
1559 return array_slice( debug_backtrace(), 1 );
1560 }
1561}
1562
1571function wfBacktrace( $raw = null ) {
1573
1574 if ( $raw === null ) {
1575 $raw = $wgCommandLineMode;
1576 }
1577
1578 if ( $raw ) {
1579 $frameFormat = "%s line %s calls %s()\n";
1580 $traceFormat = "%s";
1581 } else {
1582 $frameFormat = "<li>%s line %s calls %s()</li>\n";
1583 $traceFormat = "<ul>\n%s</ul>\n";
1584 }
1585
1586 $frames = array_map( function ( $frame ) use ( $frameFormat ) {
1587 $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
1588 $line = isset( $frame['line'] ) ? $frame['line'] : '-';
1589 $call = $frame['function'];
1590 if ( !empty( $frame['class'] ) ) {
1591 $call = $frame['class'] . $frame['type'] . $call;
1592 }
1593 return sprintf( $frameFormat, $file, $line, $call );
1594 }, wfDebugBacktrace() );
1595
1596 return sprintf( $traceFormat, implode( '', $frames ) );
1597}
1598
1608function wfGetCaller( $level = 2 ) {
1609 $backtrace = wfDebugBacktrace( $level + 1 );
1610 if ( isset( $backtrace[$level] ) ) {
1611 return wfFormatStackFrame( $backtrace[$level] );
1612 } else {
1613 return 'unknown';
1614 }
1615}
1616
1624function wfGetAllCallers( $limit = 3 ) {
1625 $trace = array_reverse( wfDebugBacktrace() );
1626 if ( !$limit || $limit > count( $trace ) - 1 ) {
1627 $limit = count( $trace ) - 1;
1628 }
1629 $trace = array_slice( $trace, -$limit - 1, $limit );
1630 return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
1631}
1632
1639function wfFormatStackFrame( $frame ) {
1640 if ( !isset( $frame['function'] ) ) {
1641 return 'NO_FUNCTION_GIVEN';
1642 }
1643 return isset( $frame['class'] ) && isset( $frame['type'] ) ?
1644 $frame['class'] . $frame['type'] . $frame['function'] :
1645 $frame['function'];
1646}
1647
1648/* Some generic result counters, pulled out of SearchEngine */
1649
1657function wfShowingResults( $offset, $limit ) {
1658 return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1659}
1660
1670function wfClientAcceptsGzip( $force = false ) {
1671 static $result = null;
1672 if ( $result === null || $force ) {
1673 $result = false;
1674 if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
1675 # @todo FIXME: We may want to blacklist some broken browsers
1676 $m = [];
1677 if ( preg_match(
1678 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1679 $_SERVER['HTTP_ACCEPT_ENCODING'],
1680 $m
1681 )
1682 ) {
1683 if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
1684 $result = false;
1685 return $result;
1686 }
1687 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
1688 $result = true;
1689 }
1690 }
1691 }
1692 return $result;
1693}
1694
1704function wfEscapeWikiText( $text ) {
1706 static $repl = null, $repl2 = null;
1707 if ( $repl === null || defined( 'MW_PARSER_TEST' ) || defined( 'MW_PHPUNIT_TEST' ) ) {
1708 // Tests depend upon being able to change $wgEnableMagicLinks, so don't cache
1709 // in those situations
1710 $repl = [
1711 '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
1712 '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
1713 '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
1714 "\n#" => "\n&#35;", "\r#" => "\r&#35;",
1715 "\n*" => "\n&#42;", "\r*" => "\r&#42;",
1716 "\n:" => "\n&#58;", "\r:" => "\r&#58;",
1717 "\n " => "\n&#32;", "\r " => "\r&#32;",
1718 "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
1719 "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
1720 "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
1721 "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
1722 '__' => '_&#95;', '://' => '&#58;//',
1723 ];
1724
1725 $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
1726 // We have to catch everything "\s" matches in PCRE
1727 foreach ( $magicLinks as $magic ) {
1728 $repl["$magic "] = "$magic&#32;";
1729 $repl["$magic\t"] = "$magic&#9;";
1730 $repl["$magic\r"] = "$magic&#13;";
1731 $repl["$magic\n"] = "$magic&#10;";
1732 $repl["$magic\f"] = "$magic&#12;";
1733 }
1734
1735 // And handle protocols that don't use "://"
1737 $repl2 = [];
1738 foreach ( $wgUrlProtocols as $prot ) {
1739 if ( substr( $prot, -1 ) === ':' ) {
1740 $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
1741 }
1742 }
1743 $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/';
1744 }
1745 $text = substr( strtr( "\n$text", $repl ), 1 );
1746 $text = preg_replace( $repl2, '$1&#58;', $text );
1747 return $text;
1748}
1749
1760function wfSetVar( &$dest, $source, $force = false ) {
1761 $temp = $dest;
1762 if ( !is_null( $source ) || $force ) {
1763 $dest = $source;
1764 }
1765 return $temp;
1766}
1767
1777function wfSetBit( &$dest, $bit, $state = true ) {
1778 $temp = (bool)( $dest & $bit );
1779 if ( !is_null( $state ) ) {
1780 if ( $state ) {
1781 $dest |= $bit;
1782 } else {
1783 $dest &= ~$bit;
1784 }
1785 }
1786 return $temp;
1787}
1788
1795function wfVarDump( $var ) {
1796 global $wgOut;
1797 $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
1798 if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
1799 print $s;
1800 } else {
1801 $wgOut->addHTML( $s );
1802 }
1803}
1804
1812function wfHttpError( $code, $label, $desc ) {
1813 global $wgOut;
1815 if ( $wgOut ) {
1816 $wgOut->disable();
1817 $wgOut->sendCacheControl();
1818 }
1819
1820 MediaWiki\HeaderCallback::warnIfHeadersSent();
1821 header( 'Content-type: text/html; charset=utf-8' );
1822 print '<!DOCTYPE html>' .
1823 '<html><head><title>' .
1824 htmlspecialchars( $label ) .
1825 '</title></head><body><h1>' .
1826 htmlspecialchars( $label ) .
1827 '</h1><p>' .
1828 nl2br( htmlspecialchars( $desc ) ) .
1829 "</p></body></html>\n";
1830}
1831
1849function wfResetOutputBuffers( $resetGzipEncoding = true ) {
1850 if ( $resetGzipEncoding ) {
1851 // Suppress Content-Encoding and Content-Length
1852 // headers from 1.10+s wfOutputHandler
1855 }
1856 while ( $status = ob_get_status() ) {
1857 if ( isset( $status['flags'] ) ) {
1858 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1859 $deleteable = ( $status['flags'] & $flags ) === $flags;
1860 } elseif ( isset( $status['del'] ) ) {
1861 $deleteable = $status['del'];
1862 } else {
1863 // Guess that any PHP-internal setting can't be removed.
1864 $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
1865 }
1866 if ( !$deleteable ) {
1867 // Give up, and hope the result doesn't break
1868 // output behavior.
1869 break;
1870 }
1871 if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1872 // Unit testing barrier to prevent this function from breaking PHPUnit.
1873 break;
1874 }
1875 if ( !ob_end_clean() ) {
1876 // Could not remove output buffer handler; abort now
1877 // to avoid getting in some kind of infinite loop.
1878 break;
1879 }
1880 if ( $resetGzipEncoding ) {
1881 if ( $status['name'] == 'ob_gzhandler' ) {
1882 // Reset the 'Content-Encoding' field set by this handler
1883 // so we can start fresh.
1884 header_remove( 'Content-Encoding' );
1885 break;
1886 }
1887 }
1888 }
1889}
1890
1904 wfResetOutputBuffers( false );
1905}
1906
1915function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1916 # No arg means accept anything (per HTTP spec)
1917 if ( !$accept ) {
1918 return [ $def => 1.0 ];
1919 }
1920
1921 $prefs = [];
1922
1923 $parts = explode( ',', $accept );
1924
1925 foreach ( $parts as $part ) {
1926 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1927 $values = explode( ';', trim( $part ) );
1928 $match = [];
1929 if ( count( $values ) == 1 ) {
1930 $prefs[$values[0]] = 1.0;
1931 } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1932 $prefs[$values[0]] = floatval( $match[1] );
1933 }
1934 }
1935
1936 return $prefs;
1937}
1938
1951function mimeTypeMatch( $type, $avail ) {
1952 if ( array_key_exists( $type, $avail ) ) {
1953 return $type;
1954 } else {
1955 $mainType = explode( '/', $type )[0];
1956 if ( array_key_exists( "$mainType/*", $avail ) ) {
1957 return "$mainType/*";
1958 } elseif ( array_key_exists( '*/*', $avail ) ) {
1959 return '*/*';
1960 } else {
1961 return null;
1962 }
1963 }
1964}
1965
1979function wfNegotiateType( $cprefs, $sprefs ) {
1980 $combine = [];
1981
1982 foreach ( array_keys( $sprefs ) as $type ) {
1983 $subType = explode( '/', $type )[1];
1984 if ( $subType != '*' ) {
1985 $ckey = mimeTypeMatch( $type, $cprefs );
1986 if ( $ckey ) {
1987 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1988 }
1989 }
1990 }
1991
1992 foreach ( array_keys( $cprefs ) as $type ) {
1993 $subType = explode( '/', $type )[1];
1994 if ( $subType != '*' && !array_key_exists( $type, $sprefs ) ) {
1995 $skey = mimeTypeMatch( $type, $sprefs );
1996 if ( $skey ) {
1997 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1998 }
1999 }
2000 }
2001
2002 $bestq = 0;
2003 $besttype = null;
2004
2005 foreach ( array_keys( $combine ) as $type ) {
2006 if ( $combine[$type] > $bestq ) {
2007 $besttype = $type;
2008 $bestq = $combine[$type];
2009 }
2010 }
2011
2012 return $besttype;
2013}
2014
2021function wfSuppressWarnings( $end = false ) {
2022 MediaWiki\suppressWarnings( $end );
2023}
2024
2030 MediaWiki\suppressWarnings( true );
2031}
2032
2041function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
2042 $ret = MWTimestamp::convert( $outputtype, $ts );
2043 if ( $ret === false ) {
2044 wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
2045 }
2046 return $ret;
2047}
2048
2057function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
2058 if ( is_null( $ts ) ) {
2059 return null;
2060 } else {
2061 return wfTimestamp( $outputtype, $ts );
2062 }
2063}
2064
2070function wfTimestampNow() {
2071 # return NOW
2072 return MWTimestamp::now( TS_MW );
2073}
2074
2080function wfIsWindows() {
2081 static $isWindows = null;
2082 if ( $isWindows === null ) {
2083 $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
2084 }
2085 return $isWindows;
2086}
2087
2093function wfIsHHVM() {
2094 return defined( 'HHVM_VERSION' );
2095}
2096
2108function wfTempDir() {
2110
2111 if ( $wgTmpDirectory !== false ) {
2112 return $wgTmpDirectory;
2113 }
2114
2116}
2117
2127function wfMkdirParents( $dir, $mode = null, $caller = null ) {
2129
2130 if ( FileBackend::isStoragePath( $dir ) ) { // sanity
2131 throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
2132 }
2133
2134 if ( !is_null( $caller ) ) {
2135 wfDebug( "$caller: called wfMkdirParents($dir)\n" );
2136 }
2137
2138 if ( strval( $dir ) === '' || is_dir( $dir ) ) {
2139 return true;
2140 }
2141
2142 $dir = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $dir );
2143
2144 if ( is_null( $mode ) ) {
2145 $mode = $wgDirectoryMode;
2146 }
2147
2148 // Turn off the normal warning, we're doing our own below
2149 MediaWiki\suppressWarnings();
2150 $ok = mkdir( $dir, $mode, true ); // PHP5 <3
2151 MediaWiki\restoreWarnings();
2152
2153 if ( !$ok ) {
2154 // directory may have been created on another request since we last checked
2155 if ( is_dir( $dir ) ) {
2156 return true;
2157 }
2158
2159 // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
2160 wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
2161 }
2162 return $ok;
2163}
2164
2170function wfRecursiveRemoveDir( $dir ) {
2171 wfDebug( __FUNCTION__ . "( $dir )\n" );
2172 // taken from https://secure.php.net/manual/en/function.rmdir.php#98622
2173 if ( is_dir( $dir ) ) {
2174 $objects = scandir( $dir );
2175 foreach ( $objects as $object ) {
2176 if ( $object != "." && $object != ".." ) {
2177 if ( filetype( $dir . '/' . $object ) == "dir" ) {
2178 wfRecursiveRemoveDir( $dir . '/' . $object );
2179 } else {
2180 unlink( $dir . '/' . $object );
2181 }
2182 }
2183 }
2184 reset( $objects );
2185 rmdir( $dir );
2186 }
2187}
2188
2195function wfPercent( $nr, $acc = 2, $round = true ) {
2196 $ret = sprintf( "%.${acc}f", $nr );
2197 return $round ? round( $ret, $acc ) . '%' : "$ret%";
2198}
2199
2223function wfIniGetBool( $setting ) {
2224 $val = strtolower( ini_get( $setting ) );
2225 // 'on' and 'true' can't have whitespace around them, but '1' can.
2226 return $val == 'on'
2227 || $val == 'true'
2228 || $val == 'yes'
2229 || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
2230}
2231
2244function wfEscapeShellArg( /*...*/ ) {
2245 $args = func_get_args();
2246
2247 return call_user_func_array( Shell::class . '::escape', $args );
2248}
2249
2258 return Shell::isDisabled() ? 'disabled' : false;
2259}
2260
2284function wfShellExec( $cmd, &$retval = null, $environ = [],
2285 $limits = [], $options = []
2286) {
2287 if ( Shell::isDisabled() ) {
2288 $retval = 1;
2289 // Backwards compatibility be upon us...
2290 return 'Unable to run external programs, proc_open() is disabled.';
2291 }
2292
2293 if ( is_array( $cmd ) ) {
2294 $cmd = Shell::escape( $cmd );
2295 }
2296
2297 $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
2298 $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
2299
2300 try {
2301 $result = Shell::command( [] )
2302 ->unsafeParams( (array)$cmd )
2303 ->environment( $environ )
2304 ->limits( $limits )
2305 ->includeStderr( $includeStderr )
2306 ->profileMethod( $profileMethod )
2307 ->execute();
2308 } catch ( ProcOpenError $ex ) {
2309 $retval = -1;
2310 return '';
2311 }
2312
2313 $retval = $result->getExitCode();
2314
2315 return $result->getStdout();
2316}
2317
2335function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
2336 return wfShellExec( $cmd, $retval, $environ, $limits,
2337 [ 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ] );
2338}
2339
2349}
2350
2363function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
2365 // Give site config file a chance to run the script in a wrapper.
2366 // The caller may likely want to call wfBasename() on $script.
2367 Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2368 $cmd = isset( $options['php'] ) ? [ $options['php'] ] : [ $wgPhpCli ];
2369 if ( isset( $options['wrapper'] ) ) {
2370 $cmd[] = $options['wrapper'];
2371 }
2372 $cmd[] = $script;
2373 // Escape each parameter for shell
2374 return Shell::escape( array_merge( $cmd, $parameters ) );
2375}
2376
2387function wfMerge( $old, $mine, $yours, &$result ) {
2389
2390 # This check may also protect against code injection in
2391 # case of broken installations.
2392 MediaWiki\suppressWarnings();
2393 $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2394 MediaWiki\restoreWarnings();
2395
2396 if ( !$haveDiff3 ) {
2397 wfDebug( "diff3 not found\n" );
2398 return false;
2399 }
2400
2401 # Make temporary files
2402 $td = wfTempDir();
2403 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2404 $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
2405 $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
2406
2407 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2408 # a newline character. To avoid this, we normalize the trailing whitespace before
2409 # creating the diff.
2410
2411 fwrite( $oldtextFile, rtrim( $old ) . "\n" );
2412 fclose( $oldtextFile );
2413 fwrite( $mytextFile, rtrim( $mine ) . "\n" );
2414 fclose( $mytextFile );
2415 fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
2416 fclose( $yourtextFile );
2417
2418 # Check for a conflict
2419 $cmd = Shell::escape( $wgDiff3, '-a', '--overlap-only', $mytextName,
2420 $oldtextName, $yourtextName );
2421 $handle = popen( $cmd, 'r' );
2422
2423 if ( fgets( $handle, 1024 ) ) {
2424 $conflict = true;
2425 } else {
2426 $conflict = false;
2427 }
2428 pclose( $handle );
2429
2430 # Merge differences
2431 $cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName,
2432 $oldtextName, $yourtextName );
2433 $handle = popen( $cmd, 'r' );
2434 $result = '';
2435 do {
2436 $data = fread( $handle, 8192 );
2437 if ( strlen( $data ) == 0 ) {
2438 break;
2439 }
2440 $result .= $data;
2441 } while ( true );
2442 pclose( $handle );
2443 unlink( $mytextName );
2444 unlink( $oldtextName );
2445 unlink( $yourtextName );
2446
2447 if ( $result === '' && $old !== '' && !$conflict ) {
2448 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
2449 $conflict = true;
2450 }
2451 return !$conflict;
2452}
2453
2465function wfDiff( $before, $after, $params = '-u' ) {
2466 if ( $before == $after ) {
2467 return '';
2468 }
2469
2471 MediaWiki\suppressWarnings();
2472 $haveDiff = $wgDiff && file_exists( $wgDiff );
2473 MediaWiki\restoreWarnings();
2474
2475 # This check may also protect against code injection in
2476 # case of broken installations.
2477 if ( !$haveDiff ) {
2478 wfDebug( "diff executable not found\n" );
2479 $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
2480 $format = new UnifiedDiffFormatter();
2481 return $format->format( $diffs );
2482 }
2483
2484 # Make temporary files
2485 $td = wfTempDir();
2486 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2487 $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
2488
2489 fwrite( $oldtextFile, $before );
2490 fclose( $oldtextFile );
2491 fwrite( $newtextFile, $after );
2492 fclose( $newtextFile );
2493
2494 // Get the diff of the two files
2495 $cmd = "$wgDiff " . $params . ' ' . Shell::escape( $oldtextName, $newtextName );
2496
2497 $h = popen( $cmd, 'r' );
2498 if ( !$h ) {
2499 unlink( $oldtextName );
2500 unlink( $newtextName );
2501 throw new Exception( __METHOD__ . '(): popen() failed' );
2502 }
2503
2504 $diff = '';
2505
2506 do {
2507 $data = fread( $h, 8192 );
2508 if ( strlen( $data ) == 0 ) {
2509 break;
2510 }
2511 $diff .= $data;
2512 } while ( true );
2513
2514 // Clean up
2515 pclose( $h );
2516 unlink( $oldtextName );
2517 unlink( $newtextName );
2518
2519 // Kill the --- and +++ lines. They're not useful.
2520 $diff_lines = explode( "\n", $diff );
2521 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
2522 unset( $diff_lines[0] );
2523 }
2524 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
2525 unset( $diff_lines[1] );
2526 }
2527
2528 $diff = implode( "\n", $diff_lines );
2529
2530 return $diff;
2531}
2532
2551function wfUsePHP( $req_ver ) {
2552 $php_ver = PHP_VERSION;
2553
2554 if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
2555 throw new MWException( "PHP $req_ver required--this is only $php_ver" );
2556 }
2557}
2558
2581function wfUseMW( $req_ver ) {
2583
2584 if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
2585 throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
2586 }
2587}
2588
2601function wfBaseName( $path, $suffix = '' ) {
2602 if ( $suffix == '' ) {
2603 $encSuffix = '';
2604 } else {
2605 $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
2606 }
2607
2608 $matches = [];
2609 if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
2610 return $matches[1];
2611 } else {
2612 return '';
2613 }
2614}
2615
2625function wfRelativePath( $path, $from ) {
2626 // Normalize mixed input on Windows...
2627 $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
2628 $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
2629
2630 // Trim trailing slashes -- fix for drive root
2631 $path = rtrim( $path, DIRECTORY_SEPARATOR );
2632 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2633
2634 $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
2635 $against = explode( DIRECTORY_SEPARATOR, $from );
2636
2637 if ( $pieces[0] !== $against[0] ) {
2638 // Non-matching Windows drive letters?
2639 // Return a full path.
2640 return $path;
2641 }
2642
2643 // Trim off common prefix
2644 while ( count( $pieces ) && count( $against )
2645 && $pieces[0] == $against[0] ) {
2646 array_shift( $pieces );
2647 array_shift( $against );
2648 }
2649
2650 // relative dots to bump us to the parent
2651 while ( count( $against ) ) {
2652 array_unshift( $pieces, '..' );
2653 array_shift( $against );
2654 }
2655
2656 array_push( $pieces, wfBaseName( $path ) );
2657
2658 return implode( DIRECTORY_SEPARATOR, $pieces );
2659}
2660
2678function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
2679 $lowercase = true, $engine = 'auto'
2680) {
2681 return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
2682}
2683
2691 wfDeprecated( __FUNCTION__, '1.27' );
2692 $session = SessionManager::getGlobalSession();
2693 $delay = $session->delaySave();
2694
2695 $session->resetId();
2696
2697 // Make sure a session is started, since that's what the old
2698 // wfResetSessionID() did.
2699 if ( session_id() !== $session->getId() ) {
2700 wfSetupSession( $session->getId() );
2701 }
2702
2703 ScopedCallback::consume( $delay );
2704}
2705
2715function wfSetupSession( $sessionId = false ) {
2716 wfDeprecated( __FUNCTION__, '1.27' );
2717
2718 if ( $sessionId ) {
2719 session_id( $sessionId );
2720 }
2721
2722 $session = SessionManager::getGlobalSession();
2723 $session->persist();
2724
2725 if ( session_id() !== $session->getId() ) {
2726 session_id( $session->getId() );
2727 }
2728 MediaWiki\quietCall( 'session_start' );
2729}
2730
2737function wfGetPrecompiledData( $name ) {
2738 global $IP;
2739
2740 $file = "$IP/serialized/$name";
2741 if ( file_exists( $file ) ) {
2742 $blob = file_get_contents( $file );
2743 if ( $blob ) {
2744 return unserialize( $blob );
2745 }
2746 }
2747 return false;
2748}
2749
2757function wfMemcKey( /*...*/ ) {
2758 return call_user_func_array(
2759 [ ObjectCache::getLocalClusterInstance(), 'makeKey' ],
2760 func_get_args()
2761 );
2762}
2763
2774function wfForeignMemcKey( $db, $prefix /*...*/ ) {
2775 $args = array_slice( func_get_args(), 2 );
2776 $keyspace = $prefix ? "$db-$prefix" : $db;
2777 return call_user_func_array(
2778 [ ObjectCache::getLocalClusterInstance(), 'makeKeyInternal' ],
2779 [ $keyspace, $args ]
2780 );
2781}
2782
2795function wfGlobalCacheKey( /*...*/ ) {
2796 return call_user_func_array(
2797 [ ObjectCache::getLocalClusterInstance(), 'makeGlobalKey' ],
2798 func_get_args()
2799 );
2800}
2801
2808function wfWikiID() {
2810 if ( $wgDBprefix ) {
2811 return "$wgDBname-$wgDBprefix";
2812 } else {
2813 return $wgDBname;
2814 }
2815}
2816
2824function wfSplitWikiID( $wiki ) {
2825 $bits = explode( '-', $wiki, 2 );
2826 if ( count( $bits ) < 2 ) {
2827 $bits[] = '';
2828 }
2829 return $bits;
2830}
2831
2857function wfGetDB( $db, $groups = [], $wiki = false ) {
2858 return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
2859}
2860
2870function wfGetLB( $wiki = false ) {
2871 if ( $wiki === false ) {
2872 return MediaWikiServices::getInstance()->getDBLoadBalancer();
2873 } else {
2874 $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2875 return $factory->getMainLB( $wiki );
2876 }
2877}
2878
2886function wfGetLBFactory() {
2887 return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2888}
2889
2898function wfFindFile( $title, $options = [] ) {
2899 return RepoGroup::singleton()->findFile( $title, $options );
2900}
2901
2909function wfLocalFile( $title ) {
2910 return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
2911}
2912
2921 return $wgMiserMode
2922 || ( SiteStats::pages() > 100000
2923 && SiteStats::edits() > 1000000
2924 && SiteStats::users() > 10000 );
2925}
2926
2935function wfScript( $script = 'index' ) {
2937 if ( $script === 'index' ) {
2938 return $wgScript;
2939 } elseif ( $script === 'load' ) {
2940 return $wgLoadScript;
2941 } else {
2942 return "{$wgScriptPath}/{$script}.php";
2943 }
2944}
2945
2951function wfGetScriptUrl() {
2952 if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
2953 /* as it was called, minus the query string.
2954 *
2955 * Some sites use Apache rewrite rules to handle subdomains,
2956 * and have PHP set up in a weird way that causes PHP_SELF
2957 * to contain the rewritten URL instead of the one that the
2958 * outside world sees.
2959 *
2960 * If in this mode, use SCRIPT_URL instead, which mod_rewrite
2961 * provides containing the "before" URL.
2962 */
2963 return $_SERVER['SCRIPT_NAME'];
2964 } else {
2965 return $_SERVER['URL'];
2966 }
2967}
2968
2976function wfBoolToStr( $value ) {
2977 return $value ? 'true' : 'false';
2978}
2979
2985function wfGetNull() {
2986 return wfIsWindows() ? 'NUL' : '/dev/null';
2987}
2988
3012 $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
3013) {
3014 if ( $timeout === null ) {
3015 $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
3016 }
3017
3018 if ( $cluster === '*' ) {
3019 $cluster = false;
3020 $wiki = false;
3021 } elseif ( $wiki === false ) {
3022 $wiki = wfWikiID();
3023 }
3024
3025 try {
3026 wfGetLBFactory()->waitForReplication( [
3027 'wiki' => $wiki,
3028 'cluster' => $cluster,
3029 'timeout' => $timeout,
3030 // B/C: first argument used to be "max seconds of lag"; ignore such values
3031 'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
3032 ] );
3033 } catch ( DBReplicationWaitError $e ) {
3034 return false;
3035 }
3036
3037 return true;
3038}
3039
3047function wfCountDown( $seconds ) {
3048 for ( $i = $seconds; $i >= 0; $i-- ) {
3049 if ( $i != $seconds ) {
3050 echo str_repeat( "\x08", strlen( $i + 1 ) );
3051 }
3052 echo $i;
3053 flush();
3054 if ( $i ) {
3055 sleep( 1 );
3056 }
3057 }
3058 echo "\n";
3059}
3060
3071 $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
3072 $name = preg_replace(
3073 "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
3074 '-',
3075 $name
3076 );
3077 // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
3078 $name = wfBaseName( $name );
3079 return $name;
3080}
3081
3087function wfMemoryLimit() {
3089 $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
3090 if ( $memlimit != -1 ) {
3091 $conflimit = wfShorthandToInteger( $wgMemoryLimit );
3092 if ( $conflimit == -1 ) {
3093 wfDebug( "Removing PHP's memory limit\n" );
3094 MediaWiki\suppressWarnings();
3095 ini_set( 'memory_limit', $conflimit );
3096 MediaWiki\restoreWarnings();
3097 return $conflimit;
3098 } elseif ( $conflimit > $memlimit ) {
3099 wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
3100 MediaWiki\suppressWarnings();
3101 ini_set( 'memory_limit', $conflimit );
3102 MediaWiki\restoreWarnings();
3103 return $conflimit;
3104 }
3105 }
3106 return $memlimit;
3107}
3108
3117
3118 $timeLimit = ini_get( 'max_execution_time' );
3119 // Note that CLI scripts use 0
3120 if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
3121 set_time_limit( $wgTransactionalTimeLimit );
3122 }
3123
3124 ignore_user_abort( true ); // ignore client disconnects
3125
3126 return $timeLimit;
3127}
3128
3136function wfShorthandToInteger( $string = '', $default = -1 ) {
3137 $string = trim( $string );
3138 if ( $string === '' ) {
3139 return $default;
3140 }
3141 $last = $string[strlen( $string ) - 1];
3142 $val = intval( $string );
3143 switch ( $last ) {
3144 case 'g':
3145 case 'G':
3146 $val *= 1024;
3147 // break intentionally missing
3148 case 'm':
3149 case 'M':
3150 $val *= 1024;
3151 // break intentionally missing
3152 case 'k':
3153 case 'K':
3154 $val *= 1024;
3155 }
3156
3157 return $val;
3158}
3159
3168function wfBCP47( $code ) {
3169 $codeSegment = explode( '-', $code );
3170 $codeBCP = [];
3171 foreach ( $codeSegment as $segNo => $seg ) {
3172 // when previous segment is x, it is a private segment and should be lc
3173 if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
3174 $codeBCP[$segNo] = strtolower( $seg );
3175 // ISO 3166 country code
3176 } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
3177 $codeBCP[$segNo] = strtoupper( $seg );
3178 // ISO 15924 script code
3179 } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
3180 $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
3181 // Use lowercase for other cases
3182 } else {
3183 $codeBCP[$segNo] = strtolower( $seg );
3184 }
3185 }
3186 $langCode = implode( '-', $codeBCP );
3187 return $langCode;
3188}
3189
3196function wfGetCache( $cacheType ) {
3197 return ObjectCache::getInstance( $cacheType );
3198}
3199
3205function wfGetMainCache() {
3207 return ObjectCache::getInstance( $wgMainCacheType );
3208}
3209
3217 return ObjectCache::getInstance( $wgMessageCacheType );
3218}
3219
3228 return ObjectCache::getInstance( $wgParserCacheType );
3229}
3230
3241function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
3242 return Hooks::run( $event, $args, $deprecatedVersion );
3243}
3244
3259function wfUnpack( $format, $data, $length = false ) {
3260 if ( $length !== false ) {
3261 $realLen = strlen( $data );
3262 if ( $realLen < $length ) {
3263 throw new MWException( "Tried to use wfUnpack on a "
3264 . "string of length $realLen, but needed one "
3265 . "of at least length $length."
3266 );
3267 }
3268 }
3269
3270 MediaWiki\suppressWarnings();
3271 $result = unpack( $format, $data );
3272 MediaWiki\restoreWarnings();
3273
3274 if ( $result === false ) {
3275 // If it cannot extract the packed data.
3276 throw new MWException( "unpack could not unpack binary data" );
3277 }
3278 return $result;
3279}
3280
3295function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
3296 # Handle redirects; callers almost always hit wfFindFile() anyway,
3297 # so just use that method because it has a fast process cache.
3298 $file = wfFindFile( $name ); // get the final name
3299 $name = $file ? $file->getTitle()->getDBkey() : $name;
3300
3301 # Run the extension hook
3302 $bad = false;
3303 if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
3304 return (bool)$bad;
3305 }
3306
3307 $cache = ObjectCache::getLocalServerInstance( 'hash' );
3308 $key = $cache->makeKey(
3309 'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist )
3310 );
3311 $badImages = $cache->get( $key );
3312
3313 if ( $badImages === false ) { // cache miss
3314 if ( $blacklist === null ) {
3315 $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
3316 }
3317 # Build the list now
3318 $badImages = [];
3319 $lines = explode( "\n", $blacklist );
3320 foreach ( $lines as $line ) {
3321 # List items only
3322 if ( substr( $line, 0, 1 ) !== '*' ) {
3323 continue;
3324 }
3325
3326 # Find all links
3327 $m = [];
3328 if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
3329 continue;
3330 }
3331
3332 $exceptions = [];
3333 $imageDBkey = false;
3334 foreach ( $m[1] as $i => $titleText ) {
3335 $title = Title::newFromText( $titleText );
3336 if ( !is_null( $title ) ) {
3337 if ( $i == 0 ) {
3338 $imageDBkey = $title->getDBkey();
3339 } else {
3340 $exceptions[$title->getPrefixedDBkey()] = true;
3341 }
3342 }
3343 }
3344
3345 if ( $imageDBkey !== false ) {
3346 $badImages[$imageDBkey] = $exceptions;
3347 }
3348 }
3349 $cache->set( $key, $badImages, 60 );
3350 }
3351
3352 $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
3353 $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
3354
3355 return $bad;
3356}
3357
3365function wfCanIPUseHTTPS( $ip ) {
3366 $canDo = true;
3367 Hooks::run( 'CanIPUseHTTPS', [ $ip, &$canDo ] );
3368 return !!$canDo;
3369}
3370
3378function wfIsInfinity( $str ) {
3379 // These are hardcoded elsewhere in MediaWiki (e.g. mediawiki.special.block.js).
3380 $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
3381 return in_array( $str, $infinityValues );
3382}
3383
3400
3401 $multipliers = [ 1 ];
3402 if ( $wgResponsiveImages ) {
3403 // These available sizes are hardcoded currently elsewhere in MediaWiki.
3404 // @see Linker::processResponsiveImages
3405 $multipliers[] = 1.5;
3406 $multipliers[] = 2;
3407 }
3408
3409 $handler = $file->getHandler();
3410 if ( !$handler || !isset( $params['width'] ) ) {
3411 return false;
3412 }
3413
3414 $basicParams = [];
3415 if ( isset( $params['page'] ) ) {
3416 $basicParams['page'] = $params['page'];
3417 }
3418
3419 $thumbLimits = [];
3420 $imageLimits = [];
3421 // Expand limits to account for multipliers
3422 foreach ( $multipliers as $multiplier ) {
3423 $thumbLimits = array_merge( $thumbLimits, array_map(
3424 function ( $width ) use ( $multiplier ) {
3425 return round( $width * $multiplier );
3426 }, $wgThumbLimits )
3427 );
3428 $imageLimits = array_merge( $imageLimits, array_map(
3429 function ( $pair ) use ( $multiplier ) {
3430 return [
3431 round( $pair[0] * $multiplier ),
3432 round( $pair[1] * $multiplier ),
3433 ];
3434 }, $wgImageLimits )
3435 );
3436 }
3437
3438 // Check if the width matches one of $wgThumbLimits
3439 if ( in_array( $params['width'], $thumbLimits ) ) {
3440 $normalParams = $basicParams + [ 'width' => $params['width'] ];
3441 // Append any default values to the map (e.g. "lossy", "lossless", ...)
3442 $handler->normaliseParams( $file, $normalParams );
3443 } else {
3444 // If not, then check if the width matchs one of $wgImageLimits
3445 $match = false;
3446 foreach ( $imageLimits as $pair ) {
3447 $normalParams = $basicParams + [ 'width' => $pair[0], 'height' => $pair[1] ];
3448 // Decide whether the thumbnail should be scaled on width or height.
3449 // Also append any default values to the map (e.g. "lossy", "lossless", ...)
3450 $handler->normaliseParams( $file, $normalParams );
3451 // Check if this standard thumbnail size maps to the given width
3452 if ( $normalParams['width'] == $params['width'] ) {
3453 $match = true;
3454 break;
3455 }
3456 }
3457 if ( !$match ) {
3458 return false; // not standard for description pages
3459 }
3460 }
3461
3462 // Check that the given values for non-page, non-width, params are just defaults
3463 foreach ( $params as $key => $value ) {
3464 if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3465 return false;
3466 }
3467 }
3468
3469 return true;
3470}
3471
3484function wfArrayPlus2d( array $baseArray, array $newValues ) {
3485 // First merge items that are in both arrays
3486 foreach ( $baseArray as $name => &$groupVal ) {
3487 if ( isset( $newValues[$name] ) ) {
3488 $groupVal += $newValues[$name];
3489 }
3490 }
3491 // Now add items that didn't exist yet
3492 $baseArray += $newValues;
3493
3494 return $baseArray;
3495}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
unserialize( $serialized)
$wgLanguageCode
Site language code.
$wgParserCacheType
The cache type for storing article HTML.
$wgMemoryLimit
The minimum amount of memory that MediaWiki "needs"; MediaWiki will try to raise PHP's memory limit i...
$wgDBprefix
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 (php/php5).
$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...
$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.
$wgVersion
MediaWiki version number.
$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
Port where you have HTTPS running Supports HTTPS on non-standard ports.
$wgDiff
Path to the GNU diff utility.
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 between 0 and 1, in a way not likely to give duplicate values for any real...
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.
wfBCP47( $code)
Get the normalised IETF language tag See unit test for examples.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfClientAcceptsGzip( $force=false)
Whether the client accept gzip encoding.
wfShellExecDisabled()
Check if wfShellExec() is effectively disabled via php.ini config.
wfIncrStats( $key, $count=1)
Increment a statistics counter.
wfLogDBError( $text, array $context=[])
Log for database errors.
wfLoadSkins(array $skins)
Load multiple skins at once.
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.
wfReadOnly()
Check whether the wiki is in read-only mode.
wfGlobalCacheKey()
Make a cache key with database-agnostic prefix.
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.
wfUseMW( $req_ver)
This function works like "use VERSION" in Perl except it checks the version of MediaWiki,...
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
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...
wfPercent( $nr, $acc=2, $round=true)
wfCountDown( $seconds)
Count down from $seconds to zero on the terminal, with a one-second pause between showing each number...
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.
wfUsePHP( $req_ver)
This function works like "use VERSION" in Perl, the program will die with a backtrace if the current ...
wfRestoreWarnings()
wfIsDebugRawPage()
Returns true if debug logging should be suppressed if $wgDebugRawPage = false.
wfHostname()
Fetch server name for use in error reporting etc.
wfReportTime()
Returns a script tag that stores the amount of time it took MediaWiki to handle the request in millis...
wfExpandUrl( $url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfSplitWikiID( $wiki)
Split a wiki ID into DB name and table prefix.
wfGetMainCache()
Get the main cache object.
wfRunHooks( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
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.
wfGetParserCacheStorage()
Get the cache object used by the parser cache.
wfMemcKey()
Make a cache key for the local wiki.
wfEscapeShellArg()
Version of escapeshellarg() that works better on Windows.
wfFindFile( $title, $options=[])
Find a file.
wfExpandIRI_callback( $matches)
Private callback for wfExpandIRI.
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.
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
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.
wfInitShellLocale()
Formerly set the locale for locale-sensitive operations.
wfRelativePath( $path, $from)
Generate a relative path name to the given file.
wfHttpError( $code, $label, $desc)
Provide a simple HTTP error.
wfMakeUrlIndexes( $url)
Make URL indexes, appropriate for the el_index field of externallinks.
wfUrlProtocols( $includeProtocolRelative=true)
Returns a regular expression of url protocols.
wfUnpack( $format, $data, $length=false)
Wrapper around php's unpack.
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.
wfMerge( $old, $mine, $yours, &$result)
wfMerge attempts to merge differences between three texts.
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.
wfGetLBFactory()
Get the load balancer factory object.
wfGetScriptUrl()
Get the script URL.
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.
wfForeignMemcKey( $db, $prefix)
Make a cache key for a foreign DB.
wfGetCache( $cacheType)
Get a specific cache object.
wfMessageFallback()
This function accepts multiple message keys and returns a message instance for the first message whic...
wfArrayFilterByKey(array $arr, callable $callback)
Like array_filter with ARRAY_FILTER_USE_KEY, but works pre-5.6.
wfIsBadImage( $name, $contextTitle=false, $blacklist=null)
Determine if an image exists on the 'bad image list'.
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...
wfErrorLog( $text, $file, array $context=[])
Log to a file without getting "file size exceeded" signals.
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.
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.
wfMemoryLimit()
Set PHP's memory limit to the larger of php.ini or $wgMemoryLimit.
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.
wfResetSessionID()
Reset the session id.
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.
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.
wfArrayFilter(array $arr, callable $callback)
Like array_filter with ARRAY_FILTER_USE_BOTH, but works pre-5.6.
wfResetOutputBuffers( $resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true, $engine='auto')
Convert an arbitrarily-long digit string from one numeric base to another, optionally zero-padding to...
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
$wgOut
Definition Setup.php:827
global $wgCommandLineMode
Definition Setup.php:526
if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:662
$IP
Definition WebStart.php:57
float $wgRequestTime
Request start time as fractional seconds since epoch.
Definition WebStart.php:42
$line
Definition cdb.php:58
if( $line===false) $args
Definition cdb.php:63
Class representing a 'diff' between two sequences of strings.
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:51
getHandler()
Get a MediaHandler instance for this file.
Definition File.php:1364
static header( $code)
Output an HTTP status code header.
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:41
The Message class provides methods which fulfil two basic services:
Definition Message.php:159
Stub profiler that does nothing.
static singleton()
Get a RepoGroup instance.
Definition RepoGroup.php:59
static getMain()
Static methods.
A statsd client that applies the sampling rate to the data items before sending them.
static edits()
static users()
static pages()
static makeVariablesScript( $data)
Definition Skin.php:368
static getUsableTempDirectory()
A formatter that outputs unified diffs.
Exception class for replica DB wait timeouts.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition deferred.txt:11
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as and the local content language as $wgContLang
Definition design.txt:57
when a variable name is used in a it is silently declared as a new local masking the global
Definition design.txt:95
this class mediates it Skin Encapsulates a look and feel for the wiki All of the functions that render HTML and make choices about how to render it are here and are called from various other places when and is meant to be subclassed with other skins that may override some of its functions The User object contains a reference to a and so rather than having a global skin object we just rely on the global User and get the skin with $wgUser and also has some character encoding functions and other locale stuff The current user interface language is instantiated as $wgLang
Definition design.txt:56
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
while(( $__line=Maintenance::readconsole()) !==false) print
Definition eval.php:64
const PROTO_CANONICAL
Definition Defines.php:224
const PROTO_HTTPS
Definition Defines.php:221
const PROTO_CURRENT
Definition Defines.php:223
const PROTO_INTERNAL
Definition Defines.php:225
const PROTO_HTTP
Definition Defines.php:220
Status::newGood()` to allow deletion, and then `return false` from the hook function. Ensure you consume the 'ChangeTagAfterDelete' hook to carry out custom deletion actions. $tag:name of the tag $user:user initiating the action & $status:Status object. See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use. & $tags:list of all active tags. Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function. Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change. & $allowedTags:List of all the tags the user is allowed to add. Any tags the user wants to add( $addTags) that are not in this array will cause it to fail. You may add or remove tags to this array as required. $addTags:List of tags user intends to add. $user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed. $performer:The User who will perform the change $user:The User whose groups will be changed & $add:The groups that will be added & $remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation. $collationName:Name of the collation in question & $collationObject:Null. Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user 's email has been confirmed successfully. $user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object. Called by Content::getParserOutput after parsing has finished. Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML). $content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput. May be used to override the normal model-specific rendering of page content. $content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering. To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state. $generateHtml:boolean, indicating whether full HTML should be generated. If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object. & $output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title. May be used to assign a different model for that title. $title:the Title in question & $model:the model name. Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers. Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core. $modeName:the requested content model name & $handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page. This is especially useful to prevent some content models to be used in some special location. $contentModel:ID of the content model in question $title:the Title in question. & $ok:Output parameter, whether it is OK to use $contentModel on $title. Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run & $pager:Pager object for contributions & $queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions & $data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title & $tools:Array of tool links $specialPage:SpecialPage instance for context and services. Can be either SpecialContributions or DeletedContributionsPage. Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested. Handler functions that modify $result should generally return false to disable further attempts at conversion. $content:The Content object to be converted. $toModel:The ID of the content model to convert to. $lossy:boolean indicating whether lossy conversion is allowed. & $result:Output parameter, in case the handler function wants to provide a converted Content object. Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g. for a special namespace, etc. $article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery & $data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished. Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions & $ret:the HTML line $row:the DB row for this line & $classes:the classes to add to the surrounding< li > & $attribs:associative array of other HTML attributes for the< li > element. Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function. $differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision 's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable 's value is null. This hook can be used to inject content into said class member variable. $differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the "mark as patrolled" link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink". $differenceEngine:DifferenceEngine object & $markAsPatrolledLink:The "mark as patrolled" link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter. For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions. & $rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision 's author, whether the revision was marked as a minor edit or not, etc. $differenceEngine:DifferenceEngine object & $newHeader:The string containing the various #mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition hooks.txt:1245
the array() calling protocol came about after MediaWiki 1.4rc1.
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2775
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. Return false to stop further processing of the tag $reader:XMLReader object $logInfo:Array of information 'ImportHandlePageXMLTag':When parsing a XML tag in a page. Return false to stop further processing of the tag $reader:XMLReader object & $pageInfo:Array of information 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. Return false to stop further processing of the tag $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. Return false to stop further processing of the tag $reader:XMLReader object 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports. & $fullInterwikiPrefix:Interwiki prefix, may contain colons. & $pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable. Can be used to lazy-load the import sources list. & $importSources:The value of $wgImportSources. Modify as necessary. See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. & $title:Title object for the current page & $request:WebRequest & $ignoreRedirect:boolean to skip redirect check & $target:Title/string of redirect target & $article:Article object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) & $article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() & $ip:IP being check & $result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED! Use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language & $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED! Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language & $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Array with elements of the form "language:title" in the order that they will be output. & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LanguageSelector':Hook to change the language selector available on a page. $out:The output page. $cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED! Use HtmlPageLinkRendererBegin instead. Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition hooks.txt:1963
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account incomplete not yet checked for validity & $retval
Definition hooks.txt:266
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title after the basic globals have been set but before ordinary actions take place $output
Definition hooks.txt:2225
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition hooks.txt:1971
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
Definition hooks.txt:863
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g. with the RejectParserCacheValue hook) because MediaWiki won 't do it for you. & $defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition hooks.txt:2780
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:962
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "&lt;div ...>$1&lt;/div>"). - flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException':Called before an exception(or PHP error) is logged. This is meant for integration with external error aggregation services
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition hooks.txt:2805
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition hooks.txt:1975
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition hooks.txt:862
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned $skin
Definition hooks.txt:1981
the value to return A Title object or null for latest all implement SearchIndexField $engine
Definition hooks.txt:2850
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
Definition hooks.txt:901
null for the local wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
Definition hooks.txt:1610
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account $user
Definition hooks.txt:247
returning false will NOT prevent logging $e
Definition hooks.txt:2146
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
$cache
Definition mcc.php:33
controlled by $wgMainCacheType controlled by $wgParserCacheType controlled by $wgMessageCacheType If you set CACHE_NONE to one of the three control default value for MediaWiki still create a but requests to it are no ops and we always fall through to the database If the cache daemon can t be it should also disable itself fairly smoothly By $wgMemc is used but when it is $parserMemc or $messageMemc this is mentioned $wgDBname
CACHE_MEMCACHED $wgMainCacheType
Definition memcached.txt:63
$source
if(is_array($mode)) switch( $mode) $input
$last
$lines
Definition router.php:61
$params