MediaWiki REL1_27
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;
30
31// Hide compatibility functions from Doxygen
33
41// hash_equals function only exists in PHP >= 5.6.0
42// http://php.net/hash_equals
43if ( !function_exists( 'hash_equals' ) ) {
69 function hash_equals( $known_string, $user_string ) {
70 // Strict type checking as in PHP's native implementation
71 if ( !is_string( $known_string ) ) {
72 trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
73 gettype( $known_string ) . ' given', E_USER_WARNING );
74
75 return false;
76 }
77
78 if ( !is_string( $user_string ) ) {
79 trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
80 gettype( $user_string ) . ' given', E_USER_WARNING );
81
82 return false;
83 }
84
85 $known_string_len = strlen( $known_string );
86 if ( $known_string_len !== strlen( $user_string ) ) {
87 return false;
88 }
89
90 $result = 0;
91 for ( $i = 0; $i < $known_string_len; $i++ ) {
92 $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] );
93 }
94
95 return ( $result === 0 );
96 }
97}
99
110function wfLoadExtension( $ext, $path = null ) {
111 if ( !$path ) {
113 $path = "$wgExtensionDirectory/$ext/extension.json";
114 }
116}
117
131function wfLoadExtensions( array $exts ) {
133 $registry = ExtensionRegistry::getInstance();
134 foreach ( $exts as $ext ) {
135 $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
136 }
137}
138
147function wfLoadSkin( $skin, $path = null ) {
148 if ( !$path ) {
150 $path = "$wgStyleDirectory/$skin/skin.json";
151 }
153}
154
162function wfLoadSkins( array $skins ) {
164 $registry = ExtensionRegistry::getInstance();
165 foreach ( $skins as $skin ) {
166 $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
167 }
168}
169
176function wfArrayDiff2( $a, $b ) {
177 return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
178}
179
185function wfArrayDiff2_cmp( $a, $b ) {
186 if ( is_string( $a ) && is_string( $b ) ) {
187 return strcmp( $a, $b );
188 } elseif ( count( $a ) !== count( $b ) ) {
189 return count( $a ) < count( $b ) ? -1 : 1;
190 } else {
191 reset( $a );
192 reset( $b );
193 while ( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
194 $cmp = strcmp( $valueA, $valueB );
195 if ( $cmp !== 0 ) {
196 return $cmp;
197 }
198 }
199 return 0;
200 }
201}
202
212function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
213 if ( is_null( $changed ) ) {
214 throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
215 }
216 if ( $default[$key] !== $value ) {
217 $changed[$key] = $value;
218 }
219}
220
240function wfMergeErrorArrays( /*...*/ ) {
241 $args = func_get_args();
242 $out = [];
243 foreach ( $args as $errors ) {
244 foreach ( $errors as $params ) {
245 $originalParams = $params;
246 if ( $params[0] instanceof MessageSpecifier ) {
247 $msg = $params[0];
248 $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
249 }
250 # @todo FIXME: Sometimes get nested arrays for $params,
251 # which leads to E_NOTICEs
252 $spec = implode( "\t", $params );
253 $out[$spec] = $originalParams;
254 }
255 }
256 return array_values( $out );
257}
258
267function wfArrayInsertAfter( array $array, array $insert, $after ) {
268 // Find the offset of the element to insert after.
269 $keys = array_keys( $array );
270 $offsetByKey = array_flip( $keys );
271
272 $offset = $offsetByKey[$after];
273
274 // Insert at the specified offset
275 $before = array_slice( $array, 0, $offset + 1, true );
276 $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
277
278 $output = $before + $insert + $after;
279
280 return $output;
281}
282
290function wfObjectToArray( $objOrArray, $recursive = true ) {
291 $array = [];
292 if ( is_object( $objOrArray ) ) {
293 $objOrArray = get_object_vars( $objOrArray );
294 }
295 foreach ( $objOrArray as $key => $value ) {
296 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
298 }
299
300 $array[$key] = $value;
301 }
302
303 return $array;
304}
305
316function wfRandom() {
317 // The maximum random value is "only" 2^31-1, so get two random
318 // values to reduce the chance of dupes
319 $max = mt_getrandmax() + 1;
320 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
321 return $rand;
322}
323
334function wfRandomString( $length = 32 ) {
335 $str = '';
336 for ( $n = 0; $n < $length; $n += 7 ) {
337 $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
338 }
339 return substr( $str, 0, $length );
340}
341
369function wfUrlencode( $s ) {
370 static $needle;
371
372 if ( is_null( $s ) ) {
373 $needle = null;
374 return '';
375 }
376
377 if ( is_null( $needle ) ) {
378 $needle = [ '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F', '%7E' ];
379 if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
380 ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
381 ) {
382 $needle[] = '%3A';
383 }
384 }
385
386 $s = urlencode( $s );
387 $s = str_ireplace(
388 $needle,
389 [ ';', '@', '$', '!', '*', '(', ')', ',', '/', '~', ':' ],
390 $s
391 );
392
393 return $s;
394}
395
406function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) {
407 if ( !is_null( $array2 ) ) {
408 $array1 = $array1 + $array2;
409 }
410
411 $cgi = '';
412 foreach ( $array1 as $key => $value ) {
413 if ( !is_null( $value ) && $value !== false ) {
414 if ( $cgi != '' ) {
415 $cgi .= '&';
416 }
417 if ( $prefix !== '' ) {
418 $key = $prefix . "[$key]";
419 }
420 if ( is_array( $value ) ) {
421 $firstTime = true;
422 foreach ( $value as $k => $v ) {
423 $cgi .= $firstTime ? '' : '&';
424 if ( is_array( $v ) ) {
425 $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" );
426 } else {
427 $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
428 }
429 $firstTime = false;
430 }
431 } else {
432 if ( is_object( $value ) ) {
433 $value = $value->__toString();
434 }
435 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
436 }
437 }
438 }
439 return $cgi;
440}
441
451function wfCgiToArray( $query ) {
452 if ( isset( $query[0] ) && $query[0] == '?' ) {
453 $query = substr( $query, 1 );
454 }
455 $bits = explode( '&', $query );
456 $ret = [];
457 foreach ( $bits as $bit ) {
458 if ( $bit === '' ) {
459 continue;
460 }
461 if ( strpos( $bit, '=' ) === false ) {
462 // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
463 $key = $bit;
464 $value = '';
465 } else {
466 list( $key, $value ) = explode( '=', $bit );
467 }
468 $key = urldecode( $key );
469 $value = urldecode( $value );
470 if ( strpos( $key, '[' ) !== false ) {
471 $keys = array_reverse( explode( '[', $key ) );
472 $key = array_pop( $keys );
473 $temp = $value;
474 foreach ( $keys as $k ) {
475 $k = substr( $k, 0, -1 );
476 $temp = [ $k => $temp ];
477 }
478 if ( isset( $ret[$key] ) ) {
479 $ret[$key] = array_merge( $ret[$key], $temp );
480 } else {
481 $ret[$key] = $temp;
482 }
483 } else {
484 $ret[$key] = $value;
485 }
486 }
487 return $ret;
488}
489
498function wfAppendQuery( $url, $query ) {
499 if ( is_array( $query ) ) {
501 }
502 if ( $query != '' ) {
503 // Remove the fragment, if there is one
504 $fragment = false;
505 $hashPos = strpos( $url, '#' );
506 if ( $hashPos !== false ) {
507 $fragment = substr( $url, $hashPos );
508 $url = substr( $url, 0, $hashPos );
509 }
510
511 // Add parameter
512 if ( false === strpos( $url, '?' ) ) {
513 $url .= '?';
514 } else {
515 $url .= '&';
516 }
517 $url .= $query;
518
519 // Put the fragment back
520 if ( $fragment !== false ) {
521 $url .= $fragment;
522 }
523 }
524 return $url;
525}
526
550function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
553 if ( $defaultProto === PROTO_CANONICAL ) {
554 $serverUrl = $wgCanonicalServer;
555 } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
556 // Make $wgInternalServer fall back to $wgServer if not set
557 $serverUrl = $wgInternalServer;
558 } else {
559 $serverUrl = $wgServer;
560 if ( $defaultProto === PROTO_CURRENT ) {
561 $defaultProto = $wgRequest->getProtocol() . '://';
562 }
563 }
564
565 // Analyze $serverUrl to obtain its protocol
566 $bits = wfParseUrl( $serverUrl );
567 $serverHasProto = $bits && $bits['scheme'] != '';
568
569 if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
570 if ( $serverHasProto ) {
571 $defaultProto = $bits['scheme'] . '://';
572 } else {
573 // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
574 // This really isn't supposed to happen. Fall back to HTTP in this
575 // ridiculous case.
576 $defaultProto = PROTO_HTTP;
577 }
578 }
579
580 $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
581
582 if ( substr( $url, 0, 2 ) == '//' ) {
583 $url = $defaultProtoWithoutSlashes . $url;
584 } elseif ( substr( $url, 0, 1 ) == '/' ) {
585 // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
586 // otherwise leave it alone.
587 $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
588 }
589
590 $bits = wfParseUrl( $url );
591
592 // ensure proper port for HTTPS arrives in URL
593 // https://phabricator.wikimedia.org/T67184
594 if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
595 $bits['port'] = $wgHttpsPort;
596 }
597
598 if ( $bits && isset( $bits['path'] ) ) {
599 $bits['path'] = wfRemoveDotSegments( $bits['path'] );
600 return wfAssembleUrl( $bits );
601 } elseif ( $bits ) {
602 # No path to expand
603 return $url;
604 } elseif ( substr( $url, 0, 1 ) != '/' ) {
605 # URL is a relative path
606 return wfRemoveDotSegments( $url );
607 }
608
609 # Expanded URL is not valid.
610 return false;
611}
612
626function wfAssembleUrl( $urlParts ) {
627 $result = '';
628
629 if ( isset( $urlParts['delimiter'] ) ) {
630 if ( isset( $urlParts['scheme'] ) ) {
631 $result .= $urlParts['scheme'];
632 }
633
634 $result .= $urlParts['delimiter'];
635 }
636
637 if ( isset( $urlParts['host'] ) ) {
638 if ( isset( $urlParts['user'] ) ) {
639 $result .= $urlParts['user'];
640 if ( isset( $urlParts['pass'] ) ) {
641 $result .= ':' . $urlParts['pass'];
642 }
643 $result .= '@';
644 }
645
646 $result .= $urlParts['host'];
647
648 if ( isset( $urlParts['port'] ) ) {
649 $result .= ':' . $urlParts['port'];
650 }
651 }
652
653 if ( isset( $urlParts['path'] ) ) {
654 $result .= $urlParts['path'];
655 }
656
657 if ( isset( $urlParts['query'] ) ) {
658 $result .= '?' . $urlParts['query'];
659 }
660
661 if ( isset( $urlParts['fragment'] ) ) {
662 $result .= '#' . $urlParts['fragment'];
663 }
664
665 return $result;
666}
667
678function wfRemoveDotSegments( $urlPath ) {
679 $output = '';
680 $inputOffset = 0;
681 $inputLength = strlen( $urlPath );
682
683 while ( $inputOffset < $inputLength ) {
684 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
685 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
686 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
687 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
688 $trimOutput = false;
689
690 if ( $prefixLengthTwo == './' ) {
691 # Step A, remove leading "./"
692 $inputOffset += 2;
693 } elseif ( $prefixLengthThree == '../' ) {
694 # Step A, remove leading "../"
695 $inputOffset += 3;
696 } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
697 # Step B, replace leading "/.$" with "/"
698 $inputOffset += 1;
699 $urlPath[$inputOffset] = '/';
700 } elseif ( $prefixLengthThree == '/./' ) {
701 # Step B, replace leading "/./" with "/"
702 $inputOffset += 2;
703 } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
704 # Step C, replace leading "/..$" with "/" and
705 # remove last path component in output
706 $inputOffset += 2;
707 $urlPath[$inputOffset] = '/';
708 $trimOutput = true;
709 } elseif ( $prefixLengthFour == '/../' ) {
710 # Step C, replace leading "/../" with "/" and
711 # remove last path component in output
712 $inputOffset += 3;
713 $trimOutput = true;
714 } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
715 # Step D, remove "^.$"
716 $inputOffset += 1;
717 } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
718 # Step D, remove "^..$"
719 $inputOffset += 2;
720 } else {
721 # Step E, move leading path segment to output
722 if ( $prefixLengthOne == '/' ) {
723 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
724 } else {
725 $slashPos = strpos( $urlPath, '/', $inputOffset );
726 }
727 if ( $slashPos === false ) {
728 $output .= substr( $urlPath, $inputOffset );
729 $inputOffset = $inputLength;
730 } else {
731 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
732 $inputOffset += $slashPos - $inputOffset;
733 }
734 }
735
736 if ( $trimOutput ) {
737 $slashPos = strrpos( $output, '/' );
738 if ( $slashPos === false ) {
739 $output = '';
740 } else {
741 $output = substr( $output, 0, $slashPos );
742 }
743 }
744 }
745
746 return $output;
747}
748
756function wfUrlProtocols( $includeProtocolRelative = true ) {
758
759 // Cache return values separately based on $includeProtocolRelative
760 static $withProtRel = null, $withoutProtRel = null;
761 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
762 if ( !is_null( $cachedValue ) ) {
763 return $cachedValue;
764 }
765
766 // Support old-style $wgUrlProtocols strings, for backwards compatibility
767 // with LocalSettings files from 1.5
768 if ( is_array( $wgUrlProtocols ) ) {
769 $protocols = [];
770 foreach ( $wgUrlProtocols as $protocol ) {
771 // Filter out '//' if !$includeProtocolRelative
772 if ( $includeProtocolRelative || $protocol !== '//' ) {
773 $protocols[] = preg_quote( $protocol, '/' );
774 }
775 }
776
777 $retval = implode( '|', $protocols );
778 } else {
779 // Ignore $includeProtocolRelative in this case
780 // This case exists for pre-1.6 compatibility, and we can safely assume
781 // that '//' won't appear in a pre-1.6 config because protocol-relative
782 // URLs weren't supported until 1.18
784 }
785
786 // Cache return value
787 if ( $includeProtocolRelative ) {
788 $withProtRel = $retval;
789 } else {
790 $withoutProtRel = $retval;
791 }
792 return $retval;
793}
794
802 return wfUrlProtocols( false );
803}
804
816function wfParseUrl( $url ) {
817 global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
818
819 // Protocol-relative URLs are handled really badly by parse_url(). It's so
820 // bad that the easiest way to handle them is to just prepend 'http:' and
821 // strip the protocol out later.
822 $wasRelative = substr( $url, 0, 2 ) == '//';
823 if ( $wasRelative ) {
824 $url = "http:$url";
825 }
826 MediaWiki\suppressWarnings();
827 $bits = parse_url( $url );
828 MediaWiki\restoreWarnings();
829 // parse_url() returns an array without scheme for some invalid URLs, e.g.
830 // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
831 if ( !$bits || !isset( $bits['scheme'] ) ) {
832 return false;
833 }
834
835 // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
836 $bits['scheme'] = strtolower( $bits['scheme'] );
837
838 // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
839 if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
840 $bits['delimiter'] = '://';
841 } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
842 $bits['delimiter'] = ':';
843 // parse_url detects for news: and mailto: the host part of an url as path
844 // We have to correct this wrong detection
845 if ( isset( $bits['path'] ) ) {
846 $bits['host'] = $bits['path'];
847 $bits['path'] = '';
848 }
849 } else {
850 return false;
851 }
852
853 /* Provide an empty host for eg. file:/// urls (see bug 28627) */
854 if ( !isset( $bits['host'] ) ) {
855 $bits['host'] = '';
856
857 // bug 45069
858 if ( isset( $bits['path'] ) ) {
859 /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
860 if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
861 $bits['path'] = '/' . $bits['path'];
862 }
863 } else {
864 $bits['path'] = '';
865 }
866 }
867
868 // If the URL was protocol-relative, fix scheme and delimiter
869 if ( $wasRelative ) {
870 $bits['scheme'] = '';
871 $bits['delimiter'] = '//';
872 }
873 return $bits;
874}
875
886function wfExpandIRI( $url ) {
887 return preg_replace_callback(
888 '/((?:%[89A-F][0-9A-F])+)/i',
889 'wfExpandIRI_callback',
890 wfExpandUrl( $url )
891 );
892}
893
900 return urldecode( $matches[1] );
901}
902
909function wfMakeUrlIndexes( $url ) {
910 $bits = wfParseUrl( $url );
911
912 // Reverse the labels in the hostname, convert to lower case
913 // For emails reverse domainpart only
914 if ( $bits['scheme'] == 'mailto' ) {
915 $mailparts = explode( '@', $bits['host'], 2 );
916 if ( count( $mailparts ) === 2 ) {
917 $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
918 } else {
919 // No domain specified, don't mangle it
920 $domainpart = '';
921 }
922 $reversedHost = $domainpart . '@' . $mailparts[0];
923 } else {
924 $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
925 }
926 // Add an extra dot to the end
927 // Why? Is it in wrong place in mailto links?
928 if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
929 $reversedHost .= '.';
930 }
931 // Reconstruct the pseudo-URL
932 $prot = $bits['scheme'];
933 $index = $prot . $bits['delimiter'] . $reversedHost;
934 // Leave out user and password. Add the port, path, query and fragment
935 if ( isset( $bits['port'] ) ) {
936 $index .= ':' . $bits['port'];
937 }
938 if ( isset( $bits['path'] ) ) {
939 $index .= $bits['path'];
940 } else {
941 $index .= '/';
942 }
943 if ( isset( $bits['query'] ) ) {
944 $index .= '?' . $bits['query'];
945 }
946 if ( isset( $bits['fragment'] ) ) {
947 $index .= '#' . $bits['fragment'];
948 }
949
950 if ( $prot == '' ) {
951 return [ "http:$index", "https:$index" ];
952 } else {
953 return [ $index ];
954 }
955}
956
963function wfMatchesDomainList( $url, $domains ) {
964 $bits = wfParseUrl( $url );
965 if ( is_array( $bits ) && isset( $bits['host'] ) ) {
966 $host = '.' . $bits['host'];
967 foreach ( (array)$domains as $domain ) {
968 $domain = '.' . $domain;
969 if ( substr( $host, -strlen( $domain ) ) === $domain ) {
970 return true;
971 }
972 }
973 }
974 return false;
975}
976
997function wfDebug( $text, $dest = 'all', array $context = [] ) {
1000
1001 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1002 return;
1003 }
1004
1005 $text = trim( $text );
1006
1007 if ( $wgDebugTimestamps ) {
1008 $context['seconds_elapsed'] = sprintf(
1009 '%6.4f',
1010 microtime( true ) - $wgRequestTime
1011 );
1012 $context['memory_used'] = sprintf(
1013 '%5.1fM',
1014 ( memory_get_usage( true ) / ( 1024 * 1024 ) )
1015 );
1016 }
1017
1018 if ( $wgDebugLogPrefix !== '' ) {
1019 $context['prefix'] = $wgDebugLogPrefix;
1020 }
1021 $context['private'] = ( $dest === false || $dest === 'private' );
1022
1023 $logger = LoggerFactory::getInstance( 'wfDebug' );
1024 $logger->debug( $text, $context );
1025}
1026
1032 static $cache;
1033 if ( $cache !== null ) {
1034 return $cache;
1035 }
1036 # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
1037 if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
1038 || (
1039 isset( $_SERVER['SCRIPT_NAME'] )
1040 && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
1041 )
1042 ) {
1043 $cache = true;
1044 } else {
1045 $cache = false;
1046 }
1047 return $cache;
1048}
1049
1055function wfDebugMem( $exact = false ) {
1056 $mem = memory_get_usage();
1057 if ( !$exact ) {
1058 $mem = floor( $mem / 1024 ) . ' KiB';
1059 } else {
1060 $mem .= ' B';
1061 }
1062 wfDebug( "Memory usage: $mem\n" );
1063}
1064
1090function wfDebugLog(
1091 $logGroup, $text, $dest = 'all', array $context = []
1092) {
1093 $text = trim( $text );
1094
1095 $logger = LoggerFactory::getInstance( $logGroup );
1096 $context['private'] = ( $dest === false || $dest === 'private' );
1097 $logger->info( $text, $context );
1098}
1099
1108function wfLogDBError( $text, array $context = [] ) {
1109 $logger = LoggerFactory::getInstance( 'wfLogDBError' );
1110 $logger->error( trim( $text ), $context );
1111}
1112
1126function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
1127 MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
1128}
1129
1140function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1141 MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
1142}
1143
1153function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1154 MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
1155}
1156
1170function wfErrorLog( $text, $file, array $context = [] ) {
1171 wfDeprecated( __METHOD__, '1.25' );
1172 $logger = LoggerFactory::getInstance( 'wfErrorLog' );
1173 $context['destination'] = $file;
1174 $logger->info( trim( $text ), $context );
1175}
1176
1182
1185
1186 $profiler = Profiler::instance();
1187 $profiler->setContext( $context );
1188 $profiler->logData();
1189
1190 $config = $context->getConfig();
1191 if ( $config->get( 'StatsdServer' ) ) {
1192 try {
1193 $statsdServer = explode( ':', $config->get( 'StatsdServer' ) );
1194 $statsdHost = $statsdServer[0];
1195 $statsdPort = isset( $statsdServer[1] ) ? $statsdServer[1] : 8125;
1196 $statsdSender = new SocketSender( $statsdHost, $statsdPort );
1197 $statsdClient = new SamplingStatsdClient( $statsdSender, true, false );
1198 $statsdClient->send( $context->getStats()->getBuffer() );
1199 } catch ( Exception $ex ) {
1200 MWExceptionHandler::logException( $ex );
1201 }
1202 }
1203
1204 # Profiling must actually be enabled...
1205 if ( $profiler instanceof ProfilerStub ) {
1206 return;
1207 }
1208
1209 if ( isset( $wgDebugLogGroups['profileoutput'] )
1210 && $wgDebugLogGroups['profileoutput'] === false
1211 ) {
1212 // Explicitly disabled
1213 return;
1214 }
1215 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1216 return;
1217 }
1218
1219 $ctx = [ 'elapsed' => $request->getElapsedTime() ];
1220 if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1221 $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
1222 }
1223 if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
1224 $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
1225 }
1226 if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
1227 $ctx['from'] = $_SERVER['HTTP_FROM'];
1228 }
1229 if ( isset( $ctx['forwarded_for'] ) ||
1230 isset( $ctx['client_ip'] ) ||
1231 isset( $ctx['from'] ) ) {
1232 $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
1233 }
1234
1235 // Don't load $wgUser at this late stage just for statistics purposes
1236 // @todo FIXME: We can detect some anons even if it is not loaded.
1237 // See User::getId()
1238 $user = $context->getUser();
1239 $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
1240
1241 // Command line script uses a FauxRequest object which does not have
1242 // any knowledge about an URL and throw an exception instead.
1243 try {
1244 $ctx['url'] = urldecode( $request->getRequestURL() );
1245 } catch ( Exception $ignored ) {
1246 // no-op
1247 }
1248
1249 $ctx['output'] = $profiler->getOutput();
1250
1251 $log = LoggerFactory::getInstance( 'profileoutput' );
1252 $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1253}
1254
1262function wfIncrStats( $key, $count = 1 ) {
1263 $stats = RequestContext::getMain()->getStats();
1264 $stats->updateCount( $key, $count );
1265}
1266
1272function wfReadOnly() {
1273 return wfReadOnlyReason() !== false;
1274}
1275
1285 $readOnly = wfConfiguredReadOnlyReason();
1286 if ( $readOnly !== false ) {
1287 return $readOnly;
1288 }
1289
1290 static $lbReadOnly = null;
1291 if ( $lbReadOnly === null ) {
1292 // Callers use this method to be aware that data presented to a user
1293 // may be very stale and thus allowing submissions can be problematic.
1294 $lbReadOnly = wfGetLB()->getReadOnlyReason();
1295 }
1296
1297 return $lbReadOnly;
1298}
1299
1308
1309 if ( $wgReadOnly === null ) {
1310 // Set $wgReadOnly for faster access next time
1311 if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
1312 $wgReadOnly = file_get_contents( $wgReadOnlyFile );
1313 } else {
1314 $wgReadOnly = false;
1315 }
1316 }
1317
1318 return $wgReadOnly;
1319}
1320
1336function wfGetLangObj( $langcode = false ) {
1337 # Identify which language to get or create a language object for.
1338 # Using is_object here due to Stub objects.
1339 if ( is_object( $langcode ) ) {
1340 # Great, we already have the object (hopefully)!
1341 return $langcode;
1342 }
1343
1345 if ( $langcode === true || $langcode === $wgLanguageCode ) {
1346 # $langcode is the language code of the wikis content language object.
1347 # or it is a boolean and value is true
1348 return $wgContLang;
1349 }
1350
1352 if ( $langcode === false || $langcode === $wgLang->getCode() ) {
1353 # $langcode is the language code of user language object.
1354 # or it was a boolean and value is false
1355 return $wgLang;
1356 }
1357
1358 $validCodes = array_keys( Language::fetchLanguageNames() );
1359 if ( in_array( $langcode, $validCodes ) ) {
1360 # $langcode corresponds to a valid language.
1361 return Language::factory( $langcode );
1362 }
1363
1364 # $langcode is a string, but not a valid language code; use content language.
1365 wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1366 return $wgContLang;
1367}
1368
1385function wfMessage( $key /*...*/ ) {
1386 $params = func_get_args();
1387 array_shift( $params );
1388 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
1389 $params = $params[0];
1390 }
1391 return new Message( $key, $params );
1392}
1393
1406function wfMessageFallback( /*...*/ ) {
1407 $args = func_get_args();
1408 return call_user_func_array( 'Message::newFallbackSequence', $args );
1409}
1410
1419function wfMsgReplaceArgs( $message, $args ) {
1420 # Fix windows line-endings
1421 # Some messages are split with explode("\n", $msg)
1422 $message = str_replace( "\r", '', $message );
1423
1424 // Replace arguments
1425 if ( is_array( $args ) && $args ) {
1426 if ( is_array( $args[0] ) ) {
1427 $args = array_values( $args[0] );
1428 }
1429 $replacementKeys = [];
1430 foreach ( $args as $n => $param ) {
1431 $replacementKeys['$' . ( $n + 1 )] = $param;
1432 }
1433 $message = strtr( $message, $replacementKeys );
1434 }
1435
1436 return $message;
1437}
1438
1446function wfHostname() {
1447 static $host;
1448 if ( is_null( $host ) ) {
1449
1450 # Hostname overriding
1452 if ( $wgOverrideHostname !== false ) {
1453 # Set static and skip any detection
1454 $host = $wgOverrideHostname;
1455 return $host;
1456 }
1457
1458 if ( function_exists( 'posix_uname' ) ) {
1459 // This function not present on Windows
1460 $uname = posix_uname();
1461 } else {
1462 $uname = false;
1463 }
1464 if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
1465 $host = $uname['nodename'];
1466 } elseif ( getenv( 'COMPUTERNAME' ) ) {
1467 # Windows computer name
1468 $host = getenv( 'COMPUTERNAME' );
1469 } else {
1470 # This may be a virtual server.
1471 $host = $_SERVER['SERVER_NAME'];
1472 }
1473 }
1474 return $host;
1475}
1476
1486function wfReportTime() {
1488
1489 $responseTime = round( ( microtime( true ) - $wgRequestTime ) * 1000 );
1490 $reportVars = [ 'wgBackendResponseTime' => $responseTime ];
1491 if ( $wgShowHostnames ) {
1492 $reportVars['wgHostname'] = wfHostname();
1493 }
1494 return Skin::makeVariablesScript( $reportVars );
1495}
1496
1507function wfDebugBacktrace( $limit = 0 ) {
1508 static $disabled = null;
1509
1510 if ( is_null( $disabled ) ) {
1511 $disabled = !function_exists( 'debug_backtrace' );
1512 if ( $disabled ) {
1513 wfDebug( "debug_backtrace() is disabled\n" );
1514 }
1515 }
1516 if ( $disabled ) {
1517 return [];
1518 }
1519
1520 if ( $limit ) {
1521 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1522 } else {
1523 return array_slice( debug_backtrace(), 1 );
1524 }
1525}
1526
1535function wfBacktrace( $raw = null ) {
1537
1538 if ( $raw === null ) {
1539 $raw = $wgCommandLineMode;
1540 }
1541
1542 if ( $raw ) {
1543 $frameFormat = "%s line %s calls %s()\n";
1544 $traceFormat = "%s";
1545 } else {
1546 $frameFormat = "<li>%s line %s calls %s()</li>\n";
1547 $traceFormat = "<ul>\n%s</ul>\n";
1548 }
1549
1550 $frames = array_map( function ( $frame ) use ( $frameFormat ) {
1551 $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
1552 $line = isset( $frame['line'] ) ? $frame['line'] : '-';
1553 $call = $frame['function'];
1554 if ( !empty( $frame['class'] ) ) {
1555 $call = $frame['class'] . $frame['type'] . $call;
1556 }
1557 return sprintf( $frameFormat, $file, $line, $call );
1558 }, wfDebugBacktrace() );
1559
1560 return sprintf( $traceFormat, implode( '', $frames ) );
1561}
1562
1572function wfGetCaller( $level = 2 ) {
1573 $backtrace = wfDebugBacktrace( $level + 1 );
1574 if ( isset( $backtrace[$level] ) ) {
1575 return wfFormatStackFrame( $backtrace[$level] );
1576 } else {
1577 return 'unknown';
1578 }
1579}
1580
1588function wfGetAllCallers( $limit = 3 ) {
1589 $trace = array_reverse( wfDebugBacktrace() );
1590 if ( !$limit || $limit > count( $trace ) - 1 ) {
1591 $limit = count( $trace ) - 1;
1592 }
1593 $trace = array_slice( $trace, -$limit - 1, $limit );
1594 return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
1595}
1596
1603function wfFormatStackFrame( $frame ) {
1604 if ( !isset( $frame['function'] ) ) {
1605 return 'NO_FUNCTION_GIVEN';
1606 }
1607 return isset( $frame['class'] ) && isset( $frame['type'] ) ?
1608 $frame['class'] . $frame['type'] . $frame['function'] :
1609 $frame['function'];
1610}
1611
1612/* Some generic result counters, pulled out of SearchEngine */
1613
1621function wfShowingResults( $offset, $limit ) {
1622 return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1623}
1624
1632function wfClientAcceptsGzip( $force = false ) {
1633 static $result = null;
1634 if ( $result === null || $force ) {
1635 $result = false;
1636 if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
1637 # @todo FIXME: We may want to blacklist some broken browsers
1638 $m = [];
1639 if ( preg_match(
1640 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1641 $_SERVER['HTTP_ACCEPT_ENCODING'],
1642 $m
1643 )
1644 ) {
1645 if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
1646 $result = false;
1647 return $result;
1648 }
1649 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
1650 $result = true;
1651 }
1652 }
1653 }
1654 return $result;
1655}
1656
1666function wfEscapeWikiText( $text ) {
1667 static $repl = null, $repl2 = null;
1668 if ( $repl === null ) {
1669 $repl = [
1670 '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
1671 '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
1672 '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
1673 "\n#" => "\n&#35;", "\r#" => "\r&#35;",
1674 "\n*" => "\n&#42;", "\r*" => "\r&#42;",
1675 "\n:" => "\n&#58;", "\r:" => "\r&#58;",
1676 "\n " => "\n&#32;", "\r " => "\r&#32;",
1677 "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
1678 "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
1679 "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
1680 "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
1681 '__' => '_&#95;', '://' => '&#58;//',
1682 ];
1683
1684 // We have to catch everything "\s" matches in PCRE
1685 foreach ( [ 'ISBN', 'RFC', 'PMID' ] as $magic ) {
1686 $repl["$magic "] = "$magic&#32;";
1687 $repl["$magic\t"] = "$magic&#9;";
1688 $repl["$magic\r"] = "$magic&#13;";
1689 $repl["$magic\n"] = "$magic&#10;";
1690 $repl["$magic\f"] = "$magic&#12;";
1691 }
1692
1693 // And handle protocols that don't use "://"
1695 $repl2 = [];
1696 foreach ( $wgUrlProtocols as $prot ) {
1697 if ( substr( $prot, -1 ) === ':' ) {
1698 $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
1699 }
1700 }
1701 $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/';
1702 }
1703 $text = substr( strtr( "\n$text", $repl ), 1 );
1704 $text = preg_replace( $repl2, '$1&#58;', $text );
1705 return $text;
1706}
1707
1718function wfSetVar( &$dest, $source, $force = false ) {
1719 $temp = $dest;
1720 if ( !is_null( $source ) || $force ) {
1721 $dest = $source;
1722 }
1723 return $temp;
1724}
1725
1735function wfSetBit( &$dest, $bit, $state = true ) {
1736 $temp = (bool)( $dest & $bit );
1737 if ( !is_null( $state ) ) {
1738 if ( $state ) {
1739 $dest |= $bit;
1740 } else {
1741 $dest &= ~$bit;
1742 }
1743 }
1744 return $temp;
1745}
1746
1753function wfVarDump( $var ) {
1754 global $wgOut;
1755 $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
1756 if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
1757 print $s;
1758 } else {
1759 $wgOut->addHTML( $s );
1760 }
1761}
1762
1770function wfHttpError( $code, $label, $desc ) {
1771 global $wgOut;
1773 if ( $wgOut ) {
1774 $wgOut->disable();
1775 $wgOut->sendCacheControl();
1776 }
1777
1778 header( 'Content-type: text/html; charset=utf-8' );
1779 print '<!DOCTYPE html>' .
1780 '<html><head><title>' .
1781 htmlspecialchars( $label ) .
1782 '</title></head><body><h1>' .
1783 htmlspecialchars( $label ) .
1784 '</h1><p>' .
1785 nl2br( htmlspecialchars( $desc ) ) .
1786 "</p></body></html>\n";
1787}
1788
1806function wfResetOutputBuffers( $resetGzipEncoding = true ) {
1807 if ( $resetGzipEncoding ) {
1808 // Suppress Content-Encoding and Content-Length
1809 // headers from 1.10+s wfOutputHandler
1812 }
1813 while ( $status = ob_get_status() ) {
1814 if ( isset( $status['flags'] ) ) {
1815 $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1816 $deleteable = ( $status['flags'] & $flags ) === $flags;
1817 } elseif ( isset( $status['del'] ) ) {
1818 $deleteable = $status['del'];
1819 } else {
1820 // Guess that any PHP-internal setting can't be removed.
1821 $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
1822 }
1823 if ( !$deleteable ) {
1824 // Give up, and hope the result doesn't break
1825 // output behavior.
1826 break;
1827 }
1828 if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1829 // Unit testing barrier to prevent this function from breaking PHPUnit.
1830 break;
1831 }
1832 if ( !ob_end_clean() ) {
1833 // Could not remove output buffer handler; abort now
1834 // to avoid getting in some kind of infinite loop.
1835 break;
1836 }
1837 if ( $resetGzipEncoding ) {
1838 if ( $status['name'] == 'ob_gzhandler' ) {
1839 // Reset the 'Content-Encoding' field set by this handler
1840 // so we can start fresh.
1841 header_remove( 'Content-Encoding' );
1842 break;
1843 }
1844 }
1845 }
1846}
1847
1861 wfResetOutputBuffers( false );
1862}
1863
1872function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1873 # No arg means accept anything (per HTTP spec)
1874 if ( !$accept ) {
1875 return [ $def => 1.0 ];
1876 }
1877
1878 $prefs = [];
1879
1880 $parts = explode( ',', $accept );
1881
1882 foreach ( $parts as $part ) {
1883 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1884 $values = explode( ';', trim( $part ) );
1885 $match = [];
1886 if ( count( $values ) == 1 ) {
1887 $prefs[$values[0]] = 1.0;
1888 } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1889 $prefs[$values[0]] = floatval( $match[1] );
1890 }
1891 }
1892
1893 return $prefs;
1894}
1895
1908function mimeTypeMatch( $type, $avail ) {
1909 if ( array_key_exists( $type, $avail ) ) {
1910 return $type;
1911 } else {
1912 $mainType = explode( '/', $type )[0];
1913 if ( array_key_exists( "$mainType/*", $avail ) ) {
1914 return "$mainType/*";
1915 } elseif ( array_key_exists( '*/*', $avail ) ) {
1916 return '*/*';
1917 } else {
1918 return null;
1919 }
1920 }
1921}
1922
1936function wfNegotiateType( $cprefs, $sprefs ) {
1937 $combine = [];
1938
1939 foreach ( array_keys( $sprefs ) as $type ) {
1940 $subType = explode( '/', $type )[1];
1941 if ( $subType != '*' ) {
1942 $ckey = mimeTypeMatch( $type, $cprefs );
1943 if ( $ckey ) {
1944 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1945 }
1946 }
1947 }
1948
1949 foreach ( array_keys( $cprefs ) as $type ) {
1950 $subType = explode( '/', $type )[1];
1951 if ( $subType != '*' && !array_key_exists( $type, $sprefs ) ) {
1952 $skey = mimeTypeMatch( $type, $sprefs );
1953 if ( $skey ) {
1954 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1955 }
1956 }
1957 }
1958
1959 $bestq = 0;
1960 $besttype = null;
1961
1962 foreach ( array_keys( $combine ) as $type ) {
1963 if ( $combine[$type] > $bestq ) {
1964 $besttype = $type;
1965 $bestq = $combine[$type];
1966 }
1967 }
1968
1969 return $besttype;
1970}
1971
1978function wfSuppressWarnings( $end = false ) {
1979 MediaWiki\suppressWarnings( $end );
1980}
1981
1987 MediaWiki\suppressWarnings( true );
1988}
1989
1990# Autodetect, convert and provide timestamps of various types
1991
1995define( 'TS_UNIX', 0 );
1996
2000define( 'TS_MW', 1 );
2001
2005define( 'TS_DB', 2 );
2006
2010define( 'TS_RFC2822', 3 );
2011
2017define( 'TS_ISO_8601', 4 );
2018
2026define( 'TS_EXIF', 5 );
2027
2031define( 'TS_ORACLE', 6 );
2032
2036define( 'TS_POSTGRES', 7 );
2037
2041define( 'TS_ISO_8601_BASIC', 9 );
2042
2051function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
2052 try {
2053 $timestamp = new MWTimestamp( $ts );
2054 return $timestamp->getTimestamp( $outputtype );
2055 } catch ( TimestampException $e ) {
2056 wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
2057 return false;
2058 }
2059}
2060
2069function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
2070 if ( is_null( $ts ) ) {
2071 return null;
2072 } else {
2073 return wfTimestamp( $outputtype, $ts );
2074 }
2075}
2076
2082function wfTimestampNow() {
2083 # return NOW
2084 return wfTimestamp( TS_MW, time() );
2085}
2086
2092function wfIsWindows() {
2093 static $isWindows = null;
2094 if ( $isWindows === null ) {
2095 $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
2096 }
2097 return $isWindows;
2098}
2099
2105function wfIsHHVM() {
2106 return defined( 'HHVM_VERSION' );
2107}
2108
2120function wfTempDir() {
2122
2123 if ( $wgTmpDirectory !== false ) {
2124 return $wgTmpDirectory;
2125 }
2126
2127 $tmpDir = array_map( "getenv", [ 'TMPDIR', 'TMP', 'TEMP' ] );
2128 $tmpDir[] = sys_get_temp_dir();
2129 $tmpDir[] = ini_get( 'upload_tmp_dir' );
2130
2131 foreach ( $tmpDir as $tmp ) {
2132 if ( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
2133 return $tmp;
2134 }
2135 }
2136
2144 if ( wfIsWindows() ) {
2145 $tmp = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'mwtmp' . '-' . get_current_user();
2146 if ( !file_exists( $tmp ) ) {
2147 mkdir( $tmp );
2148 }
2149 if ( file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
2150 return $tmp;
2151 }
2152 }
2153
2154 throw new MWException( 'No writable temporary directory could be found. ' .
2155 'Please set $wgTmpDirectory to a writable directory.' );
2156}
2157
2167function wfMkdirParents( $dir, $mode = null, $caller = null ) {
2169
2170 if ( FileBackend::isStoragePath( $dir ) ) { // sanity
2171 throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
2172 }
2173
2174 if ( !is_null( $caller ) ) {
2175 wfDebug( "$caller: called wfMkdirParents($dir)\n" );
2176 }
2177
2178 if ( strval( $dir ) === '' || is_dir( $dir ) ) {
2179 return true;
2180 }
2181
2182 $dir = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $dir );
2183
2184 if ( is_null( $mode ) ) {
2185 $mode = $wgDirectoryMode;
2186 }
2187
2188 // Turn off the normal warning, we're doing our own below
2189 MediaWiki\suppressWarnings();
2190 $ok = mkdir( $dir, $mode, true ); // PHP5 <3
2191 MediaWiki\restoreWarnings();
2192
2193 if ( !$ok ) {
2194 // directory may have been created on another request since we last checked
2195 if ( is_dir( $dir ) ) {
2196 return true;
2197 }
2198
2199 // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
2200 wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
2201 }
2202 return $ok;
2203}
2204
2211 wfDebug( __FUNCTION__ . "( $dir )\n" );
2212 // taken from http://de3.php.net/manual/en/function.rmdir.php#98622
2213 if ( is_dir( $dir ) ) {
2214 $objects = scandir( $dir );
2215 foreach ( $objects as $object ) {
2216 if ( $object != "." && $object != ".." ) {
2217 if ( filetype( $dir . '/' . $object ) == "dir" ) {
2218 wfRecursiveRemoveDir( $dir . '/' . $object );
2219 } else {
2220 unlink( $dir . '/' . $object );
2221 }
2222 }
2223 }
2224 reset( $objects );
2225 rmdir( $dir );
2226 }
2227}
2228
2235function wfPercent( $nr, $acc = 2, $round = true ) {
2236 $ret = sprintf( "%.${acc}f", $nr );
2237 return $round ? round( $ret, $acc ) . '%' : "$ret%";
2238}
2239
2263function wfIniGetBool( $setting ) {
2264 $val = strtolower( ini_get( $setting ) );
2265 // 'on' and 'true' can't have whitespace around them, but '1' can.
2266 return $val == 'on'
2267 || $val == 'true'
2268 || $val == 'yes'
2269 || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
2270}
2271
2283function wfEscapeShellArg( /*...*/ ) {
2285
2286 $args = func_get_args();
2287 if ( count( $args ) === 1 && is_array( reset( $args ) ) ) {
2288 // If only one argument has been passed, and that argument is an array,
2289 // treat it as a list of arguments
2290 $args = reset( $args );
2291 }
2292
2293 $first = true;
2294 $retVal = '';
2295 foreach ( $args as $arg ) {
2296 if ( !$first ) {
2297 $retVal .= ' ';
2298 } else {
2299 $first = false;
2300 }
2301
2302 if ( wfIsWindows() ) {
2303 // Escaping for an MSVC-style command line parser and CMD.EXE
2304 // @codingStandardsIgnoreStart For long URLs
2305 // Refs:
2306 // * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
2307 // * http://technet.microsoft.com/en-us/library/cc723564.aspx
2308 // * Bug #13518
2309 // * CR r63214
2310 // Double the backslashes before any double quotes. Escape the double quotes.
2311 // @codingStandardsIgnoreEnd
2312 $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
2313 $arg = '';
2314 $iteration = 0;
2315 foreach ( $tokens as $token ) {
2316 if ( $iteration % 2 == 1 ) {
2317 // Delimiter, a double quote preceded by zero or more slashes
2318 $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
2319 } elseif ( $iteration % 4 == 2 ) {
2320 // ^ in $token will be outside quotes, need to be escaped
2321 $arg .= str_replace( '^', '^^', $token );
2322 } else { // $iteration % 4 == 0
2323 // ^ in $token will appear inside double quotes, so leave as is
2324 $arg .= $token;
2325 }
2326 $iteration++;
2327 }
2328 // Double the backslashes before the end of the string, because
2329 // we will soon add a quote
2330 $m = [];
2331 if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
2332 $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
2333 }
2334
2335 // Add surrounding quotes
2336 $retVal .= '"' . $arg . '"';
2337 } else {
2338 $retVal .= escapeshellarg( $arg );
2339 }
2340 }
2341 return $retVal;
2342}
2343
2351 static $disabled = null;
2352 if ( is_null( $disabled ) ) {
2353 if ( !function_exists( 'proc_open' ) ) {
2354 wfDebug( "proc_open() is disabled\n" );
2355 $disabled = 'disabled';
2356 } else {
2357 $disabled = false;
2358 }
2359 }
2360 return $disabled;
2361}
2362
2385function wfShellExec( $cmd, &$retval = null, $environ = [],
2386 $limits = [], $options = []
2387) {
2390
2391 $disabled = wfShellExecDisabled();
2392 if ( $disabled ) {
2393 $retval = 1;
2394 return 'Unable to run external programs, proc_open() is disabled.';
2395 }
2396
2397 $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
2398 $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
2399
2401
2402 $envcmd = '';
2403 foreach ( $environ as $k => $v ) {
2404 if ( wfIsWindows() ) {
2405 /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
2406 * appear in the environment variable, so we must use carat escaping as documented in
2407 * http://technet.microsoft.com/en-us/library/cc723564.aspx
2408 * Note however that the quote isn't listed there, but is needed, and the parentheses
2409 * are listed there but doesn't appear to need it.
2410 */
2411 $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& ';
2412 } else {
2413 /* Assume this is a POSIX shell, thus required to accept variable assignments before the command
2414 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01
2415 */
2416 $envcmd .= "$k=" . escapeshellarg( $v ) . ' ';
2417 }
2418 }
2419 if ( is_array( $cmd ) ) {
2420 $cmd = wfEscapeShellArg( $cmd );
2421 }
2422
2423 $cmd = $envcmd . $cmd;
2424
2425 $useLogPipe = false;
2426 if ( is_executable( '/bin/bash' ) ) {
2427 $time = intval( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
2428 if ( isset( $limits['walltime'] ) ) {
2429 $wallTime = intval( $limits['walltime'] );
2430 } elseif ( isset( $limits['time'] ) ) {
2431 $wallTime = $time;
2432 } else {
2433 $wallTime = intval( $wgMaxShellWallClockTime );
2434 }
2435 $mem = intval( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory );
2436 $filesize = intval( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
2437
2438 if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
2439 $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
2440 escapeshellarg( $cmd ) . ' ' .
2441 escapeshellarg(
2442 "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' .
2443 "MW_CPU_LIMIT=$time; " .
2444 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' .
2445 "MW_MEM_LIMIT=$mem; " .
2446 "MW_FILE_SIZE_LIMIT=$filesize; " .
2447 "MW_WALL_CLOCK_LIMIT=$wallTime; " .
2448 "MW_USE_LOG_PIPE=yes"
2449 );
2450 $useLogPipe = true;
2451 } elseif ( $includeStderr ) {
2452 $cmd .= ' 2>&1';
2453 }
2454 } elseif ( $includeStderr ) {
2455 $cmd .= ' 2>&1';
2456 }
2457 wfDebug( "wfShellExec: $cmd\n" );
2458
2459 // Don't try to execute commands that exceed Linux's MAX_ARG_STRLEN.
2460 // Other platforms may be more accomodating, but we don't want to be
2461 // accomodating, because very long commands probably include user
2462 // input. See T129506.
2463 if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) {
2464 throw new Exception( __METHOD__ .
2465 '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
2466 }
2467
2468 $desc = [
2469 0 => [ 'file', 'php://stdin', 'r' ],
2470 1 => [ 'pipe', 'w' ],
2471 2 => [ 'file', 'php://stderr', 'w' ] ];
2472 if ( $useLogPipe ) {
2473 $desc[3] = [ 'pipe', 'w' ];
2474 }
2475 $pipes = null;
2476 $scoped = Profiler::instance()->scopedProfileIn( __FUNCTION__ . '-' . $profileMethod );
2477 $proc = proc_open( $cmd, $desc, $pipes );
2478 if ( !$proc ) {
2479 wfDebugLog( 'exec', "proc_open() failed: $cmd" );
2480 $retval = -1;
2481 return '';
2482 }
2483 $outBuffer = $logBuffer = '';
2484 $emptyArray = [];
2485 $status = false;
2486 $logMsg = false;
2487
2488 /* According to the documentation, it is possible for stream_select()
2489 * to fail due to EINTR. I haven't managed to induce this in testing
2490 * despite sending various signals. If it did happen, the error
2491 * message would take the form:
2492 *
2493 * stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
2494 *
2495 * where [4] is the value of the macro EINTR and "Interrupted system
2496 * call" is string which according to the Linux manual is "possibly"
2497 * localised according to LC_MESSAGES.
2498 */
2499 $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
2500 $eintrMessage = "stream_select(): unable to select [$eintr]";
2501
2502 // Build a table mapping resource IDs to pipe FDs to work around a
2503 // PHP 5.3 issue in which stream_select() does not preserve array keys
2504 // <https://bugs.php.net/bug.php?id=53427>.
2505 $fds = [];
2506 foreach ( $pipes as $fd => $pipe ) {
2507 $fds[(int)$pipe] = $fd;
2508 }
2509
2510 $running = true;
2511 $timeout = null;
2512 $numReadyPipes = 0;
2513
2514 while ( $running === true || $numReadyPipes !== 0 ) {
2515 if ( $running ) {
2516 $status = proc_get_status( $proc );
2517 // If the process has terminated, switch to nonblocking selects
2518 // for getting any data still waiting to be read.
2519 if ( !$status['running'] ) {
2520 $running = false;
2521 $timeout = 0;
2522 }
2523 }
2524
2525 $readyPipes = $pipes;
2526
2527 // Clear last error
2528 \MediaWiki\suppressWarnings();
2529 trigger_error( '' );
2530 $numReadyPipes = stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout );
2531 \MediaWiki\restoreWarnings();
2532
2533 if ( $numReadyPipes === false ) {
2534 // @codingStandardsIgnoreEnd
2535 $error = error_get_last();
2536 if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
2537 continue;
2538 } else {
2539 trigger_error( $error['message'], E_USER_WARNING );
2540 $logMsg = $error['message'];
2541 break;
2542 }
2543 }
2544 foreach ( $readyPipes as $pipe ) {
2545 $block = fread( $pipe, 65536 );
2546 $fd = $fds[(int)$pipe];
2547 if ( $block === '' ) {
2548 // End of file
2549 fclose( $pipes[$fd] );
2550 unset( $pipes[$fd] );
2551 if ( !$pipes ) {
2552 break 2;
2553 }
2554 } elseif ( $block === false ) {
2555 // Read error
2556 $logMsg = "Error reading from pipe";
2557 break 2;
2558 } elseif ( $fd == 1 ) {
2559 // From stdout
2560 $outBuffer .= $block;
2561 } elseif ( $fd == 3 ) {
2562 // From log FD
2563 $logBuffer .= $block;
2564 if ( strpos( $block, "\n" ) !== false ) {
2565 $lines = explode( "\n", $logBuffer );
2566 $logBuffer = array_pop( $lines );
2567 foreach ( $lines as $line ) {
2568 wfDebugLog( 'exec', $line );
2569 }
2570 }
2571 }
2572 }
2573 }
2574
2575 foreach ( $pipes as $pipe ) {
2576 fclose( $pipe );
2577 }
2578
2579 // Use the status previously collected if possible, since proc_get_status()
2580 // just calls waitpid() which will not return anything useful the second time.
2581 if ( $running ) {
2582 $status = proc_get_status( $proc );
2583 }
2584
2585 if ( $logMsg !== false ) {
2586 // Read/select error
2587 $retval = -1;
2588 proc_close( $proc );
2589 } elseif ( $status['signaled'] ) {
2590 $logMsg = "Exited with signal {$status['termsig']}";
2591 $retval = 128 + $status['termsig'];
2592 proc_close( $proc );
2593 } else {
2594 if ( $status['running'] ) {
2595 $retval = proc_close( $proc );
2596 } else {
2597 $retval = $status['exitcode'];
2598 proc_close( $proc );
2599 }
2600 if ( $retval == 127 ) {
2601 $logMsg = "Possibly missing executable file";
2602 } elseif ( $retval >= 129 && $retval <= 192 ) {
2603 $logMsg = "Probably exited with signal " . ( $retval - 128 );
2604 }
2605 }
2606
2607 if ( $logMsg !== false ) {
2608 wfDebugLog( 'exec', "$logMsg: $cmd" );
2609 }
2610
2611 return $outBuffer;
2612}
2613
2630function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
2631 return wfShellExec( $cmd, $retval, $environ, $limits,
2632 [ 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ] );
2633}
2634
2640 static $done = false;
2641 if ( $done ) {
2642 return;
2643 }
2644 $done = true;
2646 putenv( "LC_CTYPE=$wgShellLocale" );
2647 setlocale( LC_CTYPE, $wgShellLocale );
2648}
2649
2662function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
2664 // Give site config file a chance to run the script in a wrapper.
2665 // The caller may likely want to call wfBasename() on $script.
2666 Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2667 $cmd = isset( $options['php'] ) ? [ $options['php'] ] : [ $wgPhpCli ];
2668 if ( isset( $options['wrapper'] ) ) {
2669 $cmd[] = $options['wrapper'];
2670 }
2671 $cmd[] = $script;
2672 // Escape each parameter for shell
2673 return wfEscapeShellArg( array_merge( $cmd, $parameters ) );
2674}
2675
2686function wfMerge( $old, $mine, $yours, &$result ) {
2688
2689 # This check may also protect against code injection in
2690 # case of broken installations.
2691 MediaWiki\suppressWarnings();
2692 $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2693 MediaWiki\restoreWarnings();
2694
2695 if ( !$haveDiff3 ) {
2696 wfDebug( "diff3 not found\n" );
2697 return false;
2698 }
2699
2700 # Make temporary files
2701 $td = wfTempDir();
2702 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2703 $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
2704 $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
2705
2706 # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2707 # a newline character. To avoid this, we normalize the trailing whitespace before
2708 # creating the diff.
2709
2710 fwrite( $oldtextFile, rtrim( $old ) . "\n" );
2711 fclose( $oldtextFile );
2712 fwrite( $mytextFile, rtrim( $mine ) . "\n" );
2713 fclose( $mytextFile );
2714 fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
2715 fclose( $yourtextFile );
2716
2717 # Check for a conflict
2718 $cmd = wfEscapeShellArg( $wgDiff3, '-a', '--overlap-only', $mytextName,
2719 $oldtextName, $yourtextName );
2720 $handle = popen( $cmd, 'r' );
2721
2722 if ( fgets( $handle, 1024 ) ) {
2723 $conflict = true;
2724 } else {
2725 $conflict = false;
2726 }
2727 pclose( $handle );
2728
2729 # Merge differences
2730 $cmd = wfEscapeShellArg( $wgDiff3, '-a', '-e', '--merge', $mytextName,
2731 $oldtextName, $yourtextName );
2732 $handle = popen( $cmd, 'r' );
2733 $result = '';
2734 do {
2735 $data = fread( $handle, 8192 );
2736 if ( strlen( $data ) == 0 ) {
2737 break;
2738 }
2739 $result .= $data;
2740 } while ( true );
2741 pclose( $handle );
2742 unlink( $mytextName );
2743 unlink( $oldtextName );
2744 unlink( $yourtextName );
2745
2746 if ( $result === '' && $old !== '' && !$conflict ) {
2747 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
2748 $conflict = true;
2749 }
2750 return !$conflict;
2751}
2752
2764function wfDiff( $before, $after, $params = '-u' ) {
2765 if ( $before == $after ) {
2766 return '';
2767 }
2768
2770 MediaWiki\suppressWarnings();
2771 $haveDiff = $wgDiff && file_exists( $wgDiff );
2772 MediaWiki\restoreWarnings();
2773
2774 # This check may also protect against code injection in
2775 # case of broken installations.
2776 if ( !$haveDiff ) {
2777 wfDebug( "diff executable not found\n" );
2778 $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
2779 $format = new UnifiedDiffFormatter();
2780 return $format->format( $diffs );
2781 }
2782
2783 # Make temporary files
2784 $td = wfTempDir();
2785 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2786 $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
2787
2788 fwrite( $oldtextFile, $before );
2789 fclose( $oldtextFile );
2790 fwrite( $newtextFile, $after );
2791 fclose( $newtextFile );
2792
2793 // Get the diff of the two files
2794 $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
2795
2796 $h = popen( $cmd, 'r' );
2797 if ( !$h ) {
2798 unlink( $oldtextName );
2799 unlink( $newtextName );
2800 throw new Exception( __METHOD__ . '(): popen() failed' );
2801 }
2802
2803 $diff = '';
2804
2805 do {
2806 $data = fread( $h, 8192 );
2807 if ( strlen( $data ) == 0 ) {
2808 break;
2809 }
2810 $diff .= $data;
2811 } while ( true );
2812
2813 // Clean up
2814 pclose( $h );
2815 unlink( $oldtextName );
2816 unlink( $newtextName );
2817
2818 // Kill the --- and +++ lines. They're not useful.
2819 $diff_lines = explode( "\n", $diff );
2820 if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
2821 unset( $diff_lines[0] );
2822 }
2823 if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
2824 unset( $diff_lines[1] );
2825 }
2826
2827 $diff = implode( "\n", $diff_lines );
2828
2829 return $diff;
2830}
2831
2847function wfUsePHP( $req_ver ) {
2848 $php_ver = PHP_VERSION;
2849
2850 if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
2851 throw new MWException( "PHP $req_ver required--this is only $php_ver" );
2852 }
2853}
2854
2877function wfUseMW( $req_ver ) {
2879
2880 if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
2881 throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
2882 }
2883}
2884
2897function wfBaseName( $path, $suffix = '' ) {
2898 if ( $suffix == '' ) {
2899 $encSuffix = '';
2900 } else {
2901 $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
2902 }
2903
2904 $matches = [];
2905 if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
2906 return $matches[1];
2907 } else {
2908 return '';
2909 }
2910}
2911
2921function wfRelativePath( $path, $from ) {
2922 // Normalize mixed input on Windows...
2923 $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
2924 $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
2925
2926 // Trim trailing slashes -- fix for drive root
2927 $path = rtrim( $path, DIRECTORY_SEPARATOR );
2928 $from = rtrim( $from, DIRECTORY_SEPARATOR );
2929
2930 $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
2931 $against = explode( DIRECTORY_SEPARATOR, $from );
2932
2933 if ( $pieces[0] !== $against[0] ) {
2934 // Non-matching Windows drive letters?
2935 // Return a full path.
2936 return $path;
2937 }
2938
2939 // Trim off common prefix
2940 while ( count( $pieces ) && count( $against )
2941 && $pieces[0] == $against[0] ) {
2942 array_shift( $pieces );
2943 array_shift( $against );
2944 }
2945
2946 // relative dots to bump us to the parent
2947 while ( count( $against ) ) {
2948 array_unshift( $pieces, '..' );
2949 array_shift( $against );
2950 }
2951
2952 array_push( $pieces, wfBaseName( $path ) );
2953
2954 return implode( DIRECTORY_SEPARATOR, $pieces );
2955}
2956
2974function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
2975 $lowercase = true, $engine = 'auto'
2976) {
2977 return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
2978}
2979
2984function wfFixSessionID() {
2985 wfDeprecated( __FUNCTION__, '1.27' );
2986}
2987
2995 wfDeprecated( __FUNCTION__, '1.27' );
2996 $session = SessionManager::getGlobalSession();
2997 $delay = $session->delaySave();
2998
2999 $session->resetId();
3000
3001 // Make sure a session is started, since that's what the old
3002 // wfResetSessionID() did.
3003 if ( session_id() !== $session->getId() ) {
3004 wfSetupSession( $session->getId() );
3005 }
3006
3007 ScopedCallback::consume( $delay );
3008}
3009
3019function wfSetupSession( $sessionId = false ) {
3020 wfDeprecated( __FUNCTION__, '1.27' );
3021
3022 if ( $sessionId ) {
3023 session_id( $sessionId );
3024 }
3025
3026 $session = SessionManager::getGlobalSession();
3027 $session->persist();
3028
3029 if ( session_id() !== $session->getId() ) {
3030 session_id( $session->getId() );
3031 }
3032 MediaWiki\quietCall( 'session_start' );
3033}
3034
3041function wfGetPrecompiledData( $name ) {
3042 global $IP;
3043
3044 $file = "$IP/serialized/$name";
3045 if ( file_exists( $file ) ) {
3046 $blob = file_get_contents( $file );
3047 if ( $blob ) {
3048 return unserialize( $blob );
3049 }
3050 }
3051 return false;
3052}
3053
3060function wfMemcKey( /*...*/ ) {
3061 return call_user_func_array(
3062 [ ObjectCache::getLocalClusterInstance(), 'makeKey' ],
3063 func_get_args()
3064 );
3065}
3066
3077function wfForeignMemcKey( $db, $prefix /*...*/ ) {
3078 $args = array_slice( func_get_args(), 2 );
3079 $keyspace = $prefix ? "$db-$prefix" : $db;
3080 return call_user_func_array(
3081 [ ObjectCache::getLocalClusterInstance(), 'makeKeyInternal' ],
3082 [ $keyspace, $args ]
3083 );
3084}
3085
3097function wfGlobalCacheKey( /*...*/ ) {
3098 return call_user_func_array(
3099 [ ObjectCache::getLocalClusterInstance(), 'makeGlobalKey' ],
3100 func_get_args()
3101 );
3102}
3103
3110function wfWikiID() {
3112 if ( $wgDBprefix ) {
3113 return "$wgDBname-$wgDBprefix";
3114 } else {
3115 return $wgDBname;
3116 }
3117}
3118
3126function wfSplitWikiID( $wiki ) {
3127 $bits = explode( '-', $wiki, 2 );
3128 if ( count( $bits ) < 2 ) {
3129 $bits[] = '';
3130 }
3131 return $bits;
3132}
3133
3156function wfGetDB( $db, $groups = [], $wiki = false ) {
3157 return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
3158}
3159
3166function wfGetLB( $wiki = false ) {
3167 return wfGetLBFactory()->getMainLB( $wiki );
3168}
3169
3175function wfGetLBFactory() {
3176 return LBFactory::singleton();
3177}
3178
3187function wfFindFile( $title, $options = [] ) {
3188 return RepoGroup::singleton()->findFile( $title, $options );
3189}
3190
3198function wfLocalFile( $title ) {
3199 return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
3200}
3201
3210 return $wgMiserMode
3211 || ( SiteStats::pages() > 100000
3212 && SiteStats::edits() > 1000000
3213 && SiteStats::users() > 10000 );
3214}
3215
3224function wfScript( $script = 'index' ) {
3226 if ( $script === 'index' ) {
3227 return $wgScript;
3228 } elseif ( $script === 'load' ) {
3229 return $wgLoadScript;
3230 } else {
3231 return "{$wgScriptPath}/{$script}.php";
3232 }
3233}
3234
3240function wfGetScriptUrl() {
3241 if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
3242 /* as it was called, minus the query string.
3243 *
3244 * Some sites use Apache rewrite rules to handle subdomains,
3245 * and have PHP set up in a weird way that causes PHP_SELF
3246 * to contain the rewritten URL instead of the one that the
3247 * outside world sees.
3248 *
3249 * If in this mode, use SCRIPT_URL instead, which mod_rewrite
3250 * provides containing the "before" URL.
3251 */
3252 return $_SERVER['SCRIPT_NAME'];
3253 } else {
3254 return $_SERVER['URL'];
3255 }
3256}
3257
3265function wfBoolToStr( $value ) {
3266 return $value ? 'true' : 'false';
3267}
3268
3274function wfGetNull() {
3275 return wfIsWindows() ? 'NUL' : '/dev/null';
3276}
3277
3301 $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
3302) {
3303 if ( $timeout === null ) {
3304 $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
3305 }
3306
3307 if ( $cluster === '*' ) {
3308 $cluster = false;
3309 $wiki = false;
3310 } elseif ( $wiki === false ) {
3311 $wiki = wfWikiID();
3312 }
3313
3314 try {
3315 wfGetLBFactory()->waitForReplication( [
3316 'wiki' => $wiki,
3317 'cluster' => $cluster,
3318 'timeout' => $timeout,
3319 // B/C: first argument used to be "max seconds of lag"; ignore such values
3320 'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
3321 ] );
3322 } catch ( DBReplicationWaitError $e ) {
3323 return false;
3324 }
3325
3326 return true;
3327}
3328
3336function wfCountDown( $seconds ) {
3337 for ( $i = $seconds; $i >= 0; $i-- ) {
3338 if ( $i != $seconds ) {
3339 echo str_repeat( "\x08", strlen( $i + 1 ) );
3340 }
3341 echo $i;
3342 flush();
3343 if ( $i ) {
3344 sleep( 1 );
3345 }
3346 }
3347 echo "\n";
3348}
3349
3360 $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
3361 $name = wfBaseName( $name );
3362 $name = preg_replace(
3363 "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
3364 '-',
3365 $name
3366 );
3367 return $name;
3368}
3369
3375function wfMemoryLimit() {
3377 $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
3378 if ( $memlimit != -1 ) {
3379 $conflimit = wfShorthandToInteger( $wgMemoryLimit );
3380 if ( $conflimit == -1 ) {
3381 wfDebug( "Removing PHP's memory limit\n" );
3382 MediaWiki\suppressWarnings();
3383 ini_set( 'memory_limit', $conflimit );
3384 MediaWiki\restoreWarnings();
3385 return $conflimit;
3386 } elseif ( $conflimit > $memlimit ) {
3387 wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
3388 MediaWiki\suppressWarnings();
3389 ini_set( 'memory_limit', $conflimit );
3390 MediaWiki\restoreWarnings();
3391 return $conflimit;
3392 }
3393 }
3394 return $memlimit;
3395}
3396
3405
3406 $timeLimit = ini_get( 'max_execution_time' );
3407 // Note that CLI scripts use 0
3408 if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
3409 set_time_limit( $wgTransactionalTimeLimit );
3410 }
3411
3412 ignore_user_abort( true ); // ignore client disconnects
3413
3414 return $timeLimit;
3415}
3416
3424function wfShorthandToInteger( $string = '', $default = -1 ) {
3425 $string = trim( $string );
3426 if ( $string === '' ) {
3427 return $default;
3428 }
3429 $last = $string[strlen( $string ) - 1];
3430 $val = intval( $string );
3431 switch ( $last ) {
3432 case 'g':
3433 case 'G':
3434 $val *= 1024;
3435 // break intentionally missing
3436 case 'm':
3437 case 'M':
3438 $val *= 1024;
3439 // break intentionally missing
3440 case 'k':
3441 case 'K':
3442 $val *= 1024;
3443 }
3444
3445 return $val;
3446}
3447
3455function wfBCP47( $code ) {
3456 $codeSegment = explode( '-', $code );
3457 $codeBCP = [];
3458 foreach ( $codeSegment as $segNo => $seg ) {
3459 // when previous segment is x, it is a private segment and should be lc
3460 if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
3461 $codeBCP[$segNo] = strtolower( $seg );
3462 // ISO 3166 country code
3463 } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
3464 $codeBCP[$segNo] = strtoupper( $seg );
3465 // ISO 15924 script code
3466 } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
3467 $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
3468 // Use lowercase for other cases
3469 } else {
3470 $codeBCP[$segNo] = strtolower( $seg );
3471 }
3472 }
3473 $langCode = implode( '-', $codeBCP );
3474 return $langCode;
3475}
3476
3483function wfGetCache( $cacheType ) {
3484 return ObjectCache::getInstance( $cacheType );
3485}
3486
3496
3506
3516
3527function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
3528 return Hooks::run( $event, $args, $deprecatedVersion );
3529}
3530
3545function wfUnpack( $format, $data, $length = false ) {
3546 if ( $length !== false ) {
3547 $realLen = strlen( $data );
3548 if ( $realLen < $length ) {
3549 throw new MWException( "Tried to use wfUnpack on a "
3550 . "string of length $realLen, but needed one "
3551 . "of at least length $length."
3552 );
3553 }
3554 }
3555
3556 MediaWiki\suppressWarnings();
3557 $result = unpack( $format, $data );
3558 MediaWiki\restoreWarnings();
3559
3560 if ( $result === false ) {
3561 // If it cannot extract the packed data.
3562 throw new MWException( "unpack could not unpack binary data" );
3563 }
3564 return $result;
3565}
3566
3581function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
3582 # Handle redirects; callers almost always hit wfFindFile() anyway,
3583 # so just use that method because it has a fast process cache.
3584 $file = wfFindFile( $name ); // get the final name
3585 $name = $file ? $file->getTitle()->getDBkey() : $name;
3586
3587 # Run the extension hook
3588 $bad = false;
3589 if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
3590 return $bad;
3591 }
3592
3594 $key = wfMemcKey( 'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist ) );
3595 $badImages = $cache->get( $key );
3596
3597 if ( $badImages === false ) { // cache miss
3598 if ( $blacklist === null ) {
3599 $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
3600 }
3601 # Build the list now
3602 $badImages = [];
3603 $lines = explode( "\n", $blacklist );
3604 foreach ( $lines as $line ) {
3605 # List items only
3606 if ( substr( $line, 0, 1 ) !== '*' ) {
3607 continue;
3608 }
3609
3610 # Find all links
3611 $m = [];
3612 if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
3613 continue;
3614 }
3615
3616 $exceptions = [];
3617 $imageDBkey = false;
3618 foreach ( $m[1] as $i => $titleText ) {
3619 $title = Title::newFromText( $titleText );
3620 if ( !is_null( $title ) ) {
3621 if ( $i == 0 ) {
3622 $imageDBkey = $title->getDBkey();
3623 } else {
3624 $exceptions[$title->getPrefixedDBkey()] = true;
3625 }
3626 }
3627 }
3628
3629 if ( $imageDBkey !== false ) {
3630 $badImages[$imageDBkey] = $exceptions;
3631 }
3632 }
3633 $cache->set( $key, $badImages, 60 );
3634 }
3635
3636 $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
3637 $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
3638
3639 return $bad;
3640}
3641
3649function wfCanIPUseHTTPS( $ip ) {
3650 $canDo = true;
3651 Hooks::run( 'CanIPUseHTTPS', [ $ip, &$canDo ] );
3652 return !!$canDo;
3653}
3654
3662function wfIsInfinity( $str ) {
3663 $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
3664 return in_array( $str, $infinityValues );
3665}
3666
3683
3684 $multipliers = [ 1 ];
3685 if ( $wgResponsiveImages ) {
3686 // These available sizes are hardcoded currently elsewhere in MediaWiki.
3687 // @see Linker::processResponsiveImages
3688 $multipliers[] = 1.5;
3689 $multipliers[] = 2;
3690 }
3691
3692 $handler = $file->getHandler();
3693 if ( !$handler || !isset( $params['width'] ) ) {
3694 return false;
3695 }
3696
3697 $basicParams = [];
3698 if ( isset( $params['page'] ) ) {
3699 $basicParams['page'] = $params['page'];
3700 }
3701
3702 $thumbLimits = [];
3703 $imageLimits = [];
3704 // Expand limits to account for multipliers
3705 foreach ( $multipliers as $multiplier ) {
3706 $thumbLimits = array_merge( $thumbLimits, array_map(
3707 function ( $width ) use ( $multiplier ) {
3708 return round( $width * $multiplier );
3709 }, $wgThumbLimits )
3710 );
3711 $imageLimits = array_merge( $imageLimits, array_map(
3712 function ( $pair ) use ( $multiplier ) {
3713 return [
3714 round( $pair[0] * $multiplier ),
3715 round( $pair[1] * $multiplier ),
3716 ];
3717 }, $wgImageLimits )
3718 );
3719 }
3720
3721 // Check if the width matches one of $wgThumbLimits
3722 if ( in_array( $params['width'], $thumbLimits ) ) {
3723 $normalParams = $basicParams + [ 'width' => $params['width'] ];
3724 // Append any default values to the map (e.g. "lossy", "lossless", ...)
3725 $handler->normaliseParams( $file, $normalParams );
3726 } else {
3727 // If not, then check if the width matchs one of $wgImageLimits
3728 $match = false;
3729 foreach ( $imageLimits as $pair ) {
3730 $normalParams = $basicParams + [ 'width' => $pair[0], 'height' => $pair[1] ];
3731 // Decide whether the thumbnail should be scaled on width or height.
3732 // Also append any default values to the map (e.g. "lossy", "lossless", ...)
3733 $handler->normaliseParams( $file, $normalParams );
3734 // Check if this standard thumbnail size maps to the given width
3735 if ( $normalParams['width'] == $params['width'] ) {
3736 $match = true;
3737 break;
3738 }
3739 }
3740 if ( !$match ) {
3741 return false; // not standard for description pages
3742 }
3743 }
3744
3745 // Check that the given values for non-page, non-width, params are just defaults
3746 foreach ( $params as $key => $value ) {
3747 if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3748 return false;
3749 }
3750 }
3751
3752 return true;
3753}
3754
3767function wfArrayPlus2d( array $baseArray, array $newValues ) {
3768 // First merge items that are in both arrays
3769 foreach ( $baseArray as $name => &$groupVal ) {
3770 if ( isset( $newValues[$name] ) ) {
3771 $groupVal += $newValues[$name];
3772 }
3773 }
3774 // Now add items that didn't exist yet
3775 $baseArray += $newValues;
3776
3777 return $baseArray;
3778}
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
These are additional characters that should be replaced with '-' 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.
$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 slaves 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()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell,...
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...
const TS_UNIX
Unix time - the number of seconds since 1970-01-01 00:00:00 UTC.
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...
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
wfStripIllegalFilenameChars( $name)
Replace all invalid characters with - Additional characters can be defined in $wgIllegalFileChars (se...
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.
$i
Definition Parser.php:1694
$state
Definition Parser.php:1693
$wgOut
Definition Setup.php:804
global $wgCommandLineMode
Definition Setup.php:513
if(is_null($wgLocalTZoffset)) if(! $wgDBerrorLogTZ) $wgRequest
Definition Setup.php:657
$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.
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:1365
static header( $code)
Output an HTTP status code header.
static singleton()
Get an LBFactory instance.
Definition LBFactory.php:77
MediaWiki exception.
Library for creating and parsing MW-style timestamps.
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
static getLocalServerInstance( $fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
static getInstance( $id)
Get a cached instance of the specified type of cache object.
static getLocalClusterInstance()
Get the main cluster-local cache object.
Stub profiler that does nothing.
static instance()
Singleton.
Definition Profiler.php:60
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 consume(ScopedCallback &$sc=null)
Trigger a scoped callback and destroy it.
static edits()
static users()
static pages()
static makeVariablesScript( $data)
Definition Skin.php:329
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition Title.php:606
static newFromText( $text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition Title.php:277
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:266
const PROTO_HTTPS
Definition Defines.php:263
const PROTO_CURRENT
Definition Defines.php:265
const PROTO_INTERNAL
Definition Defines.php:267
const PROTO_HTTP
Definition Defines.php:262
const SHELL_MAX_ARG_STRLEN
Definition Defines.php:313
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:1007
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:1048
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
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':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:1799
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
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 and may include noclasses after processing 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
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:1042
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:1810
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:2413
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:944
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition hooks.txt:2555
error also a ContextSource you ll probably need to make sure the header is varied on $request
Definition hooks.txt:2530
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:1081
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:846
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:1816
see documentation in includes Linker php for Linker::makeImageLink & $time
Definition hooks.txt:1615
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:314
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:885
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:1458
returning false will NOT prevent logging $e
Definition hooks.txt:1940
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:847
$from
if(count( $args)==0) $dir
if( $limit) $timestamp
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:44
$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
$version
$last
$lines
Definition router.php:66
$params