MediaWiki  master
GlobalFunctions.php
Go to the documentation of this file.
1 <?php
23 if ( !defined( 'MEDIAWIKI' ) ) {
24  die( "This file is part of MediaWiki, it is not a valid entry point" );
25 }
26 
34 
35 // Hide compatibility functions from Doxygen
37 
45 // hash_equals function only exists in PHP >= 5.6.0
46 // https://secure.php.net/hash_equals
47 if ( !function_exists( 'hash_equals' ) ) {
73  function hash_equals( $known_string, $user_string ) {
74  // Strict type checking as in PHP's native implementation
75  if ( !is_string( $known_string ) ) {
76  trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
77  gettype( $known_string ) . ' given', E_USER_WARNING );
78 
79  return false;
80  }
81 
82  if ( !is_string( $user_string ) ) {
83  trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
84  gettype( $user_string ) . ' given', E_USER_WARNING );
85 
86  return false;
87  }
88 
89  $known_string_len = strlen( $known_string );
90  if ( $known_string_len !== strlen( $user_string ) ) {
91  return false;
92  }
93 
94  $result = 0;
95  for ( $i = 0; $i < $known_string_len; $i++ ) {
96  $result |= ord( $known_string[$i] ) ^ ord( $user_string[$i] );
97  }
98 
99  return ( $result === 0 );
100  }
101 }
103 
114 function wfLoadExtension( $ext, $path = null ) {
115  if ( !$path ) {
117  $path = "$wgExtensionDirectory/$ext/extension.json";
118  }
120 }
121 
135 function wfLoadExtensions( array $exts ) {
137  $registry = ExtensionRegistry::getInstance();
138  foreach ( $exts as $ext ) {
139  $registry->queue( "$wgExtensionDirectory/$ext/extension.json" );
140  }
141 }
142 
151 function wfLoadSkin( $skin, $path = null ) {
152  if ( !$path ) {
154  $path = "$wgStyleDirectory/$skin/skin.json";
155  }
157 }
158 
166 function wfLoadSkins( array $skins ) {
168  $registry = ExtensionRegistry::getInstance();
169  foreach ( $skins as $skin ) {
170  $registry->queue( "$wgStyleDirectory/$skin/skin.json" );
171  }
172 }
173 
180 function wfArrayDiff2( $a, $b ) {
181  return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
182 }
183 
189 function wfArrayDiff2_cmp( $a, $b ) {
190  if ( is_string( $a ) && is_string( $b ) ) {
191  return strcmp( $a, $b );
192  } elseif ( count( $a ) !== count( $b ) ) {
193  return count( $a ) < count( $b ) ? -1 : 1;
194  } else {
195  reset( $a );
196  reset( $b );
197  while ( key( $a ) !== null && key( $b ) !== null ) {
198  $valueA = current( $a );
199  $valueB = current( $b );
200  $cmp = strcmp( $valueA, $valueB );
201  if ( $cmp !== 0 ) {
202  return $cmp;
203  }
204  next( $a );
205  next( $b );
206  }
207  return 0;
208  }
209 }
210 
219 function wfArrayFilter( array $arr, callable $callback ) {
220  if ( defined( 'ARRAY_FILTER_USE_BOTH' ) ) {
221  return array_filter( $arr, $callback, ARRAY_FILTER_USE_BOTH );
222  }
223  $filteredKeys = array_filter( array_keys( $arr ), function ( $key ) use ( $arr, $callback ) {
224  return call_user_func( $callback, $arr[$key], $key );
225  } );
226  return array_intersect_key( $arr, array_fill_keys( $filteredKeys, true ) );
227 }
228 
237 function wfArrayFilterByKey( array $arr, callable $callback ) {
238  return wfArrayFilter( $arr, function ( $val, $key ) use ( $callback ) {
239  return call_user_func( $callback, $key );
240  } );
241 }
242 
252 function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
253  if ( is_null( $changed ) ) {
254  throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
255  }
256  if ( $default[$key] !== $value ) {
257  $changed[$key] = $value;
258  }
259 }
260 
280 function wfMergeErrorArrays( /*...*/ ) {
281  $args = func_get_args();
282  $out = [];
283  foreach ( $args as $errors ) {
284  foreach ( $errors as $params ) {
285  $originalParams = $params;
286  if ( $params[0] instanceof MessageSpecifier ) {
287  $msg = $params[0];
288  $params = array_merge( [ $msg->getKey() ], $msg->getParams() );
289  }
290  # @todo FIXME: Sometimes get nested arrays for $params,
291  # which leads to E_NOTICEs
292  $spec = implode( "\t", $params );
293  $out[$spec] = $originalParams;
294  }
295  }
296  return array_values( $out );
297 }
298 
307 function wfArrayInsertAfter( array $array, array $insert, $after ) {
308  // Find the offset of the element to insert after.
309  $keys = array_keys( $array );
310  $offsetByKey = array_flip( $keys );
311 
312  $offset = $offsetByKey[$after];
313 
314  // Insert at the specified offset
315  $before = array_slice( $array, 0, $offset + 1, true );
316  $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
317 
318  $output = $before + $insert + $after;
319 
320  return $output;
321 }
322 
330 function wfObjectToArray( $objOrArray, $recursive = true ) {
331  $array = [];
332  if ( is_object( $objOrArray ) ) {
333  $objOrArray = get_object_vars( $objOrArray );
334  }
335  foreach ( $objOrArray as $key => $value ) {
336  if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
338  }
339 
340  $array[$key] = $value;
341  }
342 
343  return $array;
344 }
345 
356 function wfRandom() {
357  // The maximum random value is "only" 2^31-1, so get two random
358  // values to reduce the chance of dupes
359  $max = mt_getrandmax() + 1;
360  $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
361  return $rand;
362 }
363 
374 function wfRandomString( $length = 32 ) {
375  $str = '';
376  for ( $n = 0; $n < $length; $n += 7 ) {
377  $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
378  }
379  return substr( $str, 0, $length );
380 }
381 
409 function wfUrlencode( $s ) {
410  static $needle;
411 
412  if ( is_null( $s ) ) {
413  $needle = null;
414  return '';
415  }
416 
417  if ( is_null( $needle ) ) {
418  $needle = [ '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F', '%7E' ];
419  if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
420  ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
421  ) {
422  $needle[] = '%3A';
423  }
424  }
425 
426  $s = urlencode( $s );
427  $s = str_ireplace(
428  $needle,
429  [ ';', '@', '$', '!', '*', '(', ')', ',', '/', '~', ':' ],
430  $s
431  );
432 
433  return $s;
434 }
435 
446 function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) {
447  if ( !is_null( $array2 ) ) {
448  $array1 = $array1 + $array2;
449  }
450 
451  $cgi = '';
452  foreach ( $array1 as $key => $value ) {
453  if ( !is_null( $value ) && $value !== false ) {
454  if ( $cgi != '' ) {
455  $cgi .= '&';
456  }
457  if ( $prefix !== '' ) {
458  $key = $prefix . "[$key]";
459  }
460  if ( is_array( $value ) ) {
461  $firstTime = true;
462  foreach ( $value as $k => $v ) {
463  $cgi .= $firstTime ? '' : '&';
464  if ( is_array( $v ) ) {
465  $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" );
466  } else {
467  $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
468  }
469  $firstTime = false;
470  }
471  } else {
472  if ( is_object( $value ) ) {
473  $value = $value->__toString();
474  }
475  $cgi .= urlencode( $key ) . '=' . urlencode( $value );
476  }
477  }
478  }
479  return $cgi;
480 }
481 
491 function wfCgiToArray( $query ) {
492  if ( isset( $query[0] ) && $query[0] == '?' ) {
493  $query = substr( $query, 1 );
494  }
495  $bits = explode( '&', $query );
496  $ret = [];
497  foreach ( $bits as $bit ) {
498  if ( $bit === '' ) {
499  continue;
500  }
501  if ( strpos( $bit, '=' ) === false ) {
502  // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
503  $key = $bit;
504  $value = '';
505  } else {
506  list( $key, $value ) = explode( '=', $bit );
507  }
508  $key = urldecode( $key );
509  $value = urldecode( $value );
510  if ( strpos( $key, '[' ) !== false ) {
511  $keys = array_reverse( explode( '[', $key ) );
512  $key = array_pop( $keys );
513  $temp = $value;
514  foreach ( $keys as $k ) {
515  $k = substr( $k, 0, -1 );
516  $temp = [ $k => $temp ];
517  }
518  if ( isset( $ret[$key] ) ) {
519  $ret[$key] = array_merge( $ret[$key], $temp );
520  } else {
521  $ret[$key] = $temp;
522  }
523  } else {
524  $ret[$key] = $value;
525  }
526  }
527  return $ret;
528 }
529 
538 function wfAppendQuery( $url, $query ) {
539  if ( is_array( $query ) ) {
541  }
542  if ( $query != '' ) {
543  // Remove the fragment, if there is one
544  $fragment = false;
545  $hashPos = strpos( $url, '#' );
546  if ( $hashPos !== false ) {
547  $fragment = substr( $url, $hashPos );
548  $url = substr( $url, 0, $hashPos );
549  }
550 
551  // Add parameter
552  if ( false === strpos( $url, '?' ) ) {
553  $url .= '?';
554  } else {
555  $url .= '&';
556  }
557  $url .= $query;
558 
559  // Put the fragment back
560  if ( $fragment !== false ) {
561  $url .= $fragment;
562  }
563  }
564  return $url;
565 }
566 
590 function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
592  $wgHttpsPort;
593  if ( $defaultProto === PROTO_CANONICAL ) {
594  $serverUrl = $wgCanonicalServer;
595  } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
596  // Make $wgInternalServer fall back to $wgServer if not set
597  $serverUrl = $wgInternalServer;
598  } else {
599  $serverUrl = $wgServer;
600  if ( $defaultProto === PROTO_CURRENT ) {
601  $defaultProto = $wgRequest->getProtocol() . '://';
602  }
603  }
604 
605  // Analyze $serverUrl to obtain its protocol
606  $bits = wfParseUrl( $serverUrl );
607  $serverHasProto = $bits && $bits['scheme'] != '';
608 
609  if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
610  if ( $serverHasProto ) {
611  $defaultProto = $bits['scheme'] . '://';
612  } else {
613  // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
614  // This really isn't supposed to happen. Fall back to HTTP in this
615  // ridiculous case.
616  $defaultProto = PROTO_HTTP;
617  }
618  }
619 
620  $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
621 
622  if ( substr( $url, 0, 2 ) == '//' ) {
623  $url = $defaultProtoWithoutSlashes . $url;
624  } elseif ( substr( $url, 0, 1 ) == '/' ) {
625  // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
626  // otherwise leave it alone.
627  $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
628  }
629 
630  $bits = wfParseUrl( $url );
631 
632  // ensure proper port for HTTPS arrives in URL
633  // https://phabricator.wikimedia.org/T67184
634  if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
635  $bits['port'] = $wgHttpsPort;
636  }
637 
638  if ( $bits && isset( $bits['path'] ) ) {
639  $bits['path'] = wfRemoveDotSegments( $bits['path'] );
640  return wfAssembleUrl( $bits );
641  } elseif ( $bits ) {
642  # No path to expand
643  return $url;
644  } elseif ( substr( $url, 0, 1 ) != '/' ) {
645  # URL is a relative path
646  return wfRemoveDotSegments( $url );
647  }
648 
649  # Expanded URL is not valid.
650  return false;
651 }
652 
666 function wfAssembleUrl( $urlParts ) {
667  $result = '';
668 
669  if ( isset( $urlParts['delimiter'] ) ) {
670  if ( isset( $urlParts['scheme'] ) ) {
671  $result .= $urlParts['scheme'];
672  }
673 
674  $result .= $urlParts['delimiter'];
675  }
676 
677  if ( isset( $urlParts['host'] ) ) {
678  if ( isset( $urlParts['user'] ) ) {
679  $result .= $urlParts['user'];
680  if ( isset( $urlParts['pass'] ) ) {
681  $result .= ':' . $urlParts['pass'];
682  }
683  $result .= '@';
684  }
685 
686  $result .= $urlParts['host'];
687 
688  if ( isset( $urlParts['port'] ) ) {
689  $result .= ':' . $urlParts['port'];
690  }
691  }
692 
693  if ( isset( $urlParts['path'] ) ) {
694  $result .= $urlParts['path'];
695  }
696 
697  if ( isset( $urlParts['query'] ) ) {
698  $result .= '?' . $urlParts['query'];
699  }
700 
701  if ( isset( $urlParts['fragment'] ) ) {
702  $result .= '#' . $urlParts['fragment'];
703  }
704 
705  return $result;
706 }
707 
718 function wfRemoveDotSegments( $urlPath ) {
719  $output = '';
720  $inputOffset = 0;
721  $inputLength = strlen( $urlPath );
722 
723  while ( $inputOffset < $inputLength ) {
724  $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
725  $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
726  $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
727  $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
728  $trimOutput = false;
729 
730  if ( $prefixLengthTwo == './' ) {
731  # Step A, remove leading "./"
732  $inputOffset += 2;
733  } elseif ( $prefixLengthThree == '../' ) {
734  # Step A, remove leading "../"
735  $inputOffset += 3;
736  } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
737  # Step B, replace leading "/.$" with "/"
738  $inputOffset += 1;
739  $urlPath[$inputOffset] = '/';
740  } elseif ( $prefixLengthThree == '/./' ) {
741  # Step B, replace leading "/./" with "/"
742  $inputOffset += 2;
743  } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
744  # Step C, replace leading "/..$" with "/" and
745  # remove last path component in output
746  $inputOffset += 2;
747  $urlPath[$inputOffset] = '/';
748  $trimOutput = true;
749  } elseif ( $prefixLengthFour == '/../' ) {
750  # Step C, replace leading "/../" with "/" and
751  # remove last path component in output
752  $inputOffset += 3;
753  $trimOutput = true;
754  } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
755  # Step D, remove "^.$"
756  $inputOffset += 1;
757  } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
758  # Step D, remove "^..$"
759  $inputOffset += 2;
760  } else {
761  # Step E, move leading path segment to output
762  if ( $prefixLengthOne == '/' ) {
763  $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
764  } else {
765  $slashPos = strpos( $urlPath, '/', $inputOffset );
766  }
767  if ( $slashPos === false ) {
768  $output .= substr( $urlPath, $inputOffset );
769  $inputOffset = $inputLength;
770  } else {
771  $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
772  $inputOffset += $slashPos - $inputOffset;
773  }
774  }
775 
776  if ( $trimOutput ) {
777  $slashPos = strrpos( $output, '/' );
778  if ( $slashPos === false ) {
779  $output = '';
780  } else {
781  $output = substr( $output, 0, $slashPos );
782  }
783  }
784  }
785 
786  return $output;
787 }
788 
796 function wfUrlProtocols( $includeProtocolRelative = true ) {
797  global $wgUrlProtocols;
798 
799  // Cache return values separately based on $includeProtocolRelative
800  static $withProtRel = null, $withoutProtRel = null;
801  $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
802  if ( !is_null( $cachedValue ) ) {
803  return $cachedValue;
804  }
805 
806  // Support old-style $wgUrlProtocols strings, for backwards compatibility
807  // with LocalSettings files from 1.5
808  if ( is_array( $wgUrlProtocols ) ) {
809  $protocols = [];
810  foreach ( $wgUrlProtocols as $protocol ) {
811  // Filter out '//' if !$includeProtocolRelative
812  if ( $includeProtocolRelative || $protocol !== '//' ) {
813  $protocols[] = preg_quote( $protocol, '/' );
814  }
815  }
816 
817  $retval = implode( '|', $protocols );
818  } else {
819  // Ignore $includeProtocolRelative in this case
820  // This case exists for pre-1.6 compatibility, and we can safely assume
821  // that '//' won't appear in a pre-1.6 config because protocol-relative
822  // URLs weren't supported until 1.18
823  $retval = $wgUrlProtocols;
824  }
825 
826  // Cache return value
827  if ( $includeProtocolRelative ) {
828  $withProtRel = $retval;
829  } else {
830  $withoutProtRel = $retval;
831  }
832  return $retval;
833 }
834 
841 function wfUrlProtocolsWithoutProtRel() {
842  return wfUrlProtocols( false );
843 }
844 
870 function wfParseUrl( $url ) {
871  global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
872 
873  // Protocol-relative URLs are handled really badly by parse_url(). It's so
874  // bad that the easiest way to handle them is to just prepend 'http:' and
875  // strip the protocol out later.
876  $wasRelative = substr( $url, 0, 2 ) == '//';
877  if ( $wasRelative ) {
878  $url = "http:$url";
879  }
880  MediaWiki\suppressWarnings();
881  $bits = parse_url( $url );
882  MediaWiki\restoreWarnings();
883  // parse_url() returns an array without scheme for some invalid URLs, e.g.
884  // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
885  if ( !$bits || !isset( $bits['scheme'] ) ) {
886  return false;
887  }
888 
889  // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
890  $bits['scheme'] = strtolower( $bits['scheme'] );
891 
892  // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
893  if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
894  $bits['delimiter'] = '://';
895  } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
896  $bits['delimiter'] = ':';
897  // parse_url detects for news: and mailto: the host part of an url as path
898  // We have to correct this wrong detection
899  if ( isset( $bits['path'] ) ) {
900  $bits['host'] = $bits['path'];
901  $bits['path'] = '';
902  }
903  } else {
904  return false;
905  }
906 
907  /* Provide an empty host for eg. file:/// urls (see T30627) */
908  if ( !isset( $bits['host'] ) ) {
909  $bits['host'] = '';
910 
911  // See T47069
912  if ( isset( $bits['path'] ) ) {
913  /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
914  if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
915  $bits['path'] = '/' . $bits['path'];
916  }
917  } else {
918  $bits['path'] = '';
919  }
920  }
921 
922  // If the URL was protocol-relative, fix scheme and delimiter
923  if ( $wasRelative ) {
924  $bits['scheme'] = '';
925  $bits['delimiter'] = '//';
926  }
927  return $bits;
928 }
929 
940 function wfExpandIRI( $url ) {
941  return preg_replace_callback(
942  '/((?:%[89A-F][0-9A-F])+)/i',
943  'wfExpandIRI_callback',
944  wfExpandUrl( $url )
945  );
946 }
947 
953 function wfExpandIRI_callback( $matches ) {
954  return urldecode( $matches[1] );
955 }
956 
963 function wfMakeUrlIndexes( $url ) {
964  $bits = wfParseUrl( $url );
965 
966  // Reverse the labels in the hostname, convert to lower case
967  // For emails reverse domainpart only
968  if ( $bits['scheme'] == 'mailto' ) {
969  $mailparts = explode( '@', $bits['host'], 2 );
970  if ( count( $mailparts ) === 2 ) {
971  $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
972  } else {
973  // No domain specified, don't mangle it
974  $domainpart = '';
975  }
976  $reversedHost = $domainpart . '@' . $mailparts[0];
977  } else {
978  $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
979  }
980  // Add an extra dot to the end
981  // Why? Is it in wrong place in mailto links?
982  if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
983  $reversedHost .= '.';
984  }
985  // Reconstruct the pseudo-URL
986  $prot = $bits['scheme'];
987  $index = $prot . $bits['delimiter'] . $reversedHost;
988  // Leave out user and password. Add the port, path, query and fragment
989  if ( isset( $bits['port'] ) ) {
990  $index .= ':' . $bits['port'];
991  }
992  if ( isset( $bits['path'] ) ) {
993  $index .= $bits['path'];
994  } else {
995  $index .= '/';
996  }
997  if ( isset( $bits['query'] ) ) {
998  $index .= '?' . $bits['query'];
999  }
1000  if ( isset( $bits['fragment'] ) ) {
1001  $index .= '#' . $bits['fragment'];
1002  }
1003 
1004  if ( $prot == '' ) {
1005  return [ "http:$index", "https:$index" ];
1006  } else {
1007  return [ $index ];
1008  }
1009 }
1010 
1017 function wfMatchesDomainList( $url, $domains ) {
1018  $bits = wfParseUrl( $url );
1019  if ( is_array( $bits ) && isset( $bits['host'] ) ) {
1020  $host = '.' . $bits['host'];
1021  foreach ( (array)$domains as $domain ) {
1022  $domain = '.' . $domain;
1023  if ( substr( $host, -strlen( $domain ) ) === $domain ) {
1024  return true;
1025  }
1026  }
1027  }
1028  return false;
1029 }
1030 
1051 function wfDebug( $text, $dest = 'all', array $context = [] ) {
1052  global $wgDebugRawPage, $wgDebugLogPrefix;
1053  global $wgDebugTimestamps, $wgRequestTime;
1054 
1055  if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1056  return;
1057  }
1058 
1059  $text = trim( $text );
1060 
1061  if ( $wgDebugTimestamps ) {
1062  $context['seconds_elapsed'] = sprintf(
1063  '%6.4f',
1064  microtime( true ) - $wgRequestTime
1065  );
1066  $context['memory_used'] = sprintf(
1067  '%5.1fM',
1068  ( memory_get_usage( true ) / ( 1024 * 1024 ) )
1069  );
1070  }
1071 
1072  if ( $wgDebugLogPrefix !== '' ) {
1073  $context['prefix'] = $wgDebugLogPrefix;
1074  }
1075  $context['private'] = ( $dest === false || $dest === 'private' );
1076 
1077  $logger = LoggerFactory::getInstance( 'wfDebug' );
1078  $logger->debug( $text, $context );
1079 }
1080 
1085 function wfIsDebugRawPage() {
1086  static $cache;
1087  if ( $cache !== null ) {
1088  return $cache;
1089  }
1090  # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
1091  if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
1092  || (
1093  isset( $_SERVER['SCRIPT_NAME'] )
1094  && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
1095  )
1096  ) {
1097  $cache = true;
1098  } else {
1099  $cache = false;
1100  }
1101  return $cache;
1102 }
1103 
1109 function wfDebugMem( $exact = false ) {
1110  $mem = memory_get_usage();
1111  if ( !$exact ) {
1112  $mem = floor( $mem / 1024 ) . ' KiB';
1113  } else {
1114  $mem .= ' B';
1115  }
1116  wfDebug( "Memory usage: $mem\n" );
1117 }
1118 
1144 function wfDebugLog(
1145  $logGroup, $text, $dest = 'all', array $context = []
1146 ) {
1147  $text = trim( $text );
1148 
1149  $logger = LoggerFactory::getInstance( $logGroup );
1150  $context['private'] = ( $dest === false || $dest === 'private' );
1151  $logger->info( $text, $context );
1152 }
1153 
1162 function wfLogDBError( $text, array $context = [] ) {
1163  $logger = LoggerFactory::getInstance( 'wfLogDBError' );
1164  $logger->error( trim( $text ), $context );
1165 }
1166 
1180 function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
1181  MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
1182 }
1183 
1194 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1195  MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
1196 }
1197 
1207 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1208  MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
1209 }
1210 
1224 function wfErrorLog( $text, $file, array $context = [] ) {
1225  wfDeprecated( __METHOD__, '1.25' );
1226  $logger = LoggerFactory::getInstance( 'wfErrorLog' );
1227  $context['destination'] = $file;
1228  $logger->info( trim( $text ), $context );
1229 }
1230 
1235 function wfLogProfilingData() {
1236  global $wgDebugLogGroups, $wgDebugRawPage;
1237 
1239  $request = $context->getRequest();
1240 
1241  $profiler = Profiler::instance();
1242  $profiler->setContext( $context );
1243  $profiler->logData();
1244 
1245  // Send out any buffered statsd metrics as needed
1247  MediaWikiServices::getInstance()->getStatsdDataFactory(),
1248  $context->getConfig()
1249  );
1250 
1251  // Profiling must actually be enabled...
1252  if ( $profiler instanceof ProfilerStub ) {
1253  return;
1254  }
1255 
1256  if ( isset( $wgDebugLogGroups['profileoutput'] )
1257  && $wgDebugLogGroups['profileoutput'] === false
1258  ) {
1259  // Explicitly disabled
1260  return;
1261  }
1262  if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1263  return;
1264  }
1265 
1266  $ctx = [ 'elapsed' => $request->getElapsedTime() ];
1267  if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1268  $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
1269  }
1270  if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
1271  $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
1272  }
1273  if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
1274  $ctx['from'] = $_SERVER['HTTP_FROM'];
1275  }
1276  if ( isset( $ctx['forwarded_for'] ) ||
1277  isset( $ctx['client_ip'] ) ||
1278  isset( $ctx['from'] ) ) {
1279  $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
1280  }
1281 
1282  // Don't load $wgUser at this late stage just for statistics purposes
1283  // @todo FIXME: We can detect some anons even if it is not loaded.
1284  // See User::getId()
1285  $user = $context->getUser();
1286  $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
1287 
1288  // Command line script uses a FauxRequest object which does not have
1289  // any knowledge about an URL and throw an exception instead.
1290  try {
1291  $ctx['url'] = urldecode( $request->getRequestURL() );
1292  } catch ( Exception $ignored ) {
1293  // no-op
1294  }
1295 
1296  $ctx['output'] = $profiler->getOutput();
1297 
1298  $log = LoggerFactory::getInstance( 'profileoutput' );
1299  $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1300 }
1301 
1309 function wfIncrStats( $key, $count = 1 ) {
1310  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1311  $stats->updateCount( $key, $count );
1312 }
1313 
1319 function wfReadOnly() {
1320  return MediaWikiServices::getInstance()->getReadOnlyMode()
1321  ->isReadOnly();
1322 }
1323 
1332 function wfReadOnlyReason() {
1333  return MediaWikiServices::getInstance()->getReadOnlyMode()
1334  ->getReason();
1335 }
1336 
1343 function wfConfiguredReadOnlyReason() {
1344  return MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1345  ->getReason();
1346 }
1347 
1363 function wfGetLangObj( $langcode = false ) {
1364  # Identify which language to get or create a language object for.
1365  # Using is_object here due to Stub objects.
1366  if ( is_object( $langcode ) ) {
1367  # Great, we already have the object (hopefully)!
1368  return $langcode;
1369  }
1370 
1372  if ( $langcode === true || $langcode === $wgLanguageCode ) {
1373  # $langcode is the language code of the wikis content language object.
1374  # or it is a boolean and value is true
1375  return $wgContLang;
1376  }
1377 
1378  global $wgLang;
1379  if ( $langcode === false || $langcode === $wgLang->getCode() ) {
1380  # $langcode is the language code of user language object.
1381  # or it was a boolean and value is false
1382  return $wgLang;
1383  }
1384 
1385  $validCodes = array_keys( Language::fetchLanguageNames() );
1386  if ( in_array( $langcode, $validCodes ) ) {
1387  # $langcode corresponds to a valid language.
1388  return Language::factory( $langcode );
1389  }
1390 
1391  # $langcode is a string, but not a valid language code; use content language.
1392  wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1393  return $wgContLang;
1394 }
1395 
1412 function wfMessage( $key /*...*/ ) {
1413  $message = new Message( $key );
1414 
1415  // We call Message::params() to reduce code duplication
1416  $params = func_get_args();
1417  array_shift( $params );
1418  if ( $params ) {
1419  call_user_func_array( [ $message, 'params' ], $params );
1420  }
1421 
1422  return $message;
1423 }
1424 
1437 function wfMessageFallback( /*...*/ ) {
1438  $args = func_get_args();
1439  return call_user_func_array( 'Message::newFallbackSequence', $args );
1440 }
1441 
1450 function wfMsgReplaceArgs( $message, $args ) {
1451  # Fix windows line-endings
1452  # Some messages are split with explode("\n", $msg)
1453  $message = str_replace( "\r", '', $message );
1454 
1455  // Replace arguments
1456  if ( is_array( $args ) && $args ) {
1457  if ( is_array( $args[0] ) ) {
1458  $args = array_values( $args[0] );
1459  }
1460  $replacementKeys = [];
1461  foreach ( $args as $n => $param ) {
1462  $replacementKeys['$' . ( $n + 1 )] = $param;
1463  }
1464  $message = strtr( $message, $replacementKeys );
1465  }
1466 
1467  return $message;
1468 }
1469 
1477 function wfHostname() {
1478  static $host;
1479  if ( is_null( $host ) ) {
1480  # Hostname overriding
1481  global $wgOverrideHostname;
1482  if ( $wgOverrideHostname !== false ) {
1483  # Set static and skip any detection
1484  $host = $wgOverrideHostname;
1485  return $host;
1486  }
1487 
1488  if ( function_exists( 'posix_uname' ) ) {
1489  // This function not present on Windows
1490  $uname = posix_uname();
1491  } else {
1492  $uname = false;
1493  }
1494  if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
1495  $host = $uname['nodename'];
1496  } elseif ( getenv( 'COMPUTERNAME' ) ) {
1497  # Windows computer name
1498  $host = getenv( 'COMPUTERNAME' );
1499  } else {
1500  # This may be a virtual server.
1501  $host = $_SERVER['SERVER_NAME'];
1502  }
1503  }
1504  return $host;
1505 }
1506 
1516 function wfReportTime() {
1517  global $wgRequestTime, $wgShowHostnames;
1518 
1519  $responseTime = round( ( microtime( true ) - $wgRequestTime ) * 1000 );
1520  $reportVars = [ 'wgBackendResponseTime' => $responseTime ];
1521  if ( $wgShowHostnames ) {
1522  $reportVars['wgHostname'] = wfHostname();
1523  }
1524  return Skin::makeVariablesScript( $reportVars );
1525 }
1526 
1537 function wfDebugBacktrace( $limit = 0 ) {
1538  static $disabled = null;
1539 
1540  if ( is_null( $disabled ) ) {
1541  $disabled = !function_exists( 'debug_backtrace' );
1542  if ( $disabled ) {
1543  wfDebug( "debug_backtrace() is disabled\n" );
1544  }
1545  }
1546  if ( $disabled ) {
1547  return [];
1548  }
1549 
1550  if ( $limit ) {
1551  return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1552  } else {
1553  return array_slice( debug_backtrace(), 1 );
1554  }
1555 }
1556 
1565 function wfBacktrace( $raw = null ) {
1567 
1568  if ( $raw === null ) {
1569  $raw = $wgCommandLineMode;
1570  }
1571 
1572  if ( $raw ) {
1573  $frameFormat = "%s line %s calls %s()\n";
1574  $traceFormat = "%s";
1575  } else {
1576  $frameFormat = "<li>%s line %s calls %s()</li>\n";
1577  $traceFormat = "<ul>\n%s</ul>\n";
1578  }
1579 
1580  $frames = array_map( function ( $frame ) use ( $frameFormat ) {
1581  $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
1582  $line = isset( $frame['line'] ) ? $frame['line'] : '-';
1583  $call = $frame['function'];
1584  if ( !empty( $frame['class'] ) ) {
1585  $call = $frame['class'] . $frame['type'] . $call;
1586  }
1587  return sprintf( $frameFormat, $file, $line, $call );
1588  }, wfDebugBacktrace() );
1589 
1590  return sprintf( $traceFormat, implode( '', $frames ) );
1591 }
1592 
1602 function wfGetCaller( $level = 2 ) {
1603  $backtrace = wfDebugBacktrace( $level + 1 );
1604  if ( isset( $backtrace[$level] ) ) {
1605  return wfFormatStackFrame( $backtrace[$level] );
1606  } else {
1607  return 'unknown';
1608  }
1609 }
1610 
1618 function wfGetAllCallers( $limit = 3 ) {
1619  $trace = array_reverse( wfDebugBacktrace() );
1620  if ( !$limit || $limit > count( $trace ) - 1 ) {
1621  $limit = count( $trace ) - 1;
1622  }
1623  $trace = array_slice( $trace, -$limit - 1, $limit );
1624  return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
1625 }
1626 
1633 function wfFormatStackFrame( $frame ) {
1634  if ( !isset( $frame['function'] ) ) {
1635  return 'NO_FUNCTION_GIVEN';
1636  }
1637  return isset( $frame['class'] ) && isset( $frame['type'] ) ?
1638  $frame['class'] . $frame['type'] . $frame['function'] :
1639  $frame['function'];
1640 }
1641 
1642 /* Some generic result counters, pulled out of SearchEngine */
1643 
1651 function wfShowingResults( $offset, $limit ) {
1652  return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1653 }
1654 
1664 function wfClientAcceptsGzip( $force = false ) {
1665  static $result = null;
1666  if ( $result === null || $force ) {
1667  $result = false;
1668  if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
1669  # @todo FIXME: We may want to blacklist some broken browsers
1670  $m = [];
1671  if ( preg_match(
1672  '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1673  $_SERVER['HTTP_ACCEPT_ENCODING'],
1674  $m
1675  )
1676  ) {
1677  if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
1678  $result = false;
1679  return $result;
1680  }
1681  wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
1682  $result = true;
1683  }
1684  }
1685  }
1686  return $result;
1687 }
1688 
1698 function wfEscapeWikiText( $text ) {
1699  global $wgEnableMagicLinks;
1700  static $repl = null, $repl2 = null;
1701  if ( $repl === null || defined( 'MW_PARSER_TEST' ) || defined( 'MW_PHPUNIT_TEST' ) ) {
1702  // Tests depend upon being able to change $wgEnableMagicLinks, so don't cache
1703  // in those situations
1704  $repl = [
1705  '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
1706  '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
1707  '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
1708  "\n#" => "\n&#35;", "\r#" => "\r&#35;",
1709  "\n*" => "\n&#42;", "\r*" => "\r&#42;",
1710  "\n:" => "\n&#58;", "\r:" => "\r&#58;",
1711  "\n " => "\n&#32;", "\r " => "\r&#32;",
1712  "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
1713  "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
1714  "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
1715  "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
1716  '__' => '_&#95;', '://' => '&#58;//',
1717  ];
1718 
1719  $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
1720  // We have to catch everything "\s" matches in PCRE
1721  foreach ( $magicLinks as $magic ) {
1722  $repl["$magic "] = "$magic&#32;";
1723  $repl["$magic\t"] = "$magic&#9;";
1724  $repl["$magic\r"] = "$magic&#13;";
1725  $repl["$magic\n"] = "$magic&#10;";
1726  $repl["$magic\f"] = "$magic&#12;";
1727  }
1728 
1729  // And handle protocols that don't use "://"
1730  global $wgUrlProtocols;
1731  $repl2 = [];
1732  foreach ( $wgUrlProtocols as $prot ) {
1733  if ( substr( $prot, -1 ) === ':' ) {
1734  $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
1735  }
1736  }
1737  $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/';
1738  }
1739  $text = substr( strtr( "\n$text", $repl ), 1 );
1740  $text = preg_replace( $repl2, '$1&#58;', $text );
1741  return $text;
1742 }
1743 
1754 function wfSetVar( &$dest, $source, $force = false ) {
1755  $temp = $dest;
1756  if ( !is_null( $source ) || $force ) {
1757  $dest = $source;
1758  }
1759  return $temp;
1760 }
1761 
1771 function wfSetBit( &$dest, $bit, $state = true ) {
1772  $temp = (bool)( $dest & $bit );
1773  if ( !is_null( $state ) ) {
1774  if ( $state ) {
1775  $dest |= $bit;
1776  } else {
1777  $dest &= ~$bit;
1778  }
1779  }
1780  return $temp;
1781 }
1782 
1789 function wfVarDump( $var ) {
1790  global $wgOut;
1791  $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
1792  if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
1793  print $s;
1794  } else {
1795  $wgOut->addHTML( $s );
1796  }
1797 }
1798 
1806 function wfHttpError( $code, $label, $desc ) {
1807  global $wgOut;
1809  if ( $wgOut ) {
1810  $wgOut->disable();
1811  $wgOut->sendCacheControl();
1812  }
1813 
1815  header( 'Content-type: text/html; charset=utf-8' );
1816  print '<!DOCTYPE html>' .
1817  '<html><head><title>' .
1818  htmlspecialchars( $label ) .
1819  '</title></head><body><h1>' .
1820  htmlspecialchars( $label ) .
1821  '</h1><p>' .
1822  nl2br( htmlspecialchars( $desc ) ) .
1823  "</p></body></html>\n";
1824 }
1825 
1843 function wfResetOutputBuffers( $resetGzipEncoding = true ) {
1844  if ( $resetGzipEncoding ) {
1845  // Suppress Content-Encoding and Content-Length
1846  // headers from 1.10+s wfOutputHandler
1848  $wgDisableOutputCompression = true;
1849  }
1850  while ( $status = ob_get_status() ) {
1851  if ( isset( $status['flags'] ) ) {
1852  $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1853  $deleteable = ( $status['flags'] & $flags ) === $flags;
1854  } elseif ( isset( $status['del'] ) ) {
1855  $deleteable = $status['del'];
1856  } else {
1857  // Guess that any PHP-internal setting can't be removed.
1858  $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
1859  }
1860  if ( !$deleteable ) {
1861  // Give up, and hope the result doesn't break
1862  // output behavior.
1863  break;
1864  }
1865  if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1866  // Unit testing barrier to prevent this function from breaking PHPUnit.
1867  break;
1868  }
1869  if ( !ob_end_clean() ) {
1870  // Could not remove output buffer handler; abort now
1871  // to avoid getting in some kind of infinite loop.
1872  break;
1873  }
1874  if ( $resetGzipEncoding ) {
1875  if ( $status['name'] == 'ob_gzhandler' ) {
1876  // Reset the 'Content-Encoding' field set by this handler
1877  // so we can start fresh.
1878  header_remove( 'Content-Encoding' );
1879  break;
1880  }
1881  }
1882  }
1883 }
1884 
1897 function wfClearOutputBuffers() {
1898  wfResetOutputBuffers( false );
1899 }
1900 
1909 function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1910  # No arg means accept anything (per HTTP spec)
1911  if ( !$accept ) {
1912  return [ $def => 1.0 ];
1913  }
1914 
1915  $prefs = [];
1916 
1917  $parts = explode( ',', $accept );
1918 
1919  foreach ( $parts as $part ) {
1920  # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1921  $values = explode( ';', trim( $part ) );
1922  $match = [];
1923  if ( count( $values ) == 1 ) {
1924  $prefs[$values[0]] = 1.0;
1925  } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1926  $prefs[$values[0]] = floatval( $match[1] );
1927  }
1928  }
1929 
1930  return $prefs;
1931 }
1932 
1945 function mimeTypeMatch( $type, $avail ) {
1946  if ( array_key_exists( $type, $avail ) ) {
1947  return $type;
1948  } else {
1949  $mainType = explode( '/', $type )[0];
1950  if ( array_key_exists( "$mainType/*", $avail ) ) {
1951  return "$mainType/*";
1952  } elseif ( array_key_exists( '*/*', $avail ) ) {
1953  return '*/*';
1954  } else {
1955  return null;
1956  }
1957  }
1958 }
1959 
1973 function wfNegotiateType( $cprefs, $sprefs ) {
1974  $combine = [];
1975 
1976  foreach ( array_keys( $sprefs ) as $type ) {
1977  $subType = explode( '/', $type )[1];
1978  if ( $subType != '*' ) {
1979  $ckey = mimeTypeMatch( $type, $cprefs );
1980  if ( $ckey ) {
1981  $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1982  }
1983  }
1984  }
1985 
1986  foreach ( array_keys( $cprefs ) as $type ) {
1987  $subType = explode( '/', $type )[1];
1988  if ( $subType != '*' && !array_key_exists( $type, $sprefs ) ) {
1989  $skey = mimeTypeMatch( $type, $sprefs );
1990  if ( $skey ) {
1991  $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1992  }
1993  }
1994  }
1995 
1996  $bestq = 0;
1997  $besttype = null;
1998 
1999  foreach ( array_keys( $combine ) as $type ) {
2000  if ( $combine[$type] > $bestq ) {
2001  $besttype = $type;
2002  $bestq = $combine[$type];
2003  }
2004  }
2005 
2006  return $besttype;
2007 }
2008 
2015 function wfSuppressWarnings( $end = false ) {
2016  MediaWiki\suppressWarnings( $end );
2017 }
2018 
2023 function wfRestoreWarnings() {
2024  MediaWiki\suppressWarnings( true );
2025 }
2026 
2035 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
2036  $ret = MWTimestamp::convert( $outputtype, $ts );
2037  if ( $ret === false ) {
2038  wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
2039  }
2040  return $ret;
2041 }
2042 
2051 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
2052  if ( is_null( $ts ) ) {
2053  return null;
2054  } else {
2055  return wfTimestamp( $outputtype, $ts );
2056  }
2057 }
2058 
2064 function wfTimestampNow() {
2065  # return NOW
2066  return MWTimestamp::now( TS_MW );
2067 }
2068 
2074 function wfIsWindows() {
2075  static $isWindows = null;
2076  if ( $isWindows === null ) {
2077  $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
2078  }
2079  return $isWindows;
2080 }
2081 
2087 function wfIsHHVM() {
2088  return defined( 'HHVM_VERSION' );
2089 }
2090 
2097 function wfIsCLI() {
2098  return PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg';
2099 }
2100 
2112 function wfTempDir() {
2114 
2115  if ( $wgTmpDirectory !== false ) {
2116  return $wgTmpDirectory;
2117  }
2118 
2120 }
2121 
2131 function wfMkdirParents( $dir, $mode = null, $caller = null ) {
2133 
2134  if ( FileBackend::isStoragePath( $dir ) ) { // sanity
2135  throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
2136  }
2137 
2138  if ( !is_null( $caller ) ) {
2139  wfDebug( "$caller: called wfMkdirParents($dir)\n" );
2140  }
2141 
2142  if ( strval( $dir ) === '' || is_dir( $dir ) ) {
2143  return true;
2144  }
2145 
2146  $dir = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $dir );
2147 
2148  if ( is_null( $mode ) ) {
2149  $mode = $wgDirectoryMode;
2150  }
2151 
2152  // Turn off the normal warning, we're doing our own below
2153  MediaWiki\suppressWarnings();
2154  $ok = mkdir( $dir, $mode, true ); // PHP5 <3
2155  MediaWiki\restoreWarnings();
2156 
2157  if ( !$ok ) {
2158  // directory may have been created on another request since we last checked
2159  if ( is_dir( $dir ) ) {
2160  return true;
2161  }
2162 
2163  // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
2164  wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
2165  }
2166  return $ok;
2167 }
2168 
2174 function wfRecursiveRemoveDir( $dir ) {
2175  wfDebug( __FUNCTION__ . "( $dir )\n" );
2176  // taken from https://secure.php.net/manual/en/function.rmdir.php#98622
2177  if ( is_dir( $dir ) ) {
2178  $objects = scandir( $dir );
2179  foreach ( $objects as $object ) {
2180  if ( $object != "." && $object != ".." ) {
2181  if ( filetype( $dir . '/' . $object ) == "dir" ) {
2182  wfRecursiveRemoveDir( $dir . '/' . $object );
2183  } else {
2184  unlink( $dir . '/' . $object );
2185  }
2186  }
2187  }
2188  reset( $objects );
2189  rmdir( $dir );
2190  }
2191 }
2192 
2199 function wfPercent( $nr, $acc = 2, $round = true ) {
2200  $ret = sprintf( "%.${acc}f", $nr );
2201  return $round ? round( $ret, $acc ) . '%' : "$ret%";
2202 }
2203 
2227 function wfIniGetBool( $setting ) {
2228  return wfStringToBool( ini_get( $setting ) );
2229 }
2230 
2243 function wfStringToBool( $val ) {
2244  $val = strtolower( $val );
2245  // 'on' and 'true' can't have whitespace around them, but '1' can.
2246  return $val == 'on'
2247  || $val == 'true'
2248  || $val == 'yes'
2249  || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
2250 }
2251 
2264 function wfEscapeShellArg( /*...*/ ) {
2265  $args = func_get_args();
2266 
2267  return call_user_func_array( Shell::class . '::escape', $args );
2268 }
2269 
2277 function wfShellExecDisabled() {
2278  wfDeprecated( __FUNCTION__, '1.30' );
2279  return Shell::isDisabled() ? 'disabled' : false;
2280 }
2281 
2305 function wfShellExec( $cmd, &$retval = null, $environ = [],
2306  $limits = [], $options = []
2307 ) {
2308  if ( Shell::isDisabled() ) {
2309  $retval = 1;
2310  // Backwards compatibility be upon us...
2311  return 'Unable to run external programs, proc_open() is disabled.';
2312  }
2313 
2314  if ( is_array( $cmd ) ) {
2315  $cmd = Shell::escape( $cmd );
2316  }
2317 
2318  $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
2319  $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
2320 
2321  try {
2322  $result = Shell::command( [] )
2323  ->unsafeParams( (array)$cmd )
2324  ->environment( $environ )
2325  ->limits( $limits )
2326  ->includeStderr( $includeStderr )
2327  ->profileMethod( $profileMethod )
2328  ->execute();
2329  } catch ( ProcOpenError $ex ) {
2330  $retval = -1;
2331  return '';
2332  }
2333 
2334  $retval = $result->getExitCode();
2335 
2336  return $result->getStdout();
2337 }
2338 
2356 function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
2357  return wfShellExec( $cmd, $retval, $environ, $limits,
2358  [ 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ] );
2359 }
2360 
2369 function wfInitShellLocale() {
2370  wfDeprecated( __FUNCTION__, '1.30' );
2371 }
2372 
2385 function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
2386  global $wgPhpCli;
2387  // Give site config file a chance to run the script in a wrapper.
2388  // The caller may likely want to call wfBasename() on $script.
2389  Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2390  $cmd = isset( $options['php'] ) ? [ $options['php'] ] : [ $wgPhpCli ];
2391  if ( isset( $options['wrapper'] ) ) {
2392  $cmd[] = $options['wrapper'];
2393  }
2394  $cmd[] = $script;
2395  // Escape each parameter for shell
2396  return Shell::escape( array_merge( $cmd, $parameters ) );
2397 }
2398 
2410 function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult = null ) {
2411  global $wgDiff3;
2412 
2413  # This check may also protect against code injection in
2414  # case of broken installations.
2415  MediaWiki\suppressWarnings();
2416  $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2417  MediaWiki\restoreWarnings();
2418 
2419  if ( !$haveDiff3 ) {
2420  wfDebug( "diff3 not found\n" );
2421  return false;
2422  }
2423 
2424  # Make temporary files
2425  $td = wfTempDir();
2426  $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2427  $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
2428  $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
2429 
2430  # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2431  # a newline character. To avoid this, we normalize the trailing whitespace before
2432  # creating the diff.
2433 
2434  fwrite( $oldtextFile, rtrim( $old ) . "\n" );
2435  fclose( $oldtextFile );
2436  fwrite( $mytextFile, rtrim( $mine ) . "\n" );
2437  fclose( $mytextFile );
2438  fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
2439  fclose( $yourtextFile );
2440 
2441  # Check for a conflict
2442  $cmd = Shell::escape( $wgDiff3, '-a', '--overlap-only', $mytextName,
2443  $oldtextName, $yourtextName );
2444  $handle = popen( $cmd, 'r' );
2445 
2446  $mergeAttemptResult = '';
2447  do {
2448  $data = fread( $handle, 8192 );
2449  if ( strlen( $data ) == 0 ) {
2450  break;
2451  }
2452  $mergeAttemptResult .= $data;
2453  } while ( true );
2454  pclose( $handle );
2455 
2456  $conflict = $mergeAttemptResult !== '';
2457 
2458  # Merge differences
2459  $cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName,
2460  $oldtextName, $yourtextName );
2461  $handle = popen( $cmd, 'r' );
2462  $result = '';
2463  do {
2464  $data = fread( $handle, 8192 );
2465  if ( strlen( $data ) == 0 ) {
2466  break;
2467  }
2468  $result .= $data;
2469  } while ( true );
2470  pclose( $handle );
2471  unlink( $mytextName );
2472  unlink( $oldtextName );
2473  unlink( $yourtextName );
2474 
2475  if ( $result === '' && $old !== '' && !$conflict ) {
2476  wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
2477  $conflict = true;
2478  }
2479  return !$conflict;
2480 }
2481 
2493 function wfDiff( $before, $after, $params = '-u' ) {
2494  if ( $before == $after ) {
2495  return '';
2496  }
2497 
2498  global $wgDiff;
2499  MediaWiki\suppressWarnings();
2500  $haveDiff = $wgDiff && file_exists( $wgDiff );
2501  MediaWiki\restoreWarnings();
2502 
2503  # This check may also protect against code injection in
2504  # case of broken installations.
2505  if ( !$haveDiff ) {
2506  wfDebug( "diff executable not found\n" );
2507  $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
2508  $format = new UnifiedDiffFormatter();
2509  return $format->format( $diffs );
2510  }
2511 
2512  # Make temporary files
2513  $td = wfTempDir();
2514  $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2515  $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
2516 
2517  fwrite( $oldtextFile, $before );
2518  fclose( $oldtextFile );
2519  fwrite( $newtextFile, $after );
2520  fclose( $newtextFile );
2521 
2522  // Get the diff of the two files
2523  $cmd = "$wgDiff " . $params . ' ' . Shell::escape( $oldtextName, $newtextName );
2524 
2525  $h = popen( $cmd, 'r' );
2526  if ( !$h ) {
2527  unlink( $oldtextName );
2528  unlink( $newtextName );
2529  throw new Exception( __METHOD__ . '(): popen() failed' );
2530  }
2531 
2532  $diff = '';
2533 
2534  do {
2535  $data = fread( $h, 8192 );
2536  if ( strlen( $data ) == 0 ) {
2537  break;
2538  }
2539  $diff .= $data;
2540  } while ( true );
2541 
2542  // Clean up
2543  pclose( $h );
2544  unlink( $oldtextName );
2545  unlink( $newtextName );
2546 
2547  // Kill the --- and +++ lines. They're not useful.
2548  $diff_lines = explode( "\n", $diff );
2549  if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
2550  unset( $diff_lines[0] );
2551  }
2552  if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
2553  unset( $diff_lines[1] );
2554  }
2555 
2556  $diff = implode( "\n", $diff_lines );
2557 
2558  return $diff;
2559 }
2560 
2579 function wfUsePHP( $req_ver ) {
2580  $php_ver = PHP_VERSION;
2581 
2582  if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
2583  throw new MWException( "PHP $req_ver required--this is only $php_ver" );
2584  }
2585 }
2586 
2609 function wfUseMW( $req_ver ) {
2611 
2612  if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
2613  throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
2614  }
2615 }
2616 
2629 function wfBaseName( $path, $suffix = '' ) {
2630  if ( $suffix == '' ) {
2631  $encSuffix = '';
2632  } else {
2633  $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
2634  }
2635 
2636  $matches = [];
2637  if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
2638  return $matches[1];
2639  } else {
2640  return '';
2641  }
2642 }
2643 
2653 function wfRelativePath( $path, $from ) {
2654  // Normalize mixed input on Windows...
2655  $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
2656  $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
2657 
2658  // Trim trailing slashes -- fix for drive root
2659  $path = rtrim( $path, DIRECTORY_SEPARATOR );
2660  $from = rtrim( $from, DIRECTORY_SEPARATOR );
2661 
2662  $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
2663  $against = explode( DIRECTORY_SEPARATOR, $from );
2664 
2665  if ( $pieces[0] !== $against[0] ) {
2666  // Non-matching Windows drive letters?
2667  // Return a full path.
2668  return $path;
2669  }
2670 
2671  // Trim off common prefix
2672  while ( count( $pieces ) && count( $against )
2673  && $pieces[0] == $against[0] ) {
2674  array_shift( $pieces );
2675  array_shift( $against );
2676  }
2677 
2678  // relative dots to bump us to the parent
2679  while ( count( $against ) ) {
2680  array_unshift( $pieces, '..' );
2681  array_shift( $against );
2682  }
2683 
2684  array_push( $pieces, wfBaseName( $path ) );
2685 
2686  return implode( DIRECTORY_SEPARATOR, $pieces );
2687 }
2688 
2706 function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
2707  $lowercase = true, $engine = 'auto'
2708 ) {
2709  wfDeprecated( __FUNCTION__, '1.27' );
2710  return Wikimedia\base_convert( $input, $sourceBase, $destBase, $pad, $lowercase, $engine );
2711 }
2712 
2719 function wfResetSessionID() {
2720  wfDeprecated( __FUNCTION__, '1.27' );
2721  $session = SessionManager::getGlobalSession();
2722  $delay = $session->delaySave();
2723 
2724  $session->resetId();
2725 
2726  // Make sure a session is started, since that's what the old
2727  // wfResetSessionID() did.
2728  if ( session_id() !== $session->getId() ) {
2729  wfSetupSession( $session->getId() );
2730  }
2731 
2732  ScopedCallback::consume( $delay );
2733 }
2734 
2744 function wfSetupSession( $sessionId = false ) {
2745  wfDeprecated( __FUNCTION__, '1.27' );
2746 
2747  if ( $sessionId ) {
2748  session_id( $sessionId );
2749  }
2750 
2751  $session = SessionManager::getGlobalSession();
2752  $session->persist();
2753 
2754  if ( session_id() !== $session->getId() ) {
2755  session_id( $session->getId() );
2756  }
2757  MediaWiki\quietCall( 'session_start' );
2758 }
2759 
2766 function wfGetPrecompiledData( $name ) {
2767  global $IP;
2768 
2769  $file = "$IP/serialized/$name";
2770  if ( file_exists( $file ) ) {
2771  $blob = file_get_contents( $file );
2772  if ( $blob ) {
2773  return unserialize( $blob );
2774  }
2775  }
2776  return false;
2777 }
2778 
2786 function wfMemcKey( /*...*/ ) {
2787  return call_user_func_array(
2788  [ ObjectCache::getLocalClusterInstance(), 'makeKey' ],
2789  func_get_args()
2790  );
2791 }
2792 
2803 function wfForeignMemcKey( $db, $prefix /*...*/ ) {
2804  $args = array_slice( func_get_args(), 2 );
2805  $keyspace = $prefix ? "$db-$prefix" : $db;
2806  return call_user_func_array(
2807  [ ObjectCache::getLocalClusterInstance(), 'makeKeyInternal' ],
2808  [ $keyspace, $args ]
2809  );
2810 }
2811 
2824 function wfGlobalCacheKey( /*...*/ ) {
2825  return call_user_func_array(
2826  [ ObjectCache::getLocalClusterInstance(), 'makeGlobalKey' ],
2827  func_get_args()
2828  );
2829 }
2830 
2837 function wfWikiID() {
2839  if ( $wgDBprefix ) {
2840  return "$wgDBname-$wgDBprefix";
2841  } else {
2842  return $wgDBname;
2843  }
2844 }
2845 
2853 function wfSplitWikiID( $wiki ) {
2854  $bits = explode( '-', $wiki, 2 );
2855  if ( count( $bits ) < 2 ) {
2856  $bits[] = '';
2857  }
2858  return $bits;
2859 }
2860 
2886 function wfGetDB( $db, $groups = [], $wiki = false ) {
2887  return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
2888 }
2889 
2899 function wfGetLB( $wiki = false ) {
2900  if ( $wiki === false ) {
2901  return MediaWikiServices::getInstance()->getDBLoadBalancer();
2902  } else {
2903  $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2904  return $factory->getMainLB( $wiki );
2905  }
2906 }
2907 
2915 function wfGetLBFactory() {
2916  return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2917 }
2918 
2927 function wfFindFile( $title, $options = [] ) {
2928  return RepoGroup::singleton()->findFile( $title, $options );
2929 }
2930 
2938 function wfLocalFile( $title ) {
2939  return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
2940 }
2941 
2948 function wfQueriesMustScale() {
2950  return $wgMiserMode
2951  || ( SiteStats::pages() > 100000
2952  && SiteStats::edits() > 1000000
2953  && SiteStats::users() > 10000 );
2954 }
2955 
2964 function wfScript( $script = 'index' ) {
2966  if ( $script === 'index' ) {
2967  return $wgScript;
2968  } elseif ( $script === 'load' ) {
2969  return $wgLoadScript;
2970  } else {
2971  return "{$wgScriptPath}/{$script}.php";
2972  }
2973 }
2974 
2980 function wfGetScriptUrl() {
2981  if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
2982  /* as it was called, minus the query string.
2983  *
2984  * Some sites use Apache rewrite rules to handle subdomains,
2985  * and have PHP set up in a weird way that causes PHP_SELF
2986  * to contain the rewritten URL instead of the one that the
2987  * outside world sees.
2988  *
2989  * If in this mode, use SCRIPT_URL instead, which mod_rewrite
2990  * provides containing the "before" URL.
2991  */
2992  return $_SERVER['SCRIPT_NAME'];
2993  } else {
2994  return $_SERVER['URL'];
2995  }
2996 }
2997 
3005 function wfBoolToStr( $value ) {
3006  return $value ? 'true' : 'false';
3007 }
3008 
3014 function wfGetNull() {
3015  return wfIsWindows() ? 'NUL' : '/dev/null';
3016 }
3017 
3040 function wfWaitForSlaves(
3041  $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
3042 ) {
3043  if ( $timeout === null ) {
3044  $timeout = wfIsCLI() ? 86400 : 10;
3045  }
3046 
3047  if ( $cluster === '*' ) {
3048  $cluster = false;
3049  $wiki = false;
3050  } elseif ( $wiki === false ) {
3051  $wiki = wfWikiID();
3052  }
3053 
3054  try {
3055  wfGetLBFactory()->waitForReplication( [
3056  'wiki' => $wiki,
3057  'cluster' => $cluster,
3058  'timeout' => $timeout,
3059  // B/C: first argument used to be "max seconds of lag"; ignore such values
3060  'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
3061  ] );
3062  } catch ( DBReplicationWaitError $e ) {
3063  return false;
3064  }
3065 
3066  return true;
3067 }
3068 
3078 function wfCountDown( $seconds ) {
3079  for ( $i = $seconds; $i >= 0; $i-- ) {
3080  if ( $i != $seconds ) {
3081  echo str_repeat( "\x08", strlen( $i + 1 ) );
3082  }
3083  echo $i;
3084  flush();
3085  if ( $i ) {
3086  sleep( 1 );
3087  }
3088  }
3089  echo "\n";
3090 }
3091 
3100 function wfStripIllegalFilenameChars( $name ) {
3102  $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
3103  $name = preg_replace(
3104  "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
3105  '-',
3106  $name
3107  );
3108  // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
3109  $name = wfBaseName( $name );
3110  return $name;
3111 }
3112 
3118 function wfMemoryLimit() {
3120  $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
3121  if ( $memlimit != -1 ) {
3122  $conflimit = wfShorthandToInteger( $wgMemoryLimit );
3123  if ( $conflimit == -1 ) {
3124  wfDebug( "Removing PHP's memory limit\n" );
3125  MediaWiki\suppressWarnings();
3126  ini_set( 'memory_limit', $conflimit );
3127  MediaWiki\restoreWarnings();
3128  return $conflimit;
3129  } elseif ( $conflimit > $memlimit ) {
3130  wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
3131  MediaWiki\suppressWarnings();
3132  ini_set( 'memory_limit', $conflimit );
3133  MediaWiki\restoreWarnings();
3134  return $conflimit;
3135  }
3136  }
3137  return $memlimit;
3138 }
3139 
3146 function wfTransactionalTimeLimit() {
3148 
3149  $timeLimit = ini_get( 'max_execution_time' );
3150  // Note that CLI scripts use 0
3151  if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
3152  set_time_limit( $wgTransactionalTimeLimit );
3153  }
3154 
3155  ignore_user_abort( true ); // ignore client disconnects
3156 
3157  return $timeLimit;
3158 }
3159 
3167 function wfShorthandToInteger( $string = '', $default = -1 ) {
3168  $string = trim( $string );
3169  if ( $string === '' ) {
3170  return $default;
3171  }
3172  $last = $string[strlen( $string ) - 1];
3173  $val = intval( $string );
3174  switch ( $last ) {
3175  case 'g':
3176  case 'G':
3177  $val *= 1024;
3178  // break intentionally missing
3179  case 'm':
3180  case 'M':
3181  $val *= 1024;
3182  // break intentionally missing
3183  case 'k':
3184  case 'K':
3185  $val *= 1024;
3186  }
3187 
3188  return $val;
3189 }
3190 
3201 function wfBCP47( $code ) {
3202  return LanguageCode::bcp47( $code );
3203 }
3204 
3211 function wfGetCache( $cacheType ) {
3212  return ObjectCache::getInstance( $cacheType );
3213 }
3214 
3220 function wfGetMainCache() {
3222  return ObjectCache::getInstance( $wgMainCacheType );
3223 }
3224 
3230 function wfGetMessageCacheStorage() {
3232  return ObjectCache::getInstance( $wgMessageCacheType );
3233 }
3234 
3241 function wfGetParserCacheStorage() {
3243  return ObjectCache::getInstance( $wgParserCacheType );
3244 }
3245 
3256 function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
3257  wfDeprecated( __METHOD__, '1.25' );
3258  return Hooks::run( $event, $args, $deprecatedVersion );
3259 }
3260 
3275 function wfUnpack( $format, $data, $length = false ) {
3276  if ( $length !== false ) {
3277  $realLen = strlen( $data );
3278  if ( $realLen < $length ) {
3279  throw new MWException( "Tried to use wfUnpack on a "
3280  . "string of length $realLen, but needed one "
3281  . "of at least length $length."
3282  );
3283  }
3284  }
3285 
3286  MediaWiki\suppressWarnings();
3287  $result = unpack( $format, $data );
3288  MediaWiki\restoreWarnings();
3289 
3290  if ( $result === false ) {
3291  // If it cannot extract the packed data.
3292  throw new MWException( "unpack could not unpack binary data" );
3293  }
3294  return $result;
3295 }
3296 
3311 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
3312  # Handle redirects; callers almost always hit wfFindFile() anyway,
3313  # so just use that method because it has a fast process cache.
3314  $file = wfFindFile( $name ); // get the final name
3315  $name = $file ? $file->getTitle()->getDBkey() : $name;
3316 
3317  # Run the extension hook
3318  $bad = false;
3319  if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
3320  return (bool)$bad;
3321  }
3322 
3324  $key = $cache->makeKey(
3325  'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist )
3326  );
3327  $badImages = $cache->get( $key );
3328 
3329  if ( $badImages === false ) { // cache miss
3330  if ( $blacklist === null ) {
3331  $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
3332  }
3333  # Build the list now
3334  $badImages = [];
3335  $lines = explode( "\n", $blacklist );
3336  foreach ( $lines as $line ) {
3337  # List items only
3338  if ( substr( $line, 0, 1 ) !== '*' ) {
3339  continue;
3340  }
3341 
3342  # Find all links
3343  $m = [];
3344  if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
3345  continue;
3346  }
3347 
3348  $exceptions = [];
3349  $imageDBkey = false;
3350  foreach ( $m[1] as $i => $titleText ) {
3351  $title = Title::newFromText( $titleText );
3352  if ( !is_null( $title ) ) {
3353  if ( $i == 0 ) {
3354  $imageDBkey = $title->getDBkey();
3355  } else {
3356  $exceptions[$title->getPrefixedDBkey()] = true;
3357  }
3358  }
3359  }
3360 
3361  if ( $imageDBkey !== false ) {
3362  $badImages[$imageDBkey] = $exceptions;
3363  }
3364  }
3365  $cache->set( $key, $badImages, 60 );
3366  }
3367 
3368  $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
3369  $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
3370 
3371  return $bad;
3372 }
3373 
3381 function wfCanIPUseHTTPS( $ip ) {
3382  $canDo = true;
3383  Hooks::run( 'CanIPUseHTTPS', [ $ip, &$canDo ] );
3384  return !!$canDo;
3385 }
3386 
3394 function wfIsInfinity( $str ) {
3395  // These are hardcoded elsewhere in MediaWiki (e.g. mediawiki.special.block.js).
3396  $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
3397  return in_array( $str, $infinityValues );
3398 }
3399 
3414 function wfThumbIsStandard( File $file, array $params ) {
3416 
3417  $multipliers = [ 1 ];
3418  if ( $wgResponsiveImages ) {
3419  // These available sizes are hardcoded currently elsewhere in MediaWiki.
3420  // @see Linker::processResponsiveImages
3421  $multipliers[] = 1.5;
3422  $multipliers[] = 2;
3423  }
3424 
3425  $handler = $file->getHandler();
3426  if ( !$handler || !isset( $params['width'] ) ) {
3427  return false;
3428  }
3429 
3430  $basicParams = [];
3431  if ( isset( $params['page'] ) ) {
3432  $basicParams['page'] = $params['page'];
3433  }
3434 
3435  $thumbLimits = [];
3436  $imageLimits = [];
3437  // Expand limits to account for multipliers
3438  foreach ( $multipliers as $multiplier ) {
3439  $thumbLimits = array_merge( $thumbLimits, array_map(
3440  function ( $width ) use ( $multiplier ) {
3441  return round( $width * $multiplier );
3442  }, $wgThumbLimits )
3443  );
3444  $imageLimits = array_merge( $imageLimits, array_map(
3445  function ( $pair ) use ( $multiplier ) {
3446  return [
3447  round( $pair[0] * $multiplier ),
3448  round( $pair[1] * $multiplier ),
3449  ];
3450  }, $wgImageLimits )
3451  );
3452  }
3453 
3454  // Check if the width matches one of $wgThumbLimits
3455  if ( in_array( $params['width'], $thumbLimits ) ) {
3456  $normalParams = $basicParams + [ 'width' => $params['width'] ];
3457  // Append any default values to the map (e.g. "lossy", "lossless", ...)
3458  $handler->normaliseParams( $file, $normalParams );
3459  } else {
3460  // If not, then check if the width matchs one of $wgImageLimits
3461  $match = false;
3462  foreach ( $imageLimits as $pair ) {
3463  $normalParams = $basicParams + [ 'width' => $pair[0], 'height' => $pair[1] ];
3464  // Decide whether the thumbnail should be scaled on width or height.
3465  // Also append any default values to the map (e.g. "lossy", "lossless", ...)
3466  $handler->normaliseParams( $file, $normalParams );
3467  // Check if this standard thumbnail size maps to the given width
3468  if ( $normalParams['width'] == $params['width'] ) {
3469  $match = true;
3470  break;
3471  }
3472  }
3473  if ( !$match ) {
3474  return false; // not standard for description pages
3475  }
3476  }
3477 
3478  // Check that the given values for non-page, non-width, params are just defaults
3479  foreach ( $params as $key => $value ) {
3480  if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3481  return false;
3482  }
3483  }
3484 
3485  return true;
3486 }
3487 
3500 function wfArrayPlus2d( array $baseArray, array $newValues ) {
3501  // First merge items that are in both arrays
3502  foreach ( $baseArray as $name => &$groupVal ) {
3503  if ( isset( $newValues[$name] ) ) {
3504  $groupVal += $newValues[$name];
3505  }
3506  }
3507  // Now add items that didn't exist yet
3508  $baseArray += $newValues;
3509 
3510  return $baseArray;
3511 }
3512 
3521 function wfGetRusage() {
3522  if ( !function_exists( 'getrusage' ) ) {
3523  return false;
3524  } elseif ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
3525  return getrusage( 2 /* RUSAGE_THREAD */ );
3526  } else {
3527  return getrusage( 0 /* RUSAGE_SELF */ );
3528  }
3529 }
3530 
3536 function wfProfileIn( $functionname ) {
3537 }
3538 
3544 function wfProfileOut( $functionname = 'missing' ) {
3545 }
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
wfGetDB($db, $groups=[], $wiki=false)
Get a Database object.
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:784
wfPercent($nr, $acc=2, $round=true)
the array() calling protocol came about after MediaWiki 1.4rc1.
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:1584
wfWaitForSlaves($ifWritesSince=null, $wiki=false, $cluster=false, $timeout=null)
Waits for the replica DBs to catch up to the master position.
wfCanIPUseHTTPS($ip)
Determine whether the client at a given source IP is likely to be able to access the wiki via HTTPS...
$wgScript
The URL path to index.php.
magic word the default is to use $key to get the and $key value or $key value text $key value html to format the value $key
Definition: hooks.txt:2563
$wgVersion
MediaWiki version number.
wfScript($script= 'index')
Get the path to a specified script file, respecting file extensions; this is a wrapper around $wgScri...
static warning($msg, $callerOffset=1, $level=E_USER_NOTICE, $log= 'auto')
Adds a warning entry to the log.
Definition: MWDebug.php:151
wfIsHHVM()
Check if we are running under HHVM.
if(is_array($mode)) switch($mode) $input
wfForeignMemcKey($db, $prefix)
Make a cache key for a foreign DB.
wfMerge($old, $mine, $yours, &$result, &$mergeAttemptResult=null)
wfMerge attempts to merge differences between three texts.
$IP
Definition: WebStart.php:54
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:1973
wfShorthandToInteger($string= '', $default=-1)
Converts shorthand byte notation to integer form.
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
wfMkdirParents($dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
if(!$wgDBerrorLogTZ) $wgRequest
Definition: Setup.php:737
wfDebugMem($exact=false)
Send a line giving PHP memory usage.
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException'returning false will NOT prevent logging $e
Definition: hooks.txt:2149
wfGetRusage()
Get system resource usage of current request context.
static instance()
Singleton.
Definition: Profiler.php:62
static header($code)
Output an HTTP status code header.
Definition: HttpStatus.php:96
$wgInternalServer
Internal server name as known to CDN, if different.
wfHostname()
Fetch server name for use in error reporting etc.
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:187
mimeTypeMatch($type, $avail)
Checks if a given MIME type matches any of the keys in the given array.
static configuration should be added through ResourceLoaderGetConfigVars instead can be used to get the real title after the basic globals have been set but before ordinary actions take place $output
Definition: hooks.txt:2206
static getInstance($id)
Get a cached instance of the specified type of cache object.
Definition: ObjectCache.php:92
wfRelativePath($path, $from)
Generate a relative path name to the given file.
wfFormatStackFrame($frame)
Return a string representation of frame.
wfDebugBacktrace($limit=0)
Safety wrapper for debug_backtrace().
wfRunHooks($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
wfBacktrace($raw=null)
Get a debug backtrace as a string.
wfLogProfilingData()
A formatter that outputs unified diffs.
$source
wfLogDBError($text, array $context=[])
Log for database errors.
wfHttpError($code, $label, $desc)
Provide a simple HTTP error.
$value
const PROTO_CURRENT
Definition: Defines.php:223
wfAppendToArrayIfNotDefault($key, $value, $default, &$changed)
Appends to second array if $value differs from that in $default.
wfMessageFallback()
This function accepts multiple message keys and returns a message instance for the first message whic...
wfMakeUrlIndexes($url)
Make URL indexes, appropriate for the el_index field of externallinks.
static getLocalClusterInstance()
Get the main cluster-local cache object.
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 MediaWikiServices
Definition: injection.txt:23
wfUrlProtocolsWithoutProtRel()
Like wfUrlProtocols(), but excludes '//' from the protocol list.
$wgDisableOutputCompression
Disable output compression (enabled by default if zlib is available)
wfIsBadImage($name, $contextTitle=false, $blacklist=null)
Determine if an image exists on the 'bad image list'.
wfObjectToArray($objOrArray, $recursive=true)
Recursively converts the parameter (an object) to an array with the same data.
wfLoadExtension($ext, $path=null)
Load an extension.
wfShellExec($cmd, &$retval=null, $environ=[], $limits=[], $options=[])
Execute a shell command, with time and memory limits mirrored from the PHP configuration if supported...
wfUrlencode($s)
We want some things to be included as literal characters in our title URLs for prettiness, which urlencode encodes by default.
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:273
$wgTmpDirectory
The local filesystem path to a temporary directory.
when a variable name is used in a it is silently declared as a new local masking the global
Definition: design.txt:93
wfExpandUrl($url, $defaultProto=PROTO_CURRENT)
Expand a potentially local URL to a fully-qualified URL.
wfBoolToStr($value)
Convenience function converts boolean values into "true" or "false" (string) values.
wfIsWindows()
Check if the operating system is Windows.
wfReportTime()
Returns a script tag that stores the amount of time it took MediaWiki to handle the request in millis...
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...
wfStripIllegalFilenameChars($name)
Replace all invalid characters with '-'.
wfGetMessageCacheStorage()
Get the cache object used by the message cache.
getTitle()
Return the associated title object.
Definition: File.php:326
wfRandomString($length=32)
Get a random string containing a number of pseudo-random hex characters.
static makeVariablesScript($data)
Definition: Skin.php:382
wfNegotiateType($cprefs, $sprefs)
Returns the 'best' match between a client's requested internet media types and the server's list of a...
wfArrayDiff2($a, $b)
Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
wfDebug($text, $dest= 'all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfGetParserCacheStorage()
Get the cache object used by the parser cache.
static fetchLanguageNames($inLanguage=null, $include= 'mw')
Get an array of language names, indexed by code.
Definition: Language.php:803
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
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 'ImportHandleUnknownUser':When a user doesn't exist locally, this hook is called to give extensions an opportunity to auto-create it.If the auto-creation is successful, return false.$name:User name 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload.Return false to stop further processing of the tag $reader:XMLReader object $revisionInfo:Array of information 'ImportLogInterwikiLink':Hook to change the interwiki link used in log entries and edit summaries for transwiki imports.&$fullInterwikiPrefix:Interwiki prefix, may contain colons.&$pageTitle:String that contains page title. 'ImportSources':Called when reading from the $wgImportSources configuration variable.Can be used to lazy-load the import sources list.&$importSources:The value of $wgImportSources.Modify as necessary.See the comment in DefaultSettings.php for the detail of how to structure this array. 'InfoAction':When building information to display on the action=info page.$context:IContextSource object &$pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect.&$title:Title object for the current page &$request:WebRequest &$ignoreRedirect:boolean to skip redirect check &$target:Title/string of redirect target &$article:Article object 'InternalParseBeforeLinks':during Parser's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InternalParseBeforeSanitize':during Parser's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings.Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments.&$parser:Parser object &$text:string containing partially parsed text &$stripState:Parser's internal StripState object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not.Return true without providing an interwiki to continue interwiki search.$prefix:interwiki prefix we are looking for.&$iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InvalidateEmailComplete':Called after a user's email has been invalidated successfully.$user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification.Callee may modify $url and $query, URL will be constructed as $url.$query &$url:URL to index.php &$query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) &$article:article(object) being checked 'IsTrustedProxy':Override the result of IP::isTrustedProxy() &$ip:IP being check &$result:Change this value to override the result of IP::isTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from &$allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of Sanitizer::validateEmail(), for instance to return false if the domain name doesn't match your organization.$addr:The e-mail address entered by the user &$result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user &$result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we're looking for a messages file for &$file:The messages file path, you can override this to change the location. 'LanguageGetMagic':DEPRECATED!Use $magicWords in a file listed in $wgExtensionMessagesFiles instead.Use this to define synonyms of magic words depending of the language &$magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces.Do not use this hook to add namespaces.Use CanonicalNamespaces for that.&$namespaces:Array of namespaces indexed by their numbers 'LanguageGetSpecialPageAliases':DEPRECATED!Use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead.Use to define aliases of special pages names depending of the language &$specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names.&$names:array of language code=> language name $code:language of the preferred translations 'LanguageLinks':Manipulate a page's language links.This is called in various places to allow extensions to define the effective language links for a page.$title:The page's Title.&$links:Array with elements of the form"language:title"in the order that they will be output.&$linkFlags:Associative array mapping prefixed links to arrays of flags.Currently unused, but planned to provide support for marking individual language links in the UI, e.g.for featured articles. 'LanguageSelector':Hook to change the language selector available on a page.$out:The output page.$cssClassName:CSS class name of the language selector. 'LinkBegin':DEPRECATED!Use HtmlPageLinkRendererBegin instead.Used when generating internal and interwiki links in Linker::link(), before processing starts.Return false to skip default processing and return $ret.See documentation for Linker::link() for details on the expected meanings of parameters.$skin:the Skin object $target:the Title that the link is pointing to &$html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1971
wfGlobalCacheKey()
Make a cache key with database-agnostic prefix.
if($line===false) $args
Definition: cdb.php:64
static getUsableTempDirectory()
Definition: TempFSFile.php:85
$last
wfTimestamp($outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMsgReplaceArgs($message, $args)
Replace message parameter keys on the given formatted output.
$wgLanguageCode
Site language code.
wfCountDown($seconds)
Count down from $seconds to zero on the terminal, with a one-second pause between showing each number...
wfArrayFilter(array $arr, callable $callback)
Like array_filter with ARRAY_FILTER_USE_BOTH, but works pre-5.6.
static edits()
Definition: SiteStats.php:138
$wgExtensionDirectory
Filesystem extensions directory.
wfCgiToArray($query)
This is the logical opposite of wfArrayToCgi(): it accepts a query string as its argument and returns...
wfDebugLog($logGroup, $text, $dest= 'all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not...
wfLoadExtensions(array $exts)
Load multiple extensions at once.
wfIsDebugRawPage()
Returns true if debug logging should be suppressed if $wgDebugRawPage = false.
global $wgCommandLineMode
Definition: Setup.php:601
wfConfiguredReadOnlyReason()
Get the value of $wgReadOnly or the contents of $wgReadOnlyFile.
wfDiff($before, $after, $params= '-u')
Returns unified plain-text diff of two texts.
const PROTO_HTTPS
Definition: Defines.php:221
wfResetOutputBuffers($resetGzipEncoding=true)
Clear away any user-level output buffers, discarding contents.
wfGetLB($wiki=false)
Get a load balancer object.
wfEscapeWikiText($text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
wfReadOnly()
Check whether the wiki is in read-only mode.
$wgParserCacheType
The cache type for storing article HTML.
wfSetBit(&$dest, $bit, $state=true)
As for wfSetVar except setting a bit.
wfAssembleUrl($urlParts)
This function will reassemble a URL parsed with wfParseURL.
wfTempDir()
Tries to get the system directory for temporary files.
static getMain()
Get the RequestContext object associated with the main request.
design txt This is a brief overview of the new design More thorough and up to date information is available on the documentation wiki at etc Handles the details of getting and saving to the user table of the and dealing with sessions and cookies OutputPage Encapsulates the entire HTML page that will be sent in response to any server request It is used by calling its functions to add in any and then calling but I prefer the flexibility This should also do the output encoding The system allocates a global one in $wgOut Title Represents the title of an and does all the work of translating among various forms such as plain database key
Definition: design.txt:25
unserialize($serialized)
Definition: ApiMessage.php:192
wfMergeErrorArrays()
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
static deprecated($function, $version=false, $component=false, $callerOffset=2)
Show a warning that $function is deprecated.
Definition: MWDebug.php:193
wfGetCache($cacheType)
Get a specific cache object.
wfAppendQuery($url, $query)
Append a query string to an existing URL, which may or may not already have query string parameters a...
$wgIllegalFileChars
Additional characters that are not allowed in filenames.
wfShellWikiCmd($script, array $parameters=[], array $options=[])
Generate a shell-escaped command line string to run a MediaWiki cli script.
const PROTO_INTERNAL
Definition: Defines.php:225
wfWarn($msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
getHandler()
Get a MediaHandler instance for this file.
Definition: File.php:1383
wfIniGetBool($setting)
Safety wrapper around ini_get() for boolean settings.
wfClientAcceptsGzip($force=false)
Whether the client accept gzip encoding.
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:59
static isStoragePath($path)
Check if a given path is a "mwstore://" path.
$wgMessageCacheType
The cache type for storing the contents of the MediaWiki namespace.
$wgMiserMode
Disable database-intensive features.
static warnIfHeadersSent()
Log a warning message if headers have already been sent.
wfMatchesDomainList($url, $domains)
Check whether a given URL has a domain that occurs in a given set of domains.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfErrorLog($text, $file, array $context=[])
Log to a file without getting "file size exceeded" signals.
$wgImageLimits
Limit images on image description pages to a user-selectable limit.
Class representing a 'diff' between two sequences of strings.
wfLoadSkin($skin, $path=null)
Load a skin.
$cache
Definition: mcc.php:33
$params
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
Definition: hooks.txt:1973
$wgThumbLimits
Adjust thumbnails on image pages according to a user setting.
wfIsInfinity($str)
Determine input string is represents as infinity.
CACHE_MEMCACHED $wgMainCacheType
Definition: memcached.txt:63
wfDeprecated($function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
wfIncrStats($key, $count=1)
Increment a statistics counter.
wfExpandIRI_callback($matches)
Private callback for wfExpandIRI.
wfBCP47($code)
Get the normalised IETF language tag See unit test for examples.
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:935
wfIsCLI()
Check if we are running from the commandline.
wfLoadSkins(array $skins)
Load multiple skins at once.
wfProfileOut($functionname= 'missing')
Stop profiling of a function.
static run($event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:203
wfQueriesMustScale()
Should low-performance queries be disabled?
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
wfShowingResults($offset, $limit)
wfArrayDiff2_cmp($a, $b)
wfInitShellLocale()
Formerly set the locale for locale-sensitive operations.
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
Definition: distributors.txt:9
wfVarDump($var)
A wrapper around the PHP function var_export().
wfSuppressWarnings($end=false)
Reference-counted warning suppression.
const PROTO_HTTP
Definition: Defines.php:220
the value to return A Title object or null for latest all implement SearchIndexField $engine
Definition: hooks.txt:2849
wfUsePHP($req_ver)
This function works like "use VERSION" in Perl, the program will die with a backtrace if the current ...
wfShellExecDisabled()
Check if wfShellExec() is effectively disabled via php.ini config.
wfGetAllCallers($limit=3)
Return a string consisting of callers in the stack.
wfArrayInsertAfter(array $array, array $insert, $after)
Insert array into another array after the specified KEY
wfUrlProtocols($includeProtocolRelative=true)
Returns a regular expression of url protocols.
$wgMemoryLimit
The minimum amount of memory that MediaWiki "needs"; MediaWiki will try to raise PHP's memory limit i...
wfRemoveDotSegments($urlPath)
Remove all dot-segments in the provided URL path.
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:35
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...
wfGetLBFactory()
Get the load balancer factory object.
wfBaseName($path, $suffix= '')
Return the final portion of a pathname.
const PROTO_CANONICAL
Definition: Defines.php:224
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...
wfRandom()
Get a random decimal value between 0 and 1, in a way not likely to give duplicate values for any real...
$lines
Definition: router.php:67
wfReadOnlyReason()
Check if the site is in read-only mode and return the message if so.
wfUnpack($format, $data, $length=false)
Wrapper around php's unpack.
wfUseMW($req_ver)
This function works like "use VERSION" in Perl except it checks the version of MediaWiki, the program will die with a backtrace if the current version of MediaWiki is less than the version provided.
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:784
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
$wgScriptPath
The path we should point to.
static bcp47($code)
Get the normalised IETF language tag See unit test for examples.
static getLocalServerInstance($fallback=CACHE_NONE)
Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
static emitBufferedStatsdData(IBufferingStatsdDataFactory $stats, Config $config)
Send out any buffered statsd data according to sampling rules.
Definition: MediaWiki.php:930
wfGetMainCache()
Get the main cache object.
static pages()
Definition: SiteStats.php:154
wfArrayToCgi($array1, $array2=null, $prefix= '')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
$wgDBprefix
Table name prefix.
wfProfileIn($functionname)
Begin profiling of a function.
wfRestoreWarnings()
$wgStyleDirectory
Filesystem stylesheets directory.
wfRecursiveRemoveDir($dir)
Remove a directory and all its content.
wfMemoryLimit()
Set PHP's memory limit to the larger of php.ini or $wgMemoryLimit.
wfClearOutputBuffers()
More legible than passing a 'false' parameter to wfResetOutputBuffers():
$wgCanonicalServer
Canonical URL of the server, to use in IRC feeds and notification e-mails.
wfTransactionalTimeLimit()
Set PHP's time limit to the larger of php.ini or $wgTransactionalTimeLimit.
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g.with the RejectParserCacheValue hook) because MediaWiki won't do it for you.&$defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2589
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:56
wfSetupSession($sessionId=false)
Initialise php session.
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:302
Exception class for replica DB wait timeouts.
MediaWiki Logger LoggerFactory implements a PSR[0] compatible message logging system Named Psr Log LoggerInterface instances can be obtained from the MediaWiki Logger LoggerFactory::getInstance() static method.MediaWiki\Logger\LoggerFactory expects a class implementing the MediaWiki\Logger\Spi interface to act as a factory for new Psr\Log\LoggerInterface instances.The"Spi"in MediaWiki\Logger\Spi stands for"service provider interface".An SPI is an API intended to be implemented or extended by a third party.This software design pattern is intended to enable framework extension and replaceable components.It is specifically used in the MediaWiki\Logger\LoggerFactory service to allow alternate PSR-3 logging implementations to be easily integrated with MediaWiki.The service provider interface allows the backend logging library to be implemented in multiple ways.The $wgMWLoggerDefaultSpi global provides the classname of the default MediaWiki\Logger\Spi implementation to be loaded at runtime.This can either be the name of a class implementing the MediaWiki\Logger\Spi with a zero argument const ructor or a callable that will return an MediaWiki\Logger\Spi instance.Alternately the MediaWiki\Logger\LoggerFactory MediaWiki Logger LoggerFactory
Definition: logger.txt:5
wfArrayFilterByKey(array $arr, callable $callback)
Like array_filter with ARRAY_FILTER_USE_KEY, but works pre-5.6.
wfEscapeShellArg()
Version of escapeshellarg() that works better on Windows.
static legalChars()
Get a regex character class describing the legal characters in a link.
Definition: Title.php:623
wfAcceptToPrefs($accept, $def= '*/*')
Converts an Accept-* header into an array mapping string values to quality factors.
Status::newGood()`to allow deletion, and then`return false`from the hook function.Ensure you consume the 'ChangeTagAfterDelete'hook to carry out custom deletion actions.$tag:name of the tag $user:user initiating the action &$status:Status object.See above. 'ChangeTagsListActive':Allows you to nominate which of the tags your extension uses are in active use.&$tags:list of all active tags.Append to this array. 'ChangeTagsAfterUpdateTags':Called after tags have been updated with the ChangeTags::updateTags function.Params:$addedTags:tags effectively added in the update $removedTags:tags effectively removed in the update $prevTags:tags that were present prior to the update $rc_id:recentchanges table id $rev_id:revision table id $log_id:logging table id $params:tag params $rc:RecentChange being tagged when the tagging accompanies the action or null $user:User who performed the tagging when the tagging is subsequent to the action or null 'ChangeTagsAllowedAdd':Called when checking if a user can add tags to a change.&$allowedTags:List of all the tags the user is allowed to add.Any tags the user wants to add($addTags) that are not in this array will cause it to fail.You may add or remove tags to this array as required.$addTags:List of tags user intends to add.$user:User who is adding the tags. 'ChangeUserGroups':Called before user groups are changed.$performer:The User who will perform the change $user:The User whose groups will be changed &$add:The groups that will be added &$remove:The groups that will be removed 'Collation::factory':Called if $wgCategoryCollation is an unknown collation.$collationName:Name of the collation in question &$collationObject:Null.Replace with a subclass of the Collation class that implements the collation given in $collationName. 'ConfirmEmailComplete':Called after a user's email has been confirmed successfully.$user:user(object) whose email is being confirmed 'ContentAlterParserOutput':Modify parser output for a given content object.Called by Content::getParserOutput after parsing has finished.Can be used for changes that depend on the result of the parsing but have to be done before LinksUpdate is called(such as adding tracking categories based on the rendered HTML).$content:The Content to render $title:Title of the page, as context $parserOutput:ParserOutput to manipulate 'ContentGetParserOutput':Customize parser output for a given content object, called by AbstractContent::getParserOutput.May be used to override the normal model-specific rendering of page content.$content:The Content to render $title:Title of the page, as context $revId:The revision ID, as context $options:ParserOptions for rendering.To avoid confusing the parser cache, the output can only depend on parameters provided to this hook function, not on global state.$generateHtml:boolean, indicating whether full HTML should be generated.If false, generation of HTML may be skipped, but other information should still be present in the ParserOutput object.&$output:ParserOutput, to manipulate or replace 'ContentHandlerDefaultModelFor':Called when the default content model is determined for a given title.May be used to assign a different model for that title.$title:the Title in question &$model:the model name.Use with CONTENT_MODEL_XXX constants. 'ContentHandlerForModelID':Called when a ContentHandler is requested for a given content model name, but no entry for that model exists in $wgContentHandlers.Note:if your extension implements additional models via this hook, please use GetContentModels hook to make them known to core.$modeName:the requested content model name &$handler:set this to a ContentHandler object, if desired. 'ContentModelCanBeUsedOn':Called to determine whether that content model can be used on a given page.This is especially useful to prevent some content models to be used in some special location.$contentModel:ID of the content model in question $title:the Title in question.&$ok:Output parameter, whether it is OK to use $contentModel on $title.Handler functions that modify $ok should generally return false to prevent further hooks from further modifying $ok. 'ContribsPager::getQueryInfo':Before the contributions query is about to run &$pager:Pager object for contributions &$queryInfo:The query for the contribs Pager 'ContribsPager::reallyDoQuery':Called before really executing the query for My Contributions &$data:an array of results of all contribs queries $pager:The ContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'ContributionsLineEnding':Called before a contributions HTML line is finished $page:SpecialPage object for contributions &$ret:the HTML line $row:the DB row for this line &$classes:the classes to add to the surrounding< li > &$attribs:associative array of other HTML attributes for the< li > element.Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'ContributionsToolLinks':Change tool links above Special:Contributions $id:User identifier $title:User page title &$tools:Array of tool links $specialPage:SpecialPage instance for context and services.Can be either SpecialContributions or DeletedContributionsPage.Extensions should type hint against a generic SpecialPage though. 'ConvertContent':Called by AbstractContent::convert when a conversion to another content model is requested.Handler functions that modify $result should generally return false to disable further attempts at conversion.$content:The Content object to be converted.$toModel:The ID of the content model to convert to.$lossy:boolean indicating whether lossy conversion is allowed.&$result:Output parameter, in case the handler function wants to provide a converted Content object.Note that $result->getContentModel() must return $toModel. 'CustomEditor':When invoking the page editor Return true to allow the normal editor to be used, or false if implementing a custom editor, e.g.for a special namespace, etc.$article:Article being edited $user:User performing the edit 'DatabaseOraclePostInit':Called after initialising an Oracle database $db:the DatabaseOracle object 'DeletedContribsPager::reallyDoQuery':Called before really executing the query for Special:DeletedContributions Similar to ContribsPager::reallyDoQuery &$data:an array of results of all contribs queries $pager:The DeletedContribsPager object hooked into $offset:Index offset, inclusive $limit:Exact query limit $descending:Query direction, false for ascending, true for descending 'DeletedContributionsLineEnding':Called before a DeletedContributions HTML line is finished.Similar to ContributionsLineEnding $page:SpecialPage object for DeletedContributions &$ret:the HTML line $row:the DB row for this line &$classes:the classes to add to the surrounding< li > &$attribs:associative array of other HTML attributes for the< li > element.Currently only data attributes reserved to MediaWiki are allowed(see Sanitizer::isReservedDataAttribute). 'DifferenceEngineAfterLoadNewText':called in DifferenceEngine::loadNewText() after the new revision's content has been loaded into the class member variable $differenceEngine->mNewContent but before returning true from this function.$differenceEngine:DifferenceEngine object 'DifferenceEngineLoadTextAfterNewContentIsLoaded':called in DifferenceEngine::loadText() after the new revision's content has been loaded into the class member variable $differenceEngine->mNewContent but before checking if the variable's value is null.This hook can be used to inject content into said class member variable.$differenceEngine:DifferenceEngine object 'DifferenceEngineMarkPatrolledLink':Allows extensions to change the"mark as patrolled"link which is shown both on the diff header as well as on the bottom of a page, usually wrapped in a span element which has class="patrollink".$differenceEngine:DifferenceEngine object &$markAsPatrolledLink:The"mark as patrolled"link HTML(string) $rcid:Recent change ID(rc_id) for this change(int) 'DifferenceEngineMarkPatrolledRCID':Allows extensions to possibly change the rcid parameter.For example the rcid might be set to zero due to the user being the same as the performer of the change but an extension might still want to show it under certain conditions.&$rcid:rc_id(int) of the change or 0 $differenceEngine:DifferenceEngine object $change:RecentChange object $user:User object representing the current user 'DifferenceEngineNewHeader':Allows extensions to change the $newHeader variable, which contains information about the new revision, such as the revision's author, whether the revision was marked as a minor edit or not, etc.$differenceEngine:DifferenceEngine object &$newHeader:The string containing the various#mw-diff-otitle[1-5] divs, which include things like revision author info, revision comment, RevisionDelete link and more $formattedRevisionTools:Array containing revision tools, some of which may have been injected with the DiffRevisionTools hook $nextlink:String containing the link to the next revision(if any) $status
Definition: hooks.txt:1248
$wgServer
URL of the server.
wfMemcKey()
Make a cache key for the local wiki.
$wgOut
Definition: Setup.php:902
wfSplitWikiID($wiki)
Split a wiki ID into DB name and table prefix.
wfLogWarning($msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
wfResetSessionID()
Reset the session id.
wfGetNull()
Get a platform-independent path to the null file, e.g.
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:784
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
Definition: memcached.txt:96
wfTimestampOrNull($outputtype=TS_UNIX, $ts=null)
Return a formatted timestamp, or null if input is null.
float $wgRequestTime
Request start time as fractional seconds since epoch.
Definition: WebStart.php:42
wfParseUrl($url)
parse_url() work-alike, but non-broken.
static factory($code)
Get a cached or new language object for a given language code.
Definition: Language.php:183
wfArrayPlus2d(array $baseArray, array $newValues)
Merges two (possibly) 2 dimensional arrays into the target array ($baseArray).
wfShellExecWithStderr($cmd, &$retval=null, $environ=[], $limits=[])
Execute a shell command, returning both stdout and stderr.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition: File.php:51
wfGetPrecompiledData($name)
Get an object from the precompiled serialized directory.
$wgResponsiveImages
Generate and use thumbnails suitable for screens with 1.5 and 2.0 pixel densities.
wfMessage($key)
This is the function for getting translated interface messages.
$wgLoadScript
The URL path to load.php.
wfThumbIsStandard(File $file, array $params)
Returns true if these thumbnail parameters match one that MediaWiki requests from file description pa...
$wgDirectoryMode
Default value for chmoding of new directories.
wfStringToBool($val)
Convert string value to boolean, when the following are interpreted as true:
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:244
do that in ParserLimitReportFormat instead use this to modify the parameters of the image all existing parser cache entries will be invalid To avoid you ll need to handle that somehow(e.g.with the RejectParserCacheValue hook) because MediaWiki won't do it for you.&$defaults also a ContextSource after deleting those rows but within the same transaction you ll probably need to make sure the header is varied on $request
Definition: hooks.txt:2589
wfFindFile($title, $options=[])
Find a file.
static users()
Definition: SiteStats.php:162
wfGetCaller($level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
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:244
wfGetLangObj($langcode=false)
Return a Language object from $langcode.
wfGetScriptUrl()
Get the script URL.
$matches
$wgTransactionalTimeLimit
The minimum amount of time that MediaWiki needs for "slow" write request, particularly ones with mult...