MediaWiki REL1_28
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;
30use Wikimedia\ScopedCallback;
31
32// Hide compatibility functions from Doxygen
34
42// hash_equals function only exists in PHP >= 5.6.0
43// http://php.net/hash_equals
44if ( !function_exists( 'hash_equals' ) ) {
70 function hash_equals( $known_string, $user_string ) {
71 // Strict type checking as in PHP's native implementation
72 if ( !is_string( $known_string ) ) {
73 trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
74 gettype( $known_string ) . ' given', E_USER_WARNING );
75
76 return false;
77 }
78
79 if ( !is_string( $user_string ) ) {
80 trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
81 gettype( $user_string ) . ' given', E_USER_WARNING );
82
83 return false;
84 }
85
86 $known_string_len = strlen( $known_string );
87 if ( $known_string_len !== strlen( $user_string ) ) {
88 return false;
89 }
90
91 $result = 0;
92 for ( $i = 0; $i < $known_string_len; $i++ ) {
93 $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] );
94 }
95
96 return ( $result === 0 );
97 }
98}
100
111function wfLoadExtension( $ext, $path = null ) {
112 if ( !$path ) {
114 $path = "$wgExtensionDirectory/$ext/extension.json";
115 }
117}
118
132function wfLoadExtensions( array $exts ) {
134 $registry = ExtensionRegistry::getInstance();
135 foreach ( $exts as $ext ) {
136 $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
137 }
138}
139
148function wfLoadSkin( $skin, $path = null ) {
149 if ( !$path ) {
151 $path = "$wgStyleDirectory/$skin/skin.json";
152 }
154}
155
163function wfLoadSkins( array $skins ) {
165 $registry = ExtensionRegistry::getInstance();
166 foreach ( $skins as $skin ) {
167 $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
168 }
169}
170
177function wfArrayDiff2( $a, $b ) {
178 return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
179}
180
186function wfArrayDiff2_cmp( $a, $b ) {
187 if ( is_string( $a ) && is_string( $b ) ) {
188 return strcmp( $a, $b );
189 } elseif ( count( $a ) !== count( $b ) ) {
190 return count( $a ) < count( $b ) ? -1 : 1;
191 } else {
192 reset( $a );
193 reset( $b );
194 while ( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
195 $cmp = strcmp( $valueA, $valueB );
196 if ( $cmp !== 0 ) {
197 return $cmp;
198 }
199 }
200 return 0;
201 }
202}
203
213function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
214 if ( is_null( $changed ) ) {
215 throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
216 }
217 if ( $default[$key] !== $value ) {
218 $changed[$key] = $value;
219 }
220}
221
241function wfMergeErrorArrays( /*...*/ ) {
242 $args = func_get_args();
243 $out = [];
244 foreach ( $args as $errors ) {
245 foreach ( $errors as $params ) {
246 $originalParams = $params;
247 if ( $params[0] instanceof MessageSpecifier ) {
248 $msg = $params[0];
249 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
250 }
251 # @todo FIXME: Sometimes get nested arrays for $params,
252 # which leads to E_NOTICEs
253 $spec = implode( "\t", $params );
254 $out[$spec] = $originalParams;
255 }
256 }
257 return array_values( $out );
258}
259
268function wfArrayInsertAfter( array $array, array $insert, $after ) {
269 // Find the offset of the element to insert after.
270 $keys = array_keys( $array );
271 $offsetByKey = array_flip( $keys );
272
273 $offset = $offsetByKey[$after];
274
275 // Insert at the specified offset
276 $before = array_slice( $array, 0, $offset + 1, true );
277 $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
278
279 $output = $before + $insert + $after;
280
281 return $output;
282}
283
291function wfObjectToArray( $objOrArray, $recursive = true ) {
292 $array = [];
293 if ( is_object( $objOrArray ) ) {
294 $objOrArray = get_object_vars( $objOrArray );
295 }
296 foreach ( $objOrArray as $key => $value ) {
297 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
299 }
300
301 $array[$key] = $value;
302 }
303
304 return $array;
305}
306
317function wfRandom() {
318 // The maximum random value is "only" 2^31-1, so get two random
319 // values to reduce the chance of dupes
320 $max = mt_getrandmax() + 1;
321 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
322 return $rand;
323}
324
335function wfRandomString( $length = 32 ) {
336 $str = '';
337 for ( $n = 0; $n < $length; $n += 7 ) {
338 $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
339 }
340 return substr( $str, 0, $length );
341}
342
370function wfUrlencode( $s ) {
371 static $needle;
372
373 if ( is_null( $s ) ) {
374 $needle = null;
375 return '';
376 }
377
378 if ( is_null( $needle ) ) {
379 $needle = [ '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F', '%7E' ];
380 if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
381 ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
382 ) {
383 $needle[] = '%3A';
384 }
385 }
386
387 $s = urlencode( $s );
388 $s = str_ireplace(
389 $needle,
390 [ ';', '@', '$', '!', '*', '(', ')', ',', '/', '~', ':' ],
391 $s
392 );
393
394 return $s;
395}
396
407function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) {
408 if ( !is_null( $array2 ) ) {
409 $array1 = $array1 + $array2;
410 }
411
412 $cgi = '';
413 foreach ( $array1 as $key => $value ) {
414 if ( !is_null( $value ) && $value !== false ) {
415 if ( $cgi != '' ) {
416 $cgi .= '&';
417 }
418 if ( $prefix !== '' ) {
419 $key = $prefix . "[$key]";
420 }
421 if ( is_array( $value ) ) {
422 $firstTime = true;
423 foreach ( $value as $k => $v ) {
424 $cgi .= $firstTime ? '' : '&';
425 if ( is_array( $v ) ) {
426 $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" );
427 } else {
428 $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
429 }
430 $firstTime = false;
431 }
432 } else {
433 if ( is_object( $value ) ) {
434 $value = $value->__toString();
435 }
436 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
437 }
438 }
439 }
440 return $cgi;
441}
442
452function wfCgiToArray( $query ) {
453 if ( isset( $query[0] ) && $query[0] == '?' ) {
454 $query = substr( $query, 1 );
455 }
456 $bits = explode( '&', $query );
457 $ret = [];
458 foreach ( $bits as $bit ) {
459 if ( $bit === '' ) {
460 continue;
461 }
462 if ( strpos( $bit, '=' ) === false ) {
463 // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
464 $key = $bit;
465 $value = '';
466 } else {
467 list( $key, $value ) = explode( '=', $bit );
468 }
469 $key = urldecode( $key );
470 $value = urldecode( $value );
471 if ( strpos( $key, '[' ) !== false ) {
472 $keys = array_reverse( explode( '[', $key ) );
473 $key = array_pop( $keys );
474 $temp = $value;
475 foreach ( $keys as $k ) {
476 $k = substr( $k, 0, -1 );
477 $temp = [ $k => $temp ];
478 }
479 if ( isset( $ret[$key] ) ) {
480 $ret[$key] = array_merge( $ret[$key], $temp );
481 } else {
482 $ret[$key] = $temp;
483 }
484 } else {
485 $ret[$key] = $value;
486 }
487 }
488 return $ret;
489}
490
499function wfAppendQuery( $url, $query ) {
500 if ( is_array( $query ) ) {
502 }
503 if ( $query != '' ) {
504 // Remove the fragment, if there is one
505 $fragment = false;
506 $hashPos = strpos( $url, '#' );
507 if ( $hashPos !== false ) {
508 $fragment = substr( $url, $hashPos );
509 $url = substr( $url, 0, $hashPos );
510 }
511
512 // Add parameter
513 if ( false === strpos( $url, '?' ) ) {
514 $url .= '?';
515 } else {
516 $url .= '&';
517 }
518 $url .= $query;
519
520 // Put the fragment back
521 if ( $fragment !== false ) {
522 $url .= $fragment;
523 }
524 }
525 return $url;
526}
527
551function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
554 if ( $defaultProto === PROTO_CANONICAL ) {
555 $serverUrl = $wgCanonicalServer;
556 } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
557 // Make $wgInternalServer fall back to $wgServer if not set
558 $serverUrl = $wgInternalServer;
559 } else {
560 $serverUrl = $wgServer;
561 if ( $defaultProto === PROTO_CURRENT ) {
562 $defaultProto = $wgRequest->getProtocol() . '://';
563 }
564 }
565
566 // Analyze $serverUrl to obtain its protocol
567 $bits = wfParseUrl( $serverUrl );
568 $serverHasProto = $bits && $bits['scheme'] != '';
569
570 if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
571 if ( $serverHasProto ) {
572 $defaultProto = $bits['scheme'] . '://';
573 } else {
574 // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
575 // This really isn't supposed to happen. Fall back to HTTP in this
576 // ridiculous case.
577 $defaultProto = PROTO_HTTP;
578 }
579 }
580
581 $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
582
583 if ( substr( $url, 0, 2 ) == '//' ) {
584 $url = $defaultProtoWithoutSlashes . $url;
585 } elseif ( substr( $url, 0, 1 ) == '/' ) {
586 // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
587 // otherwise leave it alone.
588 $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
589 }
590
591 $bits = wfParseUrl( $url );
592
593 // ensure proper port for HTTPS arrives in URL
594 // https://phabricator.wikimedia.org/T67184
595 if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
596 $bits['port'] = $wgHttpsPort;
597 }
598
599 if ( $bits && isset( $bits['path'] ) ) {
600 $bits['path'] = wfRemoveDotSegments( $bits['path'] );
601 return wfAssembleUrl( $bits );
602 } elseif ( $bits ) {
603 # No path to expand
604 return $url;
605 } elseif ( substr( $url, 0, 1 ) != '/' ) {
606 # URL is a relative path
607 return wfRemoveDotSegments( $url );
608 }
609
610 # Expanded URL is not valid.
611 return false;
612}
613
627function wfAssembleUrl( $urlParts ) {
628 $result = '';
629
630 if ( isset( $urlParts['delimiter'] ) ) {
631 if ( isset( $urlParts['scheme'] ) ) {
632 $result .= $urlParts['scheme'];
633 }
634
635 $result .= $urlParts['delimiter'];
636 }
637
638 if ( isset( $urlParts['host'] ) ) {
639 if ( isset( $urlParts['user'] ) ) {
640 $result .= $urlParts['user'];
641 if ( isset( $urlParts['pass'] ) ) {
642 $result .= ':' . $urlParts['pass'];
643 }
644 $result .= '@';
645 }
646
647 $result .= $urlParts['host'];
648
649 if ( isset( $urlParts['port'] ) ) {
650 $result .= ':' . $urlParts['port'];
651 }
652 }
653
654 if ( isset( $urlParts['path'] ) ) {
655 $result .= $urlParts['path'];
656 }
657
658 if ( isset( $urlParts['query'] ) ) {
659 $result .= '?' . $urlParts['query'];
660 }
661
662 if ( isset( $urlParts['fragment'] ) ) {
663 $result .= '#' . $urlParts['fragment'];
664 }
665
666 return $result;
667}
668
679function wfRemoveDotSegments( $urlPath ) {
680 $output = '';
681 $inputOffset = 0;
682 $inputLength = strlen( $urlPath );
683
684 while ( $inputOffset < $inputLength ) {
685 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
686 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
687 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
688 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
689 $trimOutput = false;
690
691 if ( $prefixLengthTwo == './' ) {
692 # Step A, remove leading "./"
693 $inputOffset += 2;
694 } elseif ( $prefixLengthThree == '../' ) {
695 # Step A, remove leading "../"
696 $inputOffset += 3;
697 } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
698 # Step B, replace leading "/.$" with "/"
699 $inputOffset += 1;
700 $urlPath[$inputOffset] = '/';
701 } elseif ( $prefixLengthThree == '/./' ) {
702 # Step B, replace leading "/./" with "/"
703 $inputOffset += 2;
704 } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
705 # Step C, replace leading "/..$" with "/" and
706 # remove last path component in output
707 $inputOffset += 2;
708 $urlPath[$inputOffset] = '/';
709 $trimOutput = true;
710 } elseif ( $prefixLengthFour == '/../' ) {
711 # Step C, replace leading "/../" with "/" and
712 # remove last path component in output
713 $inputOffset += 3;
714 $trimOutput = true;
715 } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
716 # Step D, remove "^.$"
717 $inputOffset += 1;
718 } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
719 # Step D, remove "^..$"
720 $inputOffset += 2;
721 } else {
722 # Step E, move leading path segment to output
723 if ( $prefixLengthOne == '/' ) {
724 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
725 } else {
726 $slashPos = strpos( $urlPath, '/', $inputOffset );
727 }
728 if ( $slashPos === false ) {
729 $output .= substr( $urlPath, $inputOffset );
730 $inputOffset = $inputLength;
731 } else {
732 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
733 $inputOffset += $slashPos - $inputOffset;
734 }
735 }
736
737 if ( $trimOutput ) {
738 $slashPos = strrpos( $output, '/' );
739 if ( $slashPos === false ) {
740 $output = '';
741 } else {
742 $output = substr( $output, 0, $slashPos );
743 }
744 }
745 }
746
747 return $output;
748}
749
757function wfUrlProtocols( $includeProtocolRelative = true ) {
759
760 // Cache return values separately based on $includeProtocolRelative
761 static $withProtRel = null, $withoutProtRel = null;
762 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
763 if ( !is_null( $cachedValue ) ) {
764 return $cachedValue;
765 }
766
767 // Support old-style $wgUrlProtocols strings, for backwards compatibility
768 // with LocalSettings files from 1.5
769 if ( is_array( $wgUrlProtocols ) ) {
770 $protocols = [];
771 foreach ( $wgUrlProtocols as $protocol ) {
772 // Filter out '//' if !$includeProtocolRelative
773 if ( $includeProtocolRelative || $protocol !== '//' ) {
774 $protocols[] = preg_quote( $protocol, '/' );
775 }
776 }
777
778 $retval = implode( '|', $protocols );
779 } else {
780 // Ignore $includeProtocolRelative in this case
781 // This case exists for pre-1.6 compatibility, and we can safely assume
782 // that '//' won't appear in a pre-1.6 config because protocol-relative
783 // URLs weren't supported until 1.18
785 }
786
787 // Cache return value
788 if ( $includeProtocolRelative ) {
789 $withProtRel = $retval;
790 } else {
791 $withoutProtRel = $retval;
792 }
793 return $retval;
794}
795
803 return wfUrlProtocols( false );
804}
805
817function wfParseUrl( $url ) {
818 global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
819
820 // Protocol-relative URLs are handled really badly by parse_url(). It's so
821 // bad that the easiest way to handle them is to just prepend 'http:' and
822 // strip the protocol out later.
823 $wasRelative = substr( $url, 0, 2 ) == '//';
824 if ( $wasRelative ) {
825 $url = "http:$url";
826 }
827 MediaWiki\suppressWarnings();
828 $bits = parse_url( $url );
829 MediaWiki\restoreWarnings();
830 // parse_url() returns an array without scheme for some invalid URLs, e.g.
831 // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
832 if ( !$bits || !isset( $bits['scheme'] ) ) {
833 return false;
834 }
835
836 // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
837 $bits['scheme'] = strtolower( $bits['scheme'] );
838
839 // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
840 if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
841 $bits['delimiter'] = '://';
842 } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
843 $bits['delimiter'] = ':';
844 // parse_url detects for news: and mailto: the host part of an url as path
845 // We have to correct this wrong detection
846 if ( isset( $bits['path'] ) ) {
847 $bits['host'] = $bits['path'];
848 $bits['path'] = '';
849 }
850 } else {
851 return false;
852 }
853
854 /* Provide an empty host for eg. file:/// urls (see T30627) */
855 if ( !isset( $bits['host'] ) ) {
856 $bits['host'] = '';
857
858 // See T47069
859 if ( isset( $bits['path'] ) ) {
860 /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
861 if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
862 $bits['path'] = '/' . $bits['path'];
863 }
864 } else {
865 $bits['path'] = '';
866 }
867 }
868
869 // If the URL was protocol-relative, fix scheme and delimiter
870 if ( $wasRelative ) {
871 $bits['scheme'] = '';
872 $bits['delimiter'] = '//';
873 }
874 return $bits;
875}
876
887function wfExpandIRI( $url ) {
888 return preg_replace_callback(
889 '/((?:%[89A-F][0-9A-F])+)/i',
890 'wfExpandIRI_callback',
891 wfExpandUrl( $url )
892 );
893}
894
901 return urldecode( $matches[1] );
902}
903
910function wfMakeUrlIndexes( $url ) {
911 $bits = wfParseUrl( $url );
912
913 // Reverse the labels in the hostname, convert to lower case
914 // For emails reverse domainpart only
915 if ( $bits['scheme'] == 'mailto' ) {
916 $mailparts = explode( '@', $bits['host'], 2 );
917 if ( count( $mailparts ) === 2 ) {
918 $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
919 } else {
920 // No domain specified, don't mangle it
921 $domainpart = '';
922 }
923 $reversedHost = $domainpart . '@' . $mailparts[0];
924 } else {
925 $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
926 }
927 // Add an extra dot to the end
928 // Why? Is it in wrong place in mailto links?
929 if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
930 $reversedHost .= '.';
931 }
932 // Reconstruct the pseudo-URL
933 $prot = $bits['scheme'];
934 $index = $prot . $bits['delimiter'] . $reversedHost;
935 // Leave out user and password. Add the port, path, query and fragment
936 if ( isset( $bits['port'] ) ) {
937 $index .= ':' . $bits['port'];
938 }
939 if ( isset( $bits['path'] ) ) {
940 $index .= $bits['path'];
941 } else {
942 $index .= '/';
943 }
944 if ( isset( $bits['query'] ) ) {
945 $index .= '?' . $bits['query'];
946 }
947 if ( isset( $bits['fragment'] ) ) {
948 $index .= '#' . $bits['fragment'];
949 }
950
951 if ( $prot == '' ) {
952 return [ "http:$index", "https:$index" ];
953 } else {
954 return [ $index ];
955 }
956}
957
964function wfMatchesDomainList( $url, $domains ) {
965 $bits = wfParseUrl( $url );
966 if ( is_array( $bits ) && isset( $bits['host'] ) ) {
967 $host = '.' . $bits['host'];
968 foreach ( (array)$domains as $domain ) {
969 $domain = '.' . $domain;
970 if ( substr( $host, -strlen( $domain ) ) === $domain ) {
971 return true;
972 }
973 }
974 }
975 return false;
976}
977
998function wfDebug( $text, $dest = 'all', array $context = [] ) {
1001
1002 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1003 return;
1004 }
1005
1006 $text = trim( $text );
1007
1008 if ( $wgDebugTimestamps ) {
1009 $context['seconds_elapsed'] = sprintf(
1010 '%6.4f',
1011 microtime( true ) - $wgRequestTime
1012 );
1013 $context['memory_used'] = sprintf(
1014 '%5.1fM',
1015 ( memory_get_usage( true ) / ( 1024 * 1024 ) )
1016 );
1017 }
1018
1019 if ( $wgDebugLogPrefix !== '' ) {
1020 $context['prefix'] = $wgDebugLogPrefix;
1021 }
1022 $context['private'] = ( $dest === false || $dest === 'private' );
1023
1024 $logger = LoggerFactory::getInstance( 'wfDebug' );
1025 $logger->debug( $text, $context );
1026}
1027
1033 static $cache;
1034 if ( $cache !== null ) {
1035 return $cache;
1036 }
1037 # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
1038 if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
1039 || (
1040 isset( $_SERVER['SCRIPT_NAME'] )
1041 && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
1042 )
1043 ) {
1044 $cache = true;
1045 } else {
1046 $cache = false;
1047 }
1048 return $cache;
1049}
1050
1056function wfDebugMem( $exact = false ) {
1057 $mem = memory_get_usage();
1058 if ( !$exact ) {
1059 $mem = floor( $mem / 1024 ) . ' KiB';
1060 } else {
1061 $mem .= ' B';
1062 }
1063 wfDebug( "Memory usage: $mem\n" );
1064}
1065
1091function wfDebugLog(
1092 $logGroup, $text, $dest = 'all', array $context = []
1093) {
1094 $text = trim( $text );
1095
1096 $logger = LoggerFactory::getInstance( $logGroup );
1097 $context['private'] = ( $dest === false || $dest === 'private' );
1098 $logger->info( $text, $context );
1099}
1100
1109function wfLogDBError( $text, array $context = [] ) {
1110 $logger = LoggerFactory::getInstance( 'wfLogDBError' );
1111 $logger->error( trim( $text ), $context );
1112}
1113
1127function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
1128 MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
1129}
1130
1141function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1142 MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
1143}
1144
1154function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1155 MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
1156}
1157
1171function wfErrorLog( $text, $file, array $context = [] ) {
1172 wfDeprecated( __METHOD__, '1.25' );
1173 $logger = LoggerFactory::getInstance( 'wfErrorLog' );
1174 $context['destination'] = $file;
1175 $logger->info( trim( $text ), $context );
1176}
1177
1183
1186
1187 $profiler = Profiler::instance();
1188 $profiler->setContext( $context );
1189 $profiler->logData();
1190
1191 $config = $context->getConfig();
1192 if ( $config->get( 'StatsdServer' ) ) {
1193 try {
1194 $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
1195 $statsdHost = $statsdServer[0];
1196 $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
1197 $statsdSender = new SocketSender( $statsdHost, $statsdPort );
1198 $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
1199 $statsdClient->setSamplingRates( $config->get( 'StatsdSamplingRates' ) );
1200 $statsdClient->send( $context->getStats()->getBuffer() );
1201 } catch ( Exception $ex ) {
1202 MWExceptionHandler::logException( $ex );
1203 }
1204 }
1205
1206 # Profiling must actually be enabled...
1207 if ( $profiler instanceof ProfilerStub ) {
1208 return;
1209 }
1210
1211 if ( isset( $wgDebugLogGroups['profileoutput'] )
1212 && $wgDebugLogGroups['profileoutput'] === false
1213 ) {
1214 // Explicitly disabled
1215 return;
1216 }
1217 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1218 return;
1219 }
1220
1221 $ctx = [ 'elapsed' => $request->getElapsedTime() ];
1222 if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1223 $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
1224 }
1225 if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
1226 $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
1227 }
1228 if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
1229 $ctx['from'] = $_SERVER['HTTP_FROM'];
1230 }
1231 if ( isset( $ctx['forwarded_for'] ) ||
1232 isset( $ctx['client_ip'] ) ||
1233 isset( $ctx['from'] ) ) {
1234 $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
1235 }
1236
1237 // Don't load $wgUser at this late stage just for statistics purposes
1238 // @todo FIXME: We can detect some anons even if it is not loaded.
1239 // See User::getId()
1240 $user = $context->getUser();
1241 $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
1242
1243 // Command line script uses a FauxRequest object which does not have
1244 // any knowledge about an URL and throw an exception instead.
1245 try {
1246 $ctx['url'] = urldecode( $request->getRequestURL() );
1247 } catch ( Exception $ignored ) {
1248 // no-op
1249 }
1250
1251 $ctx['output'] = $profiler->getOutput();
1252
1253 $log = LoggerFactory::getInstance( 'profileoutput' );
1254 $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1255}
1256
1264function wfIncrStats( $key, $count = 1 ) {
1265 $stats = RequestContext::getMain()->getStats();
1266 $stats->updateCount( $key, $count );
1267}
1268
1274function wfReadOnly() {
1275 return wfReadOnlyReason() !== false;
1276}
1277
1287 $readOnly = wfConfiguredReadOnlyReason();
1288 if ( $readOnly !== false ) {
1289 return $readOnly;
1290 }
1291
1292 static $lbReadOnly = null;
1293 if ( $lbReadOnly === null ) {
1294 // Callers use this method to be aware that data presented to a user
1295 // may be very stale and thus allowing submissions can be problematic.
1296 $lbReadOnly = wfGetLB()->getReadOnlyReason();
1297 }
1298
1299 return $lbReadOnly;
1300}
1301
1310
1311 if ( $wgReadOnly === null ) {
1312 // Set $wgReadOnly for faster access next time
1313 if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
1314 $wgReadOnly = file_get_contents( $wgReadOnlyFile );
1315 } else {
1316 $wgReadOnly = false;
1317 }
1318 }
1319
1320 return $wgReadOnly;
1321}
1322
1338function wfGetLangObj( $langcode = false ) {
1339 # Identify which language to get or create a language object for.
1340 # Using is_object here due to Stub objects.
1341 if ( is_object( $langcode ) ) {
1342 # Great, we already have the object (hopefully)!
1343 return $langcode;
1344 }
1345
1347 if ( $langcode === true || $langcode === $wgLanguageCode ) {
1348 # $langcode is the language code of the wikis content language object.
1349 # or it is a boolean and value is true
1350 return $wgContLang;
1351 }
1352
1354 if ( $langcode === false || $langcode === $wgLang->getCode() ) {
1355 # $langcode is the language code of user language object.
1356 # or it was a boolean and value is false
1357 return $wgLang;
1358 }
1359
1360 $validCodes = array_keys( Language::fetchLanguageNames() );
1361 if ( in_array( $langcode, $validCodes ) ) {
1362 # $langcode corresponds to a valid language.
1363 return Language::factory( $langcode );
1364 }
1365
1366 # $langcode is a string, but not a valid language code; use content language.
1367 wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1368 return $wgContLang;
1369}
1370
1387function wfMessage( $key /*...*/ ) {
1388 $params = func_get_args();
1389 array_shift( $params );
1390 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
1391 $params = $params[0];
1392 }
1393 return new Message( $key, $params );
1394}
1395
1408function wfMessageFallback( /*...*/ ) {
1409 $args = func_get_args();
1410 return call_user_func_array( 'Message::newFallbackSequence', $args );
1411}
1412
1421function wfMsgReplaceArgs( $message, $args ) {
1422 # Fix windows line-endings
1423 # Some messages are split with explode("\n", $msg)
1424 $message = str_replace( "\r", '', $message );
1425
1426 // Replace arguments
1427 if ( is_array( $args ) && $args ) {
1428 if ( is_array( $args[0] ) ) {
1429 $args = array_values( $args[0] );
1430 }
1431 $replacementKeys = [];
1432 foreach ( $args as $n => $param ) {
1433 $replacementKeys['$' . ( $n + 1 )] = $param;
1434 }
1435 $message = strtr( $message, $replacementKeys );
1436 }
1437
1438 return $message;
1439}
1440
1448function wfHostname() {
1449 static $host;
1450 if ( is_null( $host ) ) {
1451
1452 # Hostname overriding
1454 if ( $wgOverrideHostname !== false ) {
1455 # Set static and skip any detection
1456 $host = $wgOverrideHostname;
1457 return $host;
1458 }
1459
1460 if ( function_exists( 'posix_uname' ) ) {
1461 // This function not present on Windows
1462 $uname = posix_uname();
1463 } else {
1464 $uname = false;
1465 }
1466 if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
1467 $host = $uname['nodename'];
1468 } elseif ( getenv( 'COMPUTERNAME' ) ) {
1469 # Windows computer name
1470 $host = getenv( 'COMPUTERNAME' );
1471 } else {
1472 # This may be a virtual server.
1473 $host = $_SERVER['SERVER_NAME'];
1474 }
1475 }
1476 return $host;
1477}
1478
1488function wfReportTime() {
1490
1491 $responseTime = round( ( microtime( true ) - $wgRequestTime ) * 1000 );
1492 $reportVars = [ 'wgBackendResponseTime' => $responseTime ];
1493 if ( $wgShowHostnames ) {
1494 $reportVars['wgHostname'] = wfHostname();
1495 }
1496 return Skin::makeVariablesScript( $reportVars );
1497}
1498
1509function wfDebugBacktrace( $limit = 0 ) {
1510 static $disabled = null;
1511
1512 if ( is_null( $disabled ) ) {
1513 $disabled = !function_exists( 'debug_backtrace' );
1514 if ( $disabled ) {
1515 wfDebug( "debug_backtrace() is disabled\n" );
1516 }
1517 }
1518 if ( $disabled ) {
1519 return [];
1520 }
1521
1522 if ( $limit ) {
1523 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1524 } else {
1525 return array_slice( debug_backtrace(), 1 );
1526 }
1527}
1528
1537function wfBacktrace( $raw = null ) {
1539
1540 if ( $raw === null ) {
1541 $raw = $wgCommandLineMode;
1542 }
1543
1544 if ( $raw ) {
1545 $frameFormat = "%s line %s calls %s()\n";
1546 $traceFormat = "%s";
1547 } else {
1548 $frameFormat = "<li>%s line %s calls %s()</li>\n";
1549 $traceFormat = "<ul>\n%s</ul>\n";
1550 }
1551
1552 $frames = array_map( function ( $frame ) use ( $frameFormat ) {
1553 $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
1554 $line = isset( $frame['line'] ) ? $frame['line'] : '-';
1555 $call = $frame['function'];
1556 if ( !empty( $frame['class'] ) ) {
1557 $call = $frame['class'] . $frame['type'] . $call;
1558 }
1559 return sprintf( $frameFormat, $file, $line, $call );
1560 }, wfDebugBacktrace() );
1561
1562 return sprintf( $traceFormat, implode( '', $frames ) );
1563}
1564
1574function wfGetCaller( $level = 2 ) {
1575 $backtrace = wfDebugBacktrace( $level + 1 );
1576 if ( isset( $backtrace[$level] ) ) {
1577 return wfFormatStackFrame( $backtrace[$level] );
1578 } else {
1579 return 'unknown';
1580 }
1581}
1582
1590function wfGetAllCallers( $limit = 3 ) {
1591 $trace = array_reverse( wfDebugBacktrace() );
1592 if ( !$limit || $limit > count( $trace ) - 1 ) {
1593 $limit = count( $trace ) - 1;
1594 }
1595 $trace = array_slice( $trace, -$limit - 1, $limit );
1596 return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
1597}
1598
1605function wfFormatStackFrame( $frame ) {
1606 if ( !isset( $frame['function'] ) ) {
1607 return 'NO_FUNCTION_GIVEN';
1608 }
1609 return isset( $frame['class'] ) && isset( $frame['type'] ) ?
1610 $frame['class'] . $frame['type'] . $frame['function'] :
1611 $frame['function'];
1612}
1613
1614/* Some generic result counters, pulled out of SearchEngine */
1615
1623function wfShowingResults( $offset, $limit ) {
1624 return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1625}
1626
1634function wfClientAcceptsGzip( $force = false ) {
1635 static $result = null;
1636 if ( $result === null || $force ) {
1637 $result = false;
1638 if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
1639 # @todo FIXME: We may want to blacklist some broken browsers
1640 $m = [];
1641 if ( preg_match(
1642 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1643 $_SERVER['HTTP_ACCEPT_ENCODING'],
1644 $m
1645 )
1646 ) {
1647 if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
1648 $result = false;
1649 return $result;
1650 }
1651 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
1652 $result = true;
1653 }
1654 }
1655 }
1656 return $result;
1657}
1658
1668function wfEscapeWikiText( $text ) {
1670 static $repl = null, $repl2 = null;
1671 if ( $repl === null || defined( 'MW_PARSER_TEST' ) || defined( 'MW_PHPUNIT_TEST' ) ) {
1672 // Tests depend upon being able to change $wgEnableMagicLinks, so don't cache
1673 // in those situations
1674 $repl = [
1675 '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
1676 '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
1677 '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
1678 "\n#" => "\n&#35;", "\r#" => "\r&#35;",
1679 "\n*" => "\n&#42;", "\r*" => "\r&#42;",
1680 "\n:" => "\n&#58;", "\r:" => "\r&#58;",
1681 "\n " => "\n&#32;", "\r " => "\r&#32;",
1682 "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
1683 "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
1684 "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
1685 "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
1686 '__' => '_&#95;', '://' => '&#58;//',
1687 ];
1688
1689 $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
1690 // We have to catch everything "\s" matches in PCRE
1691 foreach ( $magicLinks as $magic ) {
1692 $repl["$magic "] = "$magic&#32;";
1693 $repl["$magic\t"] = "$magic&#9;";
1694 $repl["$magic\r"] = "$magic&#13;";
1695 $repl["$magic\n"] = "$magic&#10;";
1696 $repl["$magic\f"] = "$magic&#12;";
1697 }
1698
1699 // And handle protocols that don't use "://"
1701 $repl2 = [];
1702 foreach ( $wgUrlProtocols as $prot ) {
1703 if ( substr( $prot, -1 ) === ':' ) {
1704 $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
1705 }
1706 }
1707 $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/';
1708 }
1709 $text = substr( strtr( "\n$text", $repl ), 1 );
1710 $text = preg_replace( $repl2, '$1&#58;', $text );
1711 return $text;
1712}
1713
1724function wfSetVar( &$dest, $source, $force = false ) {
1725 $temp = $dest;
1726 if ( !is_null( $source ) || $force ) {
1727 $dest = $source;
1728 }
1729 return $temp;
1730}
1731
1741function wfSetBit( &$dest, $bit, $state = true ) {
1742 $temp = (bool)( $dest & $bit );
1743 if ( !is_null( $state ) ) {
1744 if ( $state ) {
1745 $dest |= $bit;
1746 } else {
1747 $dest &= ~$bit;
1748 }
1749 }
1750 return $temp;
1751}
1752
1759function wfVarDump( $var ) {
1760 global $wgOut;
1761 $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
1762 if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
1763 print $s;
1764 } else {
1765 $wgOut->addHTML( $s );
1766 }
1767}
1768
1776function wfHttpError( $code, $label, $desc ) {
1777 global $wgOut;
1779 if ( $wgOut ) {
1780 $wgOut->disable();
1781 $wgOut->sendCacheControl();
1782 }
1783
1784 header( 'Content-type: text/html; charset=utf-8' );
1785 print '<!DOCTYPE html>' .
1786 '<html><head><title>' .
1787 htmlspecialchars( $label ) .
1788 '</title></head><body><h1>' .
1789 htmlspecialchars( $label ) .
1790 '</h1><p>' .
1791 nl2br( htmlspecialchars( $desc ) ) .
1792 "</p></body></html>\n";
1793}
1794
1812function wfResetOutputBuffers( $resetGzipEncoding = true ) {
1813 if ( $resetGzipEncoding ) {
1814 // Suppress Content-Encoding and Content-Length
1815 // headers from 1.10+s wfOutputHandler
1818 }
1819 while ( $status = ob_get_status() ) {
1820 if ( isset( $status['flags'] ) ) {
1821 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1822 $deleteable = ( $status['flags'] & $flags ) === $flags;
1823 } elseif ( isset( $status['del'] ) ) {
1824 $deleteable = $status['del'];
1825 } else {
1826 // Guess that any PHP-internal setting can't be removed.
1827 $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
1828 }
1829 if ( !$deleteable ) {
1830 // Give up, and hope the result doesn't break
1831 // output behavior.
1832 break;
1833 }
1834 if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1835 // Unit testing barrier to prevent this function from breaking PHPUnit.
1836 break;
1837 }
1838 if ( !ob_end_clean() ) {
1839 // Could not remove output buffer handler; abort now
1840 // to avoid getting in some kind of infinite loop.
1841 break;
1842 }
1843 if ( $resetGzipEncoding ) {
1844 if ( $status['name'] == 'ob_gzhandler' ) {
1845 // Reset the 'Content-Encoding' field set by this handler
1846 // so we can start fresh.
1847 header_remove( 'Content-Encoding' );
1848 break;
1849 }
1850 }
1851 }
1852}
1853
1867 wfResetOutputBuffers( false );
1868}
1869
1878function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1879 # No arg means accept anything (per HTTP spec)
1880 if ( !$accept ) {
1881 return [ $def => 1.0 ];
1882 }
1883
1884 $prefs = [];
1885
1886 $parts = explode( ',', $accept );
1887
1888 foreach ( $parts as $part ) {
1889 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1890 $values = explode( ';', trim( $part ) );
1891 $match = [];
1892 if ( count( $values ) == 1 ) {
1893 $prefs[$values[0]] = 1.0;
1894 } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1895 $prefs[$values[0]] = floatval( $match[1] );
1896 }
1897 }
1898
1899 return $prefs;
1900}
1901
1914function mimeTypeMatch( $type, $avail ) {
1915 if ( array_key_exists( $type, $avail ) ) {
1916 return $type;
1917 } else {
1918 $mainType = explode( '/', $type )[0];
1919 if ( array_key_exists( "$mainType/*", $avail ) ) {
1920 return "$mainType/*";
1921 } elseif ( array_key_exists( '*/*', $avail ) ) {
1922 return '*/*';
1923 } else {
1924 return null;
1925 }
1926 }
1927}
1928
1942function wfNegotiateType( $cprefs, $sprefs ) {
1943 $combine = [];
1944
1945 foreach ( array_keys( $sprefs ) as $type ) {
1946 $subType = explode( '/', $type )[1];
1947 if ( $subType != '*' ) {
1948 $ckey = mimeTypeMatch( $type, $cprefs );
1949 if ( $ckey ) {
1950 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1951 }
1952 }
1953 }
1954
1955 foreach ( array_keys( $cprefs ) as $type ) {
1956 $subType = explode( '/', $type )[1];
1957 if ( $subType != '*' && !array_key_exists( $type, $sprefs ) ) {
1958 $skey = mimeTypeMatch( $type, $sprefs );
1959 if ( $skey ) {
1960 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1961 }
1962 }
1963 }
1964
1965 $bestq = 0;
1966 $besttype = null;
1967
1968 foreach ( array_keys( $combine ) as $type ) {
1969 if ( $combine[$type] > $bestq ) {
1970 $besttype = $type;
1971 $bestq = $combine[$type];
1972 }
1973 }
1974
1975 return $besttype;
1976}
1977
1984function wfSuppressWarnings( $end = false ) {
1985 MediaWiki\suppressWarnings( $end );
1986}
1987
1993 MediaWiki\suppressWarnings( true );
1994}
1995
1996# Autodetect, convert and provide timestamps of various types
1997
1998require_once __DIR__ . '/libs/time/defines.php';
1999
2008function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
2009 $ret = MWTimestamp::convert( $outputtype, $ts );
2010 if ( $ret === false ) {
2011 wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
2012 }
2013 return $ret;
2014}
2015
2024function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
2025 if ( is_null( $ts ) ) {
2026 return null;
2027 } else {
2028 return wfTimestamp( $outputtype, $ts );
2029 }
2030}
2031
2037function wfTimestampNow() {
2038 # return NOW
2039 return MWTimestamp::now( TS_MW );
2040}
2041
2047function wfIsWindows() {
2048 static $isWindows = null;
2049 if ( $isWindows === null ) {
2050 $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
2051 }
2052 return $isWindows;
2053}
2054
2060function wfIsHHVM() {
2061 return defined( 'HHVM_VERSION' );
2062}
2063
2075function wfTempDir() {
2077
2078 if ( $wgTmpDirectory !== false ) {
2079 return $wgTmpDirectory;
2080 }
2081
2083}
2084
2094function wfMkdirParents( $dir, $mode = null, $caller = null ) {
2096
2097 if ( FileBackend::isStoragePath( $dir ) ) { // sanity
2098 throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
2099 }
2100
2101 if ( !is_null( $caller ) ) {
2102 wfDebug( "$caller: called wfMkdirParents($dir)\n" );
2103 }
2104
2105 if ( strval( $dir ) === '' || is_dir( $dir ) ) {
2106 return true;
2107 }
2108
2109 $dir = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $dir );
2110
2111 if ( is_null( $mode ) ) {
2112 $mode = $wgDirectoryMode;
2113 }
2114
2115 // Turn off the normal warning, we're doing our own below
2116 MediaWiki\suppressWarnings();
2117 $ok = mkdir( $dir, $mode, true ); // PHP5 <3
2118 MediaWiki\restoreWarnings();
2119
2120 if ( !$ok ) {
2121 // directory may have been created on another request since we last checked
2122 if ( is_dir( $dir ) ) {
2123 return true;
2124 }
2125
2126 // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
2127 wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
2128 }
2129 return $ok;
2130}
2131
2138 wfDebug( __FUNCTION__ . "( $dir )\n" );
2139 // taken from http://de3.php.net/manual/en/function.rmdir.php#98622
2140 if ( is_dir( $dir ) ) {
2141 $objects = scandir( $dir );
2142 foreach ( $objects as $object ) {
2143 if ( $object != "." && $object != ".." ) {
2144 if ( filetype( $dir . '/' . $object ) == "dir" ) {
2145 wfRecursiveRemoveDir( $dir . '/' . $object );
2146 } else {
2147 unlink( $dir . '/' . $object );
2148 }
2149 }
2150 }
2151 reset( $objects );
2152 rmdir( $dir );
2153 }
2154}
2155
2162function wfPercent( $nr, $acc = 2, $round = true ) {
2163 $ret = sprintf( "%.${acc}f", $nr );
2164 return $round ? round( $ret, $acc ) . '%' : "$ret%";
2165}
2166
2190function wfIniGetBool( $setting ) {
2191 $val = strtolower( ini_get( $setting ) );
2192 // 'on' and 'true' can't have whitespace around them, but '1' can.
2193 return $val == 'on'
2194 || $val == 'true'
2195 || $val == 'yes'
2196 || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
2197}
2198
2209function wfEscapeShellArg( /*...*/ ) {
2211
2212 $args = func_get_args();
2213 if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
2214 // If only one argument has been passed, and that argument is an array,
2215 // treat it as a list of arguments
2216 $args = reset( $args );
2217 }
2218
2219 $first = true;
2220 $retVal = '';
2221 foreach ( $args as $arg ) {
2222 if ( !$first ) {
2223 $retVal .= ' ';
2224 } else {
2225 $first = false;
2226 }
2227
2228 if ( wfIsWindows() ) {
2229 // Escaping for an MSVC-style command line parser and CMD.EXE
2230 // @codingStandardsIgnoreStart For long URLs
2231 // Refs:
2232 // * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
2233 // * http://technet.microsoft.com/en-us/library/cc723564.aspx
2234 // * T15518
2235 // * CR r63214
2236 // Double the backslashes before any double quotes. Escape the double quotes.
2237 // @codingStandardsIgnoreEnd
2238 $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
2239 $arg = '';
2240 $iteration = 0;
2241 foreach ( $tokens as $token ) {
2242 if ( $iteration % 2 == 1 ) {
2243 // Delimiter, a double quote preceded by zero or more slashes
2244 $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
2245 } elseif ( $iteration % 4 == 2 ) {
2246 // ^ in $token will be outside quotes, need to be escaped
2247 $arg .= str_replace( '^', '^^', $token );
2248 } else { // $iteration % 4 == 0
2249 // ^ in $token will appear inside double quotes, so leave as is
2250 $arg .= $token;
2251 }
2252 $iteration++;
2253 }
2254 // Double the backslashes before the end of the string, because
2255 // we will soon add a quote
2256 $m = [];
2257 if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
2258 $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
2259 }
2260
2261 // Add surrounding quotes
2262 $retVal .= '"' . $arg . '"';
2263 } else {
2264 $retVal .= escapeshellarg( $arg );
2265 }
2266 }
2267 return $retVal;
2268}
2269
2277 static $disabled = null;
2278 if ( is_null( $disabled ) ) {
2279 if ( !function_exists( 'proc_open' ) ) {
2280 wfDebug( "proc_open() is disabled\n" );
2281 $disabled = 'disabled';
2282 } else {
2283 $disabled = false;
2284 }
2285 }
2286 return $disabled;
2287}
2288
2311function wfShellExec( $cmd, &$retval = null, $environ = [],
2312 $limits = [], $options = []
2313) {
2316
2317 $disabled = wfShellExecDisabled();
2318 if ( $disabled ) {
2319 $retval = 1;
2320 return 'Unable to run external programs, proc_open() is disabled.';
2321 }
2322
2323 $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
2324 $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
2325
2327
2328 $envcmd = '';
2329 foreach ( $environ as $k => $v ) {
2330 if ( wfIsWindows() ) {
2331 /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
2332 * appear in the environment variable, so we must use carat escaping as documented in
2333 * http://technet.microsoft.com/en-us/library/cc723564.aspx
2334 * Note however that the quote isn't listed there, but is needed, and the parentheses
2335 * are listed there but doesn't appear to need it.
2336 */
2337 $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& ';
2338 } else {
2339 /* Assume this is a POSIX shell, thus required to accept variable assignments before the command
2340 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01
2341 */
2342 $envcmd .= "$k=" . escapeshellarg( $v ) . ' ';
2343 }
2344 }
2345 if ( is_array( $cmd ) ) {
2346 $cmd = wfEscapeShellArg( $cmd );
2347 }
2348
2349 $cmd = $envcmd . $cmd;
2350
2351 $useLogPipe = false;
2352 if ( is_executable( '/bin/bash' ) ) {
2353 $time = intval( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
2354 if ( isset( $limits['walltime'] ) ) {
2355 $wallTime = intval( $limits['walltime'] );
2356 } elseif ( isset( $limits['time'] ) ) {
2357 $wallTime = $time;
2358 } else {
2359 $wallTime = intval( $wgMaxShellWallClockTime );
2360 }
2361 $mem = intval( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory );
2362 $filesize = intval( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
2363
2364 if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
2365 $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
2366 escapeshellarg( $cmd ) . ' ' .
2367 escapeshellarg(
2368 "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' .
2369 "MW_CPU_LIMIT=$time; " .
2370 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' .
2371 "MW_MEM_LIMIT=$mem; " .
2372 "MW_FILE_SIZE_LIMIT=$filesize; " .
2373 "MW_WALL_CLOCK_LIMIT=$wallTime; " .
2374 "MW_USE_LOG_PIPE=yes"
2375 );
2376 $useLogPipe = true;
2377 } elseif ( $includeStderr ) {
2378 $cmd .= ' 2>&1';
2379 }
2380 } elseif ( $includeStderr ) {
2381 $cmd .= ' 2>&1';
2382 }
2383 wfDebug( "wfShellExec: $cmd\n" );
2384
2385 // Don't try to execute commands that exceed Linux's MAX_ARG_STRLEN.
2386 // Other platforms may be more accomodating, but we don't want to be
2387 // accomodating, because very long commands probably include user
2388 // input. See T129506.
2389 if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) {
2390 throw new Exception( __METHOD__ .
2391 '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
2392 }
2393
2394 $desc = [
2395 0 => [ 'file', 'php://stdin', 'r' ],
2396 1 => [ 'pipe', 'w' ],
2397 2 => [ 'file', 'php://stderr', 'w' ] ];
2398 if ( $useLogPipe ) {
2399 $desc[3] = [ 'pipe', 'w' ];
2400 }
2401 $pipes = null;
2402 $scoped = Profiler::instance()->scopedProfileIn( __FUNCTION__ . '-' . $profileMethod );
2403 $proc = proc_open( $cmd, $desc, $pipes );
2404 if ( !$proc ) {
2405 wfDebugLog( 'exec', "proc_open() failed: $cmd" );
2406 $retval = -1;
2407 return '';
2408 }
2409 $outBuffer = $logBuffer = '';
2410 $emptyArray = [];
2411 $status = false;
2412 $logMsg = false;
2413
2414 /* According to the documentation, it is possible for stream_select()
2415 * to fail due to EINTR. I haven't managed to induce this in testing
2416 * despite sending various signals. If it did happen, the error
2417 * message would take the form:
2418 *
2419 * stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
2420 *
2421 * where [4] is the value of the macro EINTR and "Interrupted system
2422 * call" is string which according to the Linux manual is "possibly"
2423 * localised according to LC_MESSAGES.
2424 */
2425 $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
2426 $eintrMessage = "stream_select(): unable to select [$eintr]";
2427
2428 $running = true;
2429 $timeout = null;
2430 $numReadyPipes = 0;
2431
2432 while ( $running === true || $numReadyPipes !== 0 ) {
2433 if ( $running ) {
2434 $status = proc_get_status( $proc );
2435 // If the process has terminated, switch to nonblocking selects
2436 // for getting any data still waiting to be read.
2437 if ( !$status['running'] ) {
2438 $running = false;
2439 $timeout = 0;
2440 }
2441 }
2442
2443 $readyPipes = $pipes;
2444
2445 // Clear last error
2446 // @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged
2447 @trigger_error( '' );
2448 $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout );
2449 if ( $numReadyPipes === false ) {
2450 // @codingStandardsIgnoreEnd
2451 $error = error_get_last();
2452 if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
2453 continue;
2454 } else {
2455 trigger_error( $error['message'], E_USER_WARNING );
2456 $logMsg = $error['message'];
2457 break;
2458 }
2459 }
2460 foreach ( $readyPipes as $fd => $pipe ) {
2461 $block = fread( $pipe, 65536 );
2462 if ( $block === '' ) {
2463 // End of file
2464 fclose( $pipes[$fd] );
2465 unset( $pipes[$fd] );
2466 if ( !$pipes ) {
2467 break 2;
2468 }
2469 } elseif ( $block === false ) {
2470 // Read error
2471 $logMsg = "Error reading from pipe";
2472 break 2;
2473 } elseif ( $fd == 1 ) {
2474 // From stdout
2475 $outBuffer .= $block;
2476 } elseif ( $fd == 3 ) {
2477 // From log FD
2478 $logBuffer .= $block;
2479 if ( strpos( $block, "\n" ) !== false ) {
2480 $lines = explode( "\n", $logBuffer );
2481 $logBuffer = array_pop( $lines );
2482 foreach ( $lines as $line ) {
2483 wfDebugLog( 'exec', $line );
2484 }
2485 }
2486 }
2487 }
2488 }
2489
2490 foreach ( $pipes as $pipe ) {
2491 fclose( $pipe );
2492 }
2493
2494 // Use the status previously collected if possible, since proc_get_status()
2495 // just calls waitpid() which will not return anything useful the second time.
2496 if ( $running ) {
2497 $status = proc_get_status( $proc );
2498 }
2499
2500 if ( $logMsg !== false ) {
2501 // Read/select error
2502 $retval = -1;
2503 proc_close( $proc );
2504 } elseif ( $status['signaled'] ) {
2505 $logMsg = "Exited with signal {$status['termsig']}";
2506 $retval = 128 + $status['termsig'];
2507 proc_close( $proc );
2508 } else {
2509 if ( $status['running'] ) {
2510 $retval = proc_close( $proc );
2511 } else {
2512 $retval = $status['exitcode'];
2513 proc_close( $proc );
2514 }
2515 if ( $retval == 127 ) {
2516 $logMsg = "Possibly missing executable file";
2517 } elseif ( $retval >= 129 && $retval <= 192 ) {
2518 $logMsg = "Probably exited with signal " . ( $retval - 128 );
2519 }
2520 }
2521
2522 if ( $logMsg !== false ) {
2523 wfDebugLog( 'exec', "$logMsg: $cmd" );
2524 }
2525
2526 return $outBuffer;
2527}
2528
2545function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
2546 return wfShellExec( $cmd, $retval, $environ, $limits,
2547 [ 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ] );
2548}
2549
2555 static $done = false;
2556 if ( $done ) {
2557 return;
2558 }
2559 $done = true;
2561 putenv( "LC_CTYPE=$wgShellLocale" );
2562 setlocale( LC_CTYPE, $wgShellLocale );
2563}
2564
2577function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
2579 // Give site config file a chance to run the script in a wrapper.
2580 // The caller may likely want to call wfBasename() on $script.
2581 Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2582 $cmd = isset( $options['php'] ) ? [ $options['php'] ] : [ $wgPhpCli ];
2583 if ( isset( $options['wrapper'] ) ) {
2584 $cmd[] = $options['wrapper'];
2585 }
2586 $cmd[] = $script;
2587 // Escape each parameter for shell
2588 return wfEscapeShellArg( array_merge( $cmd, $parameters ) );
2589}
2590
2601function wfMerge( $old, $mine, $yours, &$result ) {
2603
2604 # This check may also protect against code injection in
2605 # case of broken installations.
2606 MediaWiki\suppressWarnings();
2607 $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2608 MediaWiki\restoreWarnings();
2609
2610 if ( !$haveDiff3 ) {
2611 wfDebug( "diff3 not found\n" );
2612 return false;
2613 }
2614
2615 # Make temporary files
2616 $td = wfTempDir();
2617 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2618 $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
2619 $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
2620
2621 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2622 # a newline character. To avoid this, we normalize the trailing whitespace before
2623 # creating the diff.
2624
2625 fwrite( $oldtextFile, rtrim( $old ) . "\n" );
2626 fclose( $oldtextFile );
2627 fwrite( $mytextFile, rtrim( $mine ) . "\n" );
2628 fclose( $mytextFile );
2629 fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
2630 fclose( $yourtextFile );
2631
2632 # Check for a conflict
2633 $cmd = wfEscapeShellArg( $wgDiff3, '-a', '--overlap-only', $mytextName,
2634 $oldtextName, $yourtextName );
2635 $handle = popen( $cmd, 'r' );
2636
2637 if ( fgets( $handle, 1024 ) ) {
2638 $conflict = true;
2639 } else {
2640 $conflict = false;
2641 }
2642 pclose( $handle );
2643
2644 # Merge differences
2645 $cmd = wfEscapeShellArg( $wgDiff3, '-a', '-e', '--merge', $mytextName,
2646 $oldtextName, $yourtextName );
2647 $handle = popen( $cmd, 'r' );
2648 $result = '';
2649 do {
2650 $data = fread( $handle, 8192 );
2651 if ( strlen( $data ) == 0 ) {
2652 break;
2653 }
2654 $result .= $data;
2655 } while ( true );
2656 pclose( $handle );
2657 unlink( $mytextName );
2658 unlink( $oldtextName );
2659 unlink( $yourtextName );
2660
2661 if ( $result === '' && $old !== '' && !$conflict ) {
2662 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
2663 $conflict = true;
2664 }
2665 return !$conflict;
2666}
2667
2679function wfDiff( $before, $after, $params = '-u' ) {
2680 if ( $before == $after ) {
2681 return '';
2682 }
2683
2685 MediaWiki\suppressWarnings();
2686 $haveDiff = $wgDiff && file_exists( $wgDiff );
2687 MediaWiki\restoreWarnings();
2688
2689 # This check may also protect against code injection in
2690 # case of broken installations.
2691 if ( !$haveDiff ) {
2692 wfDebug( "diff executable not found\n" );
2693 $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
2694 $format = new UnifiedDiffFormatter();
2695 return $format->format( $diffs );
2696 }
2697
2698 # Make temporary files
2699 $td = wfTempDir();
2700 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2701 $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
2702
2703 fwrite( $oldtextFile, $before );
2704 fclose( $oldtextFile );
2705 fwrite( $newtextFile, $after );
2706 fclose( $newtextFile );
2707
2708 // Get the diff of the two files
2709 $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
2710
2711 $h = popen( $cmd, 'r' );
2712 if ( !$h ) {
2713 unlink( $oldtextName );
2714 unlink( $newtextName );
2715 throw new Exception( __METHOD__ . '(): popen() failed' );
2716 }
2717
2718 $diff = '';
2719
2720 do {
2721 $data = fread( $h, 8192 );
2722 if ( strlen( $data ) == 0 ) {
2723 break;
2724 }
2725 $diff .= $data;
2726 } while ( true );
2727
2728 // Clean up
2729 pclose( $h );
2730 unlink( $oldtextName );
2731 unlink( $newtextName );
2732
2733 // Kill the --- and +++ lines. They're not useful.
2734 $diff_lines = explode( "\n", $diff );
2735 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
2736 unset( $diff_lines[0] );
2737 }
2738 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
2739 unset( $diff_lines[1] );
2740 }
2741
2742 $diff = implode( "\n", $diff_lines );
2743
2744 return $diff;
2745}
2746
2762function wfUsePHP( $req_ver ) {
2763 $php_ver = PHP_VERSION;
2764
2765 if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
2766 throw new MWException( "PHP $req_ver required--this is only $php_ver" );
2767 }
2768}
2769
2792function wfUseMW( $req_ver ) {
2794
2795 if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
2796 throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
2797 }
2798}
2799
2812function wfBaseName( $path, $suffix = '' ) {
2813 if ( $suffix == '' ) {
2814 $encSuffix = '';
2815 } else {
2816 $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
2817 }
2818
2819 $matches = [];
2820 if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
2821 return $matches[1];
2822 } else {
2823 return '';
2824 }
2825}
2826
2836function wfRelativePath( $path, $from ) {
2837 // Normalize mixed input on Windows...
2838 $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
2839 $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
2840
2841 // Trim trailing slashes -- fix for drive root
2842 $path = rtrim( $path, DIRECTORY_SEPARATOR );
2843 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2844
2845 $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
2846 $against = explode( DIRECTORY_SEPARATOR, $from );
2847
2848 if ( $pieces[0] !== $against[0] ) {
2849 // Non-matching Windows drive letters?
2850 // Return a full path.
2851 return $path;
2852 }
2853
2854 // Trim off common prefix
2855 while ( count( $pieces ) && count( $against )
2856 && $pieces[0] == $against[0] ) {
2857 array_shift( $pieces );
2858 array_shift( $against );
2859 }
2860
2861 // relative dots to bump us to the parent
2862 while ( count( $against ) ) {
2863 array_unshift( $pieces, '..' );
2864 array_shift( $against );
2865 }
2866
2867 array_push( $pieces, wfBaseName( $path ) );
2868
2869 return implode( DIRECTORY_SEPARATOR, $pieces );
2870}
2871
2889function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
2890 $lowercase = true, $engine = 'auto'
2891) {
2892 return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
2893}
2894
2899function wfFixSessionID() {
2900 wfDeprecated( __FUNCTION__, '1.27' );
2901}
2902
2910 wfDeprecated( __FUNCTION__, '1.27' );
2911 $session = SessionManager::getGlobalSession();
2912 $delay = $session->delaySave();
2913
2914 $session->resetId();
2915
2916 // Make sure a session is started, since that's what the old
2917 // wfResetSessionID() did.
2918 if ( session_id() !== $session->getId() ) {
2919 wfSetupSession( $session->getId() );
2920 }
2921
2922 ScopedCallback::consume( $delay );
2923}
2924
2934function wfSetupSession( $sessionId = false ) {
2935 wfDeprecated( __FUNCTION__, '1.27' );
2936
2937 if ( $sessionId ) {
2938 session_id( $sessionId );
2939 }
2940
2941 $session = SessionManager::getGlobalSession();
2942 $session->persist();
2943
2944 if ( session_id() !== $session->getId() ) {
2945 session_id( $session->getId() );
2946 }
2947 MediaWiki\quietCall( 'session_start' );
2948}
2949
2956function wfGetPrecompiledData( $name ) {
2957 global $IP;
2958
2959 $file = "$IP/serialized/$name";
2960 if ( file_exists( $file ) ) {
2961 $blob = file_get_contents( $file );
2962 if ( $blob ) {
2963 return unserialize( $blob );
2964 }
2965 }
2966 return false;
2967}
2968
2975function wfMemcKey( /*...*/ ) {
2976 return call_user_func_array(
2977 [ ObjectCache::getLocalClusterInstance(), 'makeKey' ],
2978 func_get_args()
2979 );
2980}
2981
2992function wfForeignMemcKey( $db, $prefix /*...*/ ) {
2993 $args = array_slice( func_get_args(), 2 );
2994 $keyspace = $prefix ? "$db-$prefix" : $db;
2995 return call_user_func_array(
2996 [ ObjectCache::getLocalClusterInstance(), 'makeKeyInternal' ],
2997 [ $keyspace, $args ]
2998 );
2999}
3000
3012function wfGlobalCacheKey( /*...*/ ) {
3013 return call_user_func_array(
3014 [ ObjectCache::getLocalClusterInstance(), 'makeGlobalKey' ],
3015 func_get_args()
3016 );
3017}
3018
3025function wfWikiID() {
3027 if ( $wgDBprefix ) {
3028 return "$wgDBname-$wgDBprefix";
3029 } else {
3030 return $wgDBname;
3031 }
3032}
3033
3041function wfSplitWikiID( $wiki ) {
3042 $bits = explode( '-', $wiki, 2 );
3043 if ( count( $bits ) < 2 ) {
3044 $bits[] = '';
3045 }
3046 return $bits;
3047}
3048
3074function wfGetDB( $db, $groups = [], $wiki = false ) {
3075 return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
3076}
3077
3087function wfGetLB( $wiki = false ) {
3088 if ( $wiki === false ) {
3089 return \MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancer();
3090 } else {
3091 $factory = \MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3092 return $factory->getMainLB( $wiki );
3093 }
3094}
3095
3103function wfGetLBFactory() {
3104 return \MediaWiki\MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3105}
3106
3115function wfFindFile( $title, $options = [] ) {
3116 return RepoGroup::singleton()->findFile( $title, $options );
3117}
3118
3126function wfLocalFile( $title ) {
3127 return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
3128}
3129
3138 return $wgMiserMode
3139 || ( SiteStats::pages() > 100000
3140 && SiteStats::edits() > 1000000
3141 && SiteStats::users() > 10000 );
3142}
3143
3152function wfScript( $script = 'index' ) {
3154 if ( $script === 'index' ) {
3155 return $wgScript;
3156 } elseif ( $script === 'load' ) {
3157 return $wgLoadScript;
3158 } else {
3159 return "{$wgScriptPath}/{$script}.php";
3160 }
3161}
3162
3168function wfGetScriptUrl() {
3169 if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
3170 /* as it was called, minus the query string.
3171 *
3172 * Some sites use Apache rewrite rules to handle subdomains,
3173 * and have PHP set up in a weird way that causes PHP_SELF
3174 * to contain the rewritten URL instead of the one that the
3175 * outside world sees.
3176 *
3177 * If in this mode, use SCRIPT_URL instead, which mod_rewrite
3178 * provides containing the "before" URL.
3179 */
3180 return $_SERVER['SCRIPT_NAME'];
3181 } else {
3182 return $_SERVER['URL'];
3183 }
3184}
3185
3193function wfBoolToStr( $value ) {
3194 return $value ? 'true' : 'false';
3195}
3196
3202function wfGetNull() {
3203 return wfIsWindows() ? 'NUL' : '/dev/null';
3204}
3205
3229 $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
3230) {
3231 if ( $timeout === null ) {
3232 $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
3233 }
3234
3235 if ( $cluster === '*' ) {
3236 $cluster = false;
3237 $wiki = false;
3238 } elseif ( $wiki === false ) {
3239 $wiki = wfWikiID();
3240 }
3241
3242 try {
3243 wfGetLBFactory()->waitForReplication( [
3244 'wiki' => $wiki,
3245 'cluster' => $cluster,
3246 'timeout' => $timeout,
3247 // B/C: first argument used to be "max seconds of lag"; ignore such values
3248 'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
3249 ] );
3250 } catch ( DBReplicationWaitError $e ) {
3251 return false;
3252 }
3253
3254 return true;
3255}
3256
3264function wfCountDown( $seconds ) {
3265 for ( $i = $seconds; $i >= 0; $i-- ) {
3266 if ( $i != $seconds ) {
3267 echo str_repeat( "\x08", strlen( $i + 1 ) );
3268 }
3269 echo $i;
3270 flush();
3271 if ( $i ) {
3272 sleep( 1 );
3273 }
3274 }
3275 echo "\n";
3276}
3277
3288 $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
3289 $name = preg_replace(
3290 "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
3291 '-',
3292 $name
3293 );
3294 // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
3295 $name = wfBaseName( $name );
3296 return $name;
3297}
3298
3304function wfMemoryLimit() {
3306 $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
3307 if ( $memlimit != -1 ) {
3308 $conflimit = wfShorthandToInteger( $wgMemoryLimit );
3309 if ( $conflimit == -1 ) {
3310 wfDebug( "Removing PHP's memory limit\n" );
3311 MediaWiki\suppressWarnings();
3312 ini_set( 'memory_limit', $conflimit );
3313 MediaWiki\restoreWarnings();
3314 return $conflimit;
3315 } elseif ( $conflimit > $memlimit ) {
3316 wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
3317 MediaWiki\suppressWarnings();
3318 ini_set( 'memory_limit', $conflimit );
3319 MediaWiki\restoreWarnings();
3320 return $conflimit;
3321 }
3322 }
3323 return $memlimit;
3324}
3325
3334
3335 $timeLimit = ini_get( 'max_execution_time' );
3336 // Note that CLI scripts use 0
3337 if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
3338 set_time_limit( $wgTransactionalTimeLimit );
3339 }
3340
3341 ignore_user_abort( true ); // ignore client disconnects
3342
3343 return $timeLimit;
3344}
3345
3353function wfShorthandToInteger( $string = '', $default = -1 ) {
3354 $string = trim( $string );
3355 if ( $string === '' ) {
3356 return $default;
3357 }
3358 $last = $string[strlen( $string ) - 1];
3359 $val = intval( $string );
3360 switch ( $last ) {
3361 case 'g':
3362 case 'G':
3363 $val *= 1024;
3364 // break intentionally missing
3365 case 'm':
3366 case 'M':
3367 $val *= 1024;
3368 // break intentionally missing
3369 case 'k':
3370 case 'K':
3371 $val *= 1024;
3372 }
3373
3374 return $val;
3375}
3376
3384function wfBCP47( $code ) {
3385 $codeSegment = explode( '-', $code );
3386 $codeBCP = [];
3387 foreach ( $codeSegment as $segNo => $seg ) {
3388 // when previous segment is x, it is a private segment and should be lc
3389 if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
3390 $codeBCP[$segNo] = strtolower( $seg );
3391 // ISO 3166 country code
3392 } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
3393 $codeBCP[$segNo] = strtoupper( $seg );
3394 // ISO 15924 script code
3395 } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
3396 $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
3397 // Use lowercase for other cases
3398 } else {
3399 $codeBCP[$segNo] = strtolower( $seg );
3400 }
3401 }
3402 $langCode = implode( '-', $codeBCP );
3403 return $langCode;
3404}
3405
3412function wfGetCache( $cacheType ) {
3413 return ObjectCache::getInstance( $cacheType );
3414}
3415
3421function wfGetMainCache() {
3423 return ObjectCache::getInstance( $wgMainCacheType );
3424}
3425
3433 return ObjectCache::getInstance( $wgMessageCacheType );
3434}
3435
3443 return ObjectCache::getInstance( $wgParserCacheType );
3444}
3445
3456function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
3457 return Hooks::run( $event, $args, $deprecatedVersion );
3458}
3459
3474function wfUnpack( $format, $data, $length = false ) {
3475 if ( $length !== false ) {
3476 $realLen = strlen( $data );
3477 if ( $realLen < $length ) {
3478 throw new MWException( "Tried to use wfUnpack on a "
3479 . "string of length $realLen, but needed one "
3480 . "of at least length $length."
3481 );
3482 }
3483 }
3484
3485 MediaWiki\suppressWarnings();
3486 $result = unpack( $format, $data );
3487 MediaWiki\restoreWarnings();
3488
3489 if ( $result === false ) {
3490 // If it cannot extract the packed data.
3491 throw new MWException( "unpack could not unpack binary data" );
3492 }
3493 return $result;
3494}
3495
3510function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
3511 # Handle redirects; callers almost always hit wfFindFile() anyway,
3512 # so just use that method because it has a fast process cache.
3513 $file = wfFindFile( $name ); // get the final name
3514 $name = $file ? $file->getTitle()->getDBkey() : $name;
3515
3516 # Run the extension hook
3517 $bad = false;
3518 if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
3519 return $bad;
3520 }
3521
3522 $cache = ObjectCache::getLocalServerInstance( 'hash' );
3523 $key = wfMemcKey( 'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist ) );
3524 $badImages = $cache->get( $key );
3525
3526 if ( $badImages === false ) { // cache miss
3527 if ( $blacklist === null ) {
3528 $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
3529 }
3530 # Build the list now
3531 $badImages = [];
3532 $lines = explode( "\n", $blacklist );
3533 foreach ( $lines as $line ) {
3534 # List items only
3535 if ( substr( $line, 0, 1 ) !== '*' ) {
3536 continue;
3537 }
3538
3539 # Find all links
3540 $m = [];
3541 if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
3542 continue;
3543 }
3544
3545 $exceptions = [];
3546 $imageDBkey = false;
3547 foreach ( $m[1] as $i => $titleText ) {
3548 $title = Title::newFromText( $titleText );
3549 if ( !is_null( $title ) ) {
3550 if ( $i == 0 ) {
3551 $imageDBkey = $title->getDBkey();
3552 } else {
3553 $exceptions[$title->getPrefixedDBkey()] = true;
3554 }
3555 }
3556 }
3557
3558 if ( $imageDBkey !== false ) {
3559 $badImages[$imageDBkey] = $exceptions;
3560 }
3561 }
3562 $cache->set( $key, $badImages, 60 );
3563 }
3564
3565 $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
3566 $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
3567
3568 return $bad;
3569}
3570
3578function wfCanIPUseHTTPS( $ip ) {
3579 $canDo = true;
3580 Hooks::run( 'CanIPUseHTTPS', [ $ip, &$canDo ] );
3581 return !!$canDo;
3582}
3583
3591function wfIsInfinity( $str ) {
3592 $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
3593 return in_array( $str, $infinityValues );
3594}
3595
3612
3613 $multipliers = [ 1 ];
3614 if ( $wgResponsiveImages ) {
3615 // These available sizes are hardcoded currently elsewhere in MediaWiki.
3616 // @see Linker::processResponsiveImages
3617 $multipliers[] = 1.5;
3618 $multipliers[] = 2;
3619 }
3620
3621 $handler = $file->getHandler();
3622 if ( !$handler || !isset( $params['width'] ) ) {
3623 return false;
3624 }
3625
3626 $basicParams = [];
3627 if ( isset( $params['page'] ) ) {
3628 $basicParams['page'] = $params['page'];
3629 }
3630
3631 $thumbLimits = [];
3632 $imageLimits = [];
3633 // Expand limits to account for multipliers
3634 foreach ( $multipliers as $multiplier ) {
3635 $thumbLimits = array_merge( $thumbLimits, array_map(
3636 function ( $width ) use ( $multiplier ) {
3637 return round( $width * $multiplier );
3638 }, $wgThumbLimits )
3639 );
3640 $imageLimits = array_merge( $imageLimits, array_map(
3641 function ( $pair ) use ( $multiplier ) {
3642 return [
3643 round( $pair[0] * $multiplier ),
3644 round( $pair[1] * $multiplier ),
3645 ];
3646 }, $wgImageLimits )
3647 );
3648 }
3649
3650 // Check if the width matches one of $wgThumbLimits
3651 if ( in_array( $params['width'], $thumbLimits ) ) {
3652 $normalParams = $basicParams + [ 'width' => $params['width'] ];
3653 // Append any default values to the map (e.g. "lossy", "lossless", ...)
3654 $handler->normaliseParams( $file, $normalParams );
3655 } else {
3656 // If not, then check if the width matchs one of $wgImageLimits
3657 $match = false;
3658 foreach ( $imageLimits as $pair ) {
3659 $normalParams = $basicParams + [ 'width' => $pair[0], 'height' => $pair[1] ];
3660 // Decide whether the thumbnail should be scaled on width or height.
3661 // Also append any default values to the map (e.g. "lossy", "lossless", ...)
3662 $handler->normaliseParams( $file, $normalParams );
3663 // Check if this standard thumbnail size maps to the given width
3664 if ( $normalParams['width'] == $params['width'] ) {
3665 $match = true;
3666 break;
3667 }
3668 }
3669 if ( !$match ) {
3670 return false; // not standard for description pages
3671 }
3672 }
3673
3674 // Check that the given values for non-page, non-width, params are just defaults
3675 foreach ( $params as $key => $value ) {
3676 if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3677 return false;
3678 }
3679 }
3680
3681 return true;
3682}
3683
3696function wfArrayPlus2d( array $baseArray, array $newValues ) {
3697 // First merge items that are in both arrays
3698 foreach ( $baseArray as $name => &$groupVal ) {
3699 if ( isset( $newValues[$name] ) ) {
3700 $groupVal += $newValues[$name];
3701 }
3702 }
3703 // Now add items that didn't exist yet
3704 $baseArray += $newValues;
3705
3706 return $baseArray;
3707}
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
unserialize( $serialized)
$wgMaxShellWallClockTime
Maximum wall clock time (i.e.
$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.
$wgMaxShellMemory
Maximum amount of virtual memory available to shell processes under linux, in KB.
$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.
$wgReadOnly
Set this to a string to put the wiki into read-only mode.
$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.
$wgMaxShellFileSize
Maximum file size created by shell processes under linux, in KB ImageMagick convert for example can b...
$wgTmpDirectory
The local filesystem path to a temporary directory.
$wgStyleDirectory
Filesystem stylesheets directory.
$wgShellCgroup
Under Linux: a cgroup directory used to constrain memory usage of shell commands.
$wgTransactionalTimeLimit
The minimum amount of time that MediaWiki needs for "slow" write request, particularly ones with mult...
$wgReadOnlyFile
If this lock file exists (size > 0), the wiki will be forced into read-only mode.
$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.
$wgShellLocale
Locale for LC_CTYPE, to work around http://bugs.php.net/bug.php?id=45132 For Unix-like operating syst...
$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.
$wgMaxShellTime
Maximum CPU time in seconds for shell processes under Linux.
$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)
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.
wfFixSessionID()
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()
Workaround for http://bugs.php.net/bug.php?id=45132 escapeshellarg() destroys non-ASCII characters if...
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...
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.
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:816
global $wgCommandLineMode
Definition Setup.php:495
if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:664
$IP
Definition WebStart.php:58
float $wgRequestTime
Request start time as fractional seconds since epoch.
Definition WebStart.php:43
$line
Definition cdb.php:59
if( $line===false) $args
Definition cdb.php:64
getRequest()
Get the WebRequest object.
static convert( $style=TS_UNIX, $ts)
Convert a timestamp string to a given format.
static now( $style=TS_MW)
Get the current time in the given format.
Exception class for replica DB wait timeouts.
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:50
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.
This serves as the entry point to the MediaWiki session handling system.
The Message class provides methods which fulfil two basic services:
Definition Message.php:159
Stub profiler that does nothing.
static instance()
Singleton.
Definition Profiler.php:61
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:326
static getUsableTempDirectory()
A formatter that outputs unified diffs.
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:227
const PROTO_HTTPS
Definition Defines.php:224
const PROTO_CURRENT
Definition Defines.php:226
const PROTO_INTERNAL
Definition Defines.php:228
const PROTO_HTTP
Definition Defines.php:223
const SHELL_MAX_ARG_STRLEN
Definition Defines.php:274
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set $status
Definition hooks.txt:1049
the array() calling protocol came about after MediaWiki 1.4rc1.
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object & $output
Definition hooks.txt:1102
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:249
namespace are movable Hooks may change this value to override the return value of MWNamespace::isMovable(). 'NewDifferenceEngine' do that in ParserLimitReportFormat instead use this to modify the parameters of the image and a DIV can begin in one section and end in another Make sure your code can handle that case gracefully See the EditSectionClearerLink extension for an example zero but section is usually empty its values are the globals values before the output is cached one of or reset my talk my contributions etc etc otherwise the built in rate limiting checks are if enabled allows for interception of redirect as a string mapping parameter names to values & $type
Definition hooks.txt:2568
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition hooks.txt:1752
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:Associative array mapping language codes to prefixed links of the form "language:title". & $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:1937
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:268
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:986
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context $options
Definition hooks.txt:1096
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:2710
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2685
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist e g Watchlist removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object to manipulate or replace but no entry for that model exists in $wgContentHandlers if desired 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 inclusive $limit
Definition hooks.txt:1135
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:1949
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:886
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:1955
the value to return A Title object or null for latest all implement SearchIndexField $engine
Definition hooks.txt:2755
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:304
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:925
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:1595
returning false will NOT prevent logging $e
Definition hooks.txt:2110
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:887
$from
if(count( $args)==0) $dir
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
$context
Definition load.php:50
$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
$tokens
$source
$last
$lines
Definition router.php:67
$params
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
Definition defines.php:6
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition defines.php:11