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 
720 function wfRemoveDotSegments( $urlPath ) {
721  $output = '';
722  $inputOffset = 0;
723  $inputLength = strlen( $urlPath );
724 
725  while ( $inputOffset < $inputLength ) {
726  $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
727  $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
728  $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
729  $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
730  $trimOutput = false;
731 
732  if ( $prefixLengthTwo == './' ) {
733  # Step A, remove leading "./"
734  $inputOffset += 2;
735  } elseif ( $prefixLengthThree == '../' ) {
736  # Step A, remove leading "../"
737  $inputOffset += 3;
738  } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
739  # Step B, replace leading "/.$" with "/"
740  $inputOffset += 1;
741  $urlPath[$inputOffset] = '/';
742  } elseif ( $prefixLengthThree == '/./' ) {
743  # Step B, replace leading "/./" with "/"
744  $inputOffset += 2;
745  } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
746  # Step C, replace leading "/..$" with "/" and
747  # remove last path component in output
748  $inputOffset += 2;
749  $urlPath[$inputOffset] = '/';
750  $trimOutput = true;
751  } elseif ( $prefixLengthFour == '/../' ) {
752  # Step C, replace leading "/../" with "/" and
753  # remove last path component in output
754  $inputOffset += 3;
755  $trimOutput = true;
756  } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
757  # Step D, remove "^.$"
758  $inputOffset += 1;
759  } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
760  # Step D, remove "^..$"
761  $inputOffset += 2;
762  } else {
763  # Step E, move leading path segment to output
764  if ( $prefixLengthOne == '/' ) {
765  $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
766  } else {
767  $slashPos = strpos( $urlPath, '/', $inputOffset );
768  }
769  if ( $slashPos === false ) {
770  $output .= substr( $urlPath, $inputOffset );
771  $inputOffset = $inputLength;
772  } else {
773  $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
774  $inputOffset += $slashPos - $inputOffset;
775  }
776  }
777 
778  if ( $trimOutput ) {
779  $slashPos = strrpos( $output, '/' );
780  if ( $slashPos === false ) {
781  $output = '';
782  } else {
783  $output = substr( $output, 0, $slashPos );
784  }
785  }
786  }
787 
788  return $output;
789 }
790 
798 function wfUrlProtocols( $includeProtocolRelative = true ) {
799  global $wgUrlProtocols;
800 
801  // Cache return values separately based on $includeProtocolRelative
802  static $withProtRel = null, $withoutProtRel = null;
803  $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
804  if ( !is_null( $cachedValue ) ) {
805  return $cachedValue;
806  }
807 
808  // Support old-style $wgUrlProtocols strings, for backwards compatibility
809  // with LocalSettings files from 1.5
810  if ( is_array( $wgUrlProtocols ) ) {
811  $protocols = [];
812  foreach ( $wgUrlProtocols as $protocol ) {
813  // Filter out '//' if !$includeProtocolRelative
814  if ( $includeProtocolRelative || $protocol !== '//' ) {
815  $protocols[] = preg_quote( $protocol, '/' );
816  }
817  }
818 
819  $retval = implode( '|', $protocols );
820  } else {
821  // Ignore $includeProtocolRelative in this case
822  // This case exists for pre-1.6 compatibility, and we can safely assume
823  // that '//' won't appear in a pre-1.6 config because protocol-relative
824  // URLs weren't supported until 1.18
825  $retval = $wgUrlProtocols;
826  }
827 
828  // Cache return value
829  if ( $includeProtocolRelative ) {
830  $withProtRel = $retval;
831  } else {
832  $withoutProtRel = $retval;
833  }
834  return $retval;
835 }
836 
843 function wfUrlProtocolsWithoutProtRel() {
844  return wfUrlProtocols( false );
845 }
846 
872 function wfParseUrl( $url ) {
873  global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
874 
875  // Protocol-relative URLs are handled really badly by parse_url(). It's so
876  // bad that the easiest way to handle them is to just prepend 'http:' and
877  // strip the protocol out later.
878  $wasRelative = substr( $url, 0, 2 ) == '//';
879  if ( $wasRelative ) {
880  $url = "http:$url";
881  }
882  Wikimedia\suppressWarnings();
883  $bits = parse_url( $url );
884  Wikimedia\restoreWarnings();
885  // parse_url() returns an array without scheme for some invalid URLs, e.g.
886  // parse_url("%0Ahttp://example.com") == [ 'host' => '%0Ahttp', 'path' => 'example.com' ]
887  if ( !$bits || !isset( $bits['scheme'] ) ) {
888  return false;
889  }
890 
891  // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
892  $bits['scheme'] = strtolower( $bits['scheme'] );
893 
894  // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
895  if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
896  $bits['delimiter'] = '://';
897  } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
898  $bits['delimiter'] = ':';
899  // parse_url detects for news: and mailto: the host part of an url as path
900  // We have to correct this wrong detection
901  if ( isset( $bits['path'] ) ) {
902  $bits['host'] = $bits['path'];
903  $bits['path'] = '';
904  }
905  } else {
906  return false;
907  }
908 
909  /* Provide an empty host for eg. file:/// urls (see T30627) */
910  if ( !isset( $bits['host'] ) ) {
911  $bits['host'] = '';
912 
913  // See T47069
914  if ( isset( $bits['path'] ) ) {
915  /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
916  if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
917  $bits['path'] = '/' . $bits['path'];
918  }
919  } else {
920  $bits['path'] = '';
921  }
922  }
923 
924  // If the URL was protocol-relative, fix scheme and delimiter
925  if ( $wasRelative ) {
926  $bits['scheme'] = '';
927  $bits['delimiter'] = '//';
928  }
929  return $bits;
930 }
931 
942 function wfExpandIRI( $url ) {
943  return preg_replace_callback(
944  '/((?:%[89A-F][0-9A-F])+)/i',
945  'wfExpandIRI_callback',
946  wfExpandUrl( $url )
947  );
948 }
949 
955 function wfExpandIRI_callback( $matches ) {
956  return urldecode( $matches[1] );
957 }
958 
965 function wfMakeUrlIndexes( $url ) {
966  $bits = wfParseUrl( $url );
967 
968  // Reverse the labels in the hostname, convert to lower case
969  // For emails reverse domainpart only
970  if ( $bits['scheme'] == 'mailto' ) {
971  $mailparts = explode( '@', $bits['host'], 2 );
972  if ( count( $mailparts ) === 2 ) {
973  $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
974  } else {
975  // No domain specified, don't mangle it
976  $domainpart = '';
977  }
978  $reversedHost = $domainpart . '@' . $mailparts[0];
979  } else {
980  $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
981  }
982  // Add an extra dot to the end
983  // Why? Is it in wrong place in mailto links?
984  if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
985  $reversedHost .= '.';
986  }
987  // Reconstruct the pseudo-URL
988  $prot = $bits['scheme'];
989  $index = $prot . $bits['delimiter'] . $reversedHost;
990  // Leave out user and password. Add the port, path, query and fragment
991  if ( isset( $bits['port'] ) ) {
992  $index .= ':' . $bits['port'];
993  }
994  if ( isset( $bits['path'] ) ) {
995  $index .= $bits['path'];
996  } else {
997  $index .= '/';
998  }
999  if ( isset( $bits['query'] ) ) {
1000  $index .= '?' . $bits['query'];
1001  }
1002  if ( isset( $bits['fragment'] ) ) {
1003  $index .= '#' . $bits['fragment'];
1004  }
1005 
1006  if ( $prot == '' ) {
1007  return [ "http:$index", "https:$index" ];
1008  } else {
1009  return [ $index ];
1010  }
1011 }
1012 
1019 function wfMatchesDomainList( $url, $domains ) {
1020  $bits = wfParseUrl( $url );
1021  if ( is_array( $bits ) && isset( $bits['host'] ) ) {
1022  $host = '.' . $bits['host'];
1023  foreach ( (array)$domains as $domain ) {
1024  $domain = '.' . $domain;
1025  if ( substr( $host, -strlen( $domain ) ) === $domain ) {
1026  return true;
1027  }
1028  }
1029  }
1030  return false;
1031 }
1032 
1053 function wfDebug( $text, $dest = 'all', array $context = [] ) {
1054  global $wgDebugRawPage, $wgDebugLogPrefix;
1056 
1057  if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1058  return;
1059  }
1060 
1061  $text = trim( $text );
1062 
1063  if ( $wgDebugTimestamps ) {
1064  $context['seconds_elapsed'] = sprintf(
1065  '%6.4f',
1066  microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT']
1067  );
1068  $context['memory_used'] = sprintf(
1069  '%5.1fM',
1070  ( memory_get_usage( true ) / ( 1024 * 1024 ) )
1071  );
1072  }
1073 
1074  if ( $wgDebugLogPrefix !== '' ) {
1075  $context['prefix'] = $wgDebugLogPrefix;
1076  }
1077  $context['private'] = ( $dest === false || $dest === 'private' );
1078 
1079  $logger = LoggerFactory::getInstance( 'wfDebug' );
1080  $logger->debug( $text, $context );
1081 }
1082 
1087 function wfIsDebugRawPage() {
1088  static $cache;
1089  if ( $cache !== null ) {
1090  return $cache;
1091  }
1092  // Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
1093  // phpcs:ignore MediaWiki.Usage.SuperGlobalsUsage.SuperGlobals
1094  if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
1095  || (
1096  isset( $_SERVER['SCRIPT_NAME'] )
1097  && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
1098  )
1099  ) {
1100  $cache = true;
1101  } else {
1102  $cache = false;
1103  }
1104  return $cache;
1105 }
1106 
1112 function wfDebugMem( $exact = false ) {
1113  $mem = memory_get_usage();
1114  if ( !$exact ) {
1115  $mem = floor( $mem / 1024 ) . ' KiB';
1116  } else {
1117  $mem .= ' B';
1118  }
1119  wfDebug( "Memory usage: $mem\n" );
1120 }
1121 
1147 function wfDebugLog(
1148  $logGroup, $text, $dest = 'all', array $context = []
1149 ) {
1150  $text = trim( $text );
1151 
1152  $logger = LoggerFactory::getInstance( $logGroup );
1153  $context['private'] = ( $dest === false || $dest === 'private' );
1154  $logger->info( $text, $context );
1155 }
1156 
1165 function wfLogDBError( $text, array $context = [] ) {
1166  $logger = LoggerFactory::getInstance( 'wfLogDBError' );
1167  $logger->error( trim( $text ), $context );
1168 }
1169 
1183 function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
1184  MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
1185 }
1186 
1197 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
1198  MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
1199 }
1200 
1210 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
1211  MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
1212 }
1213 
1227 function wfErrorLog( $text, $file, array $context = [] ) {
1228  wfDeprecated( __METHOD__, '1.25' );
1229  $logger = LoggerFactory::getInstance( 'wfErrorLog' );
1230  $context['destination'] = $file;
1231  $logger->info( trim( $text ), $context );
1232 }
1233 
1238 function wfLogProfilingData() {
1240 
1242  $request = $context->getRequest();
1243 
1244  $profiler = Profiler::instance();
1245  $profiler->setContext( $context );
1246  $profiler->logData();
1247 
1248  // Send out any buffered statsd metrics as needed
1250  MediaWikiServices::getInstance()->getStatsdDataFactory(),
1251  $context->getConfig()
1252  );
1253 
1254  // Profiling must actually be enabled...
1255  if ( $profiler instanceof ProfilerStub ) {
1256  return;
1257  }
1258 
1259  if ( isset( $wgDebugLogGroups['profileoutput'] )
1260  && $wgDebugLogGroups['profileoutput'] === false
1261  ) {
1262  // Explicitly disabled
1263  return;
1264  }
1265  if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
1266  return;
1267  }
1268 
1269  $ctx = [ 'elapsed' => $request->getElapsedTime() ];
1270  if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
1271  $ctx['forwarded_for'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
1272  }
1273  if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
1274  $ctx['client_ip'] = $_SERVER['HTTP_CLIENT_IP'];
1275  }
1276  if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
1277  $ctx['from'] = $_SERVER['HTTP_FROM'];
1278  }
1279  if ( isset( $ctx['forwarded_for'] ) ||
1280  isset( $ctx['client_ip'] ) ||
1281  isset( $ctx['from'] ) ) {
1282  $ctx['proxy'] = $_SERVER['REMOTE_ADDR'];
1283  }
1284 
1285  // Don't load $wgUser at this late stage just for statistics purposes
1286  // @todo FIXME: We can detect some anons even if it is not loaded.
1287  // See User::getId()
1288  $user = $context->getUser();
1289  $ctx['anon'] = $user->isItemLoaded( 'id' ) && $user->isAnon();
1290 
1291  // Command line script uses a FauxRequest object which does not have
1292  // any knowledge about an URL and throw an exception instead.
1293  try {
1294  $ctx['url'] = urldecode( $request->getRequestURL() );
1295  } catch ( Exception $ignored ) {
1296  // no-op
1297  }
1298 
1299  $ctx['output'] = $profiler->getOutput();
1300 
1301  $log = LoggerFactory::getInstance( 'profileoutput' );
1302  $log->info( "Elapsed: {elapsed}; URL: <{url}>\n{output}", $ctx );
1303 }
1304 
1312 function wfIncrStats( $key, $count = 1 ) {
1313  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
1314  $stats->updateCount( $key, $count );
1315 }
1316 
1322 function wfReadOnly() {
1323  return MediaWikiServices::getInstance()->getReadOnlyMode()
1324  ->isReadOnly();
1325 }
1326 
1335 function wfReadOnlyReason() {
1336  return MediaWikiServices::getInstance()->getReadOnlyMode()
1337  ->getReason();
1338 }
1339 
1346 function wfConfiguredReadOnlyReason() {
1347  return MediaWikiServices::getInstance()->getConfiguredReadOnlyMode()
1348  ->getReason();
1349 }
1350 
1366 function wfGetLangObj( $langcode = false ) {
1367  # Identify which language to get or create a language object for.
1368  # Using is_object here due to Stub objects.
1369  if ( is_object( $langcode ) ) {
1370  # Great, we already have the object (hopefully)!
1371  return $langcode;
1372  }
1373 
1375  if ( $langcode === true || $langcode === $wgLanguageCode ) {
1376  # $langcode is the language code of the wikis content language object.
1377  # or it is a boolean and value is true
1378  return $wgContLang;
1379  }
1380 
1381  global $wgLang;
1382  if ( $langcode === false || $langcode === $wgLang->getCode() ) {
1383  # $langcode is the language code of user language object.
1384  # or it was a boolean and value is false
1385  return $wgLang;
1386  }
1387 
1388  $validCodes = array_keys( Language::fetchLanguageNames() );
1389  if ( in_array( $langcode, $validCodes ) ) {
1390  # $langcode corresponds to a valid language.
1391  return Language::factory( $langcode );
1392  }
1393 
1394  # $langcode is a string, but not a valid language code; use content language.
1395  wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
1396  return $wgContLang;
1397 }
1398 
1415 function wfMessage( $key /*...*/ ) {
1416  $message = new Message( $key );
1417 
1418  // We call Message::params() to reduce code duplication
1419  $params = func_get_args();
1420  array_shift( $params );
1421  if ( $params ) {
1422  call_user_func_array( [ $message, 'params' ], $params );
1423  }
1424 
1425  return $message;
1426 }
1427 
1440 function wfMessageFallback( /*...*/ ) {
1441  $args = func_get_args();
1442  return call_user_func_array( 'Message::newFallbackSequence', $args );
1443 }
1444 
1453 function wfMsgReplaceArgs( $message, $args ) {
1454  # Fix windows line-endings
1455  # Some messages are split with explode("\n", $msg)
1456  $message = str_replace( "\r", '', $message );
1457 
1458  // Replace arguments
1459  if ( is_array( $args ) && $args ) {
1460  if ( is_array( $args[0] ) ) {
1461  $args = array_values( $args[0] );
1462  }
1463  $replacementKeys = [];
1464  foreach ( $args as $n => $param ) {
1465  $replacementKeys['$' . ( $n + 1 )] = $param;
1466  }
1467  $message = strtr( $message, $replacementKeys );
1468  }
1469 
1470  return $message;
1471 }
1472 
1480 function wfHostname() {
1481  static $host;
1482  if ( is_null( $host ) ) {
1483  # Hostname overriding
1484  global $wgOverrideHostname;
1485  if ( $wgOverrideHostname !== false ) {
1486  # Set static and skip any detection
1487  $host = $wgOverrideHostname;
1488  return $host;
1489  }
1490 
1491  if ( function_exists( 'posix_uname' ) ) {
1492  // This function not present on Windows
1493  $uname = posix_uname();
1494  } else {
1495  $uname = false;
1496  }
1497  if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
1498  $host = $uname['nodename'];
1499  } elseif ( getenv( 'COMPUTERNAME' ) ) {
1500  # Windows computer name
1501  $host = getenv( 'COMPUTERNAME' );
1502  } else {
1503  # This may be a virtual server.
1504  $host = $_SERVER['SERVER_NAME'];
1505  }
1506  }
1507  return $host;
1508 }
1509 
1520 function wfReportTime( $nonce = null ) {
1521  global $wgShowHostnames;
1522 
1523  $elapsed = ( microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'] );
1524  // seconds to milliseconds
1525  $responseTime = round( $elapsed * 1000 );
1526  $reportVars = [ 'wgBackendResponseTime' => $responseTime ];
1527  if ( $wgShowHostnames ) {
1528  $reportVars['wgHostname'] = wfHostname();
1529  }
1530  return Skin::makeVariablesScript( $reportVars, $nonce );
1531 }
1532 
1543 function wfDebugBacktrace( $limit = 0 ) {
1544  static $disabled = null;
1545 
1546  if ( is_null( $disabled ) ) {
1547  $disabled = !function_exists( 'debug_backtrace' );
1548  if ( $disabled ) {
1549  wfDebug( "debug_backtrace() is disabled\n" );
1550  }
1551  }
1552  if ( $disabled ) {
1553  return [];
1554  }
1555 
1556  if ( $limit ) {
1557  return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
1558  } else {
1559  return array_slice( debug_backtrace(), 1 );
1560  }
1561 }
1562 
1571 function wfBacktrace( $raw = null ) {
1573 
1574  if ( $raw === null ) {
1575  $raw = $wgCommandLineMode;
1576  }
1577 
1578  if ( $raw ) {
1579  $frameFormat = "%s line %s calls %s()\n";
1580  $traceFormat = "%s";
1581  } else {
1582  $frameFormat = "<li>%s line %s calls %s()</li>\n";
1583  $traceFormat = "<ul>\n%s</ul>\n";
1584  }
1585 
1586  $frames = array_map( function ( $frame ) use ( $frameFormat ) {
1587  $file = !empty( $frame['file'] ) ? basename( $frame['file'] ) : '-';
1588  $line = isset( $frame['line'] ) ? $frame['line'] : '-';
1589  $call = $frame['function'];
1590  if ( !empty( $frame['class'] ) ) {
1591  $call = $frame['class'] . $frame['type'] . $call;
1592  }
1593  return sprintf( $frameFormat, $file, $line, $call );
1594  }, wfDebugBacktrace() );
1595 
1596  return sprintf( $traceFormat, implode( '', $frames ) );
1597 }
1598 
1608 function wfGetCaller( $level = 2 ) {
1609  $backtrace = wfDebugBacktrace( $level + 1 );
1610  if ( isset( $backtrace[$level] ) ) {
1611  return wfFormatStackFrame( $backtrace[$level] );
1612  } else {
1613  return 'unknown';
1614  }
1615 }
1616 
1624 function wfGetAllCallers( $limit = 3 ) {
1625  $trace = array_reverse( wfDebugBacktrace() );
1626  if ( !$limit || $limit > count( $trace ) - 1 ) {
1627  $limit = count( $trace ) - 1;
1628  }
1629  $trace = array_slice( $trace, -$limit - 1, $limit );
1630  return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
1631 }
1632 
1639 function wfFormatStackFrame( $frame ) {
1640  if ( !isset( $frame['function'] ) ) {
1641  return 'NO_FUNCTION_GIVEN';
1642  }
1643  return isset( $frame['class'] ) && isset( $frame['type'] ) ?
1644  $frame['class'] . $frame['type'] . $frame['function'] :
1645  $frame['function'];
1646 }
1647 
1648 /* Some generic result counters, pulled out of SearchEngine */
1649 
1657 function wfShowingResults( $offset, $limit ) {
1658  return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
1659 }
1660 
1670 function wfClientAcceptsGzip( $force = false ) {
1671  static $result = null;
1672  if ( $result === null || $force ) {
1673  $result = false;
1674  if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
1675  # @todo FIXME: We may want to blacklist some broken browsers
1676  $m = [];
1677  if ( preg_match(
1678  '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
1679  $_SERVER['HTTP_ACCEPT_ENCODING'],
1680  $m
1681  )
1682  ) {
1683  if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
1684  $result = false;
1685  return $result;
1686  }
1687  wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
1688  $result = true;
1689  }
1690  }
1691  }
1692  return $result;
1693 }
1694 
1704 function wfEscapeWikiText( $text ) {
1705  global $wgEnableMagicLinks;
1706  static $repl = null, $repl2 = null;
1707  if ( $repl === null || defined( 'MW_PARSER_TEST' ) || defined( 'MW_PHPUNIT_TEST' ) ) {
1708  // Tests depend upon being able to change $wgEnableMagicLinks, so don't cache
1709  // in those situations
1710  $repl = [
1711  '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
1712  '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
1713  '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
1714  "\n#" => "\n&#35;", "\r#" => "\r&#35;",
1715  "\n*" => "\n&#42;", "\r*" => "\r&#42;",
1716  "\n:" => "\n&#58;", "\r:" => "\r&#58;",
1717  "\n " => "\n&#32;", "\r " => "\r&#32;",
1718  "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
1719  "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
1720  "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
1721  "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
1722  '__' => '_&#95;', '://' => '&#58;//',
1723  ];
1724 
1725  $magicLinks = array_keys( array_filter( $wgEnableMagicLinks ) );
1726  // We have to catch everything "\s" matches in PCRE
1727  foreach ( $magicLinks as $magic ) {
1728  $repl["$magic "] = "$magic&#32;";
1729  $repl["$magic\t"] = "$magic&#9;";
1730  $repl["$magic\r"] = "$magic&#13;";
1731  $repl["$magic\n"] = "$magic&#10;";
1732  $repl["$magic\f"] = "$magic&#12;";
1733  }
1734 
1735  // And handle protocols that don't use "://"
1736  global $wgUrlProtocols;
1737  $repl2 = [];
1738  foreach ( $wgUrlProtocols as $prot ) {
1739  if ( substr( $prot, -1 ) === ':' ) {
1740  $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
1741  }
1742  }
1743  $repl2 = $repl2 ? '/\b(' . implode( '|', $repl2 ) . '):/i' : '/^(?!)/';
1744  }
1745  $text = substr( strtr( "\n$text", $repl ), 1 );
1746  $text = preg_replace( $repl2, '$1&#58;', $text );
1747  return $text;
1748 }
1749 
1760 function wfSetVar( &$dest, $source, $force = false ) {
1761  $temp = $dest;
1762  if ( !is_null( $source ) || $force ) {
1763  $dest = $source;
1764  }
1765  return $temp;
1766 }
1767 
1777 function wfSetBit( &$dest, $bit, $state = true ) {
1778  $temp = (bool)( $dest & $bit );
1779  if ( !is_null( $state ) ) {
1780  if ( $state ) {
1781  $dest |= $bit;
1782  } else {
1783  $dest &= ~$bit;
1784  }
1785  }
1786  return $temp;
1787 }
1788 
1795 function wfVarDump( $var ) {
1796  global $wgOut;
1797  $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
1798  if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
1799  print $s;
1800  } else {
1801  $wgOut->addHTML( $s );
1802  }
1803 }
1804 
1812 function wfHttpError( $code, $label, $desc ) {
1813  global $wgOut;
1815  if ( $wgOut ) {
1816  $wgOut->disable();
1817  $wgOut->sendCacheControl();
1818  }
1819 
1821  header( 'Content-type: text/html; charset=utf-8' );
1822  print '<!DOCTYPE html>' .
1823  '<html><head><title>' .
1824  htmlspecialchars( $label ) .
1825  '</title></head><body><h1>' .
1826  htmlspecialchars( $label ) .
1827  '</h1><p>' .
1828  nl2br( htmlspecialchars( $desc ) ) .
1829  "</p></body></html>\n";
1830 }
1831 
1849 function wfResetOutputBuffers( $resetGzipEncoding = true ) {
1850  if ( $resetGzipEncoding ) {
1851  // Suppress Content-Encoding and Content-Length
1852  // headers from OutputHandler::handle.
1854  $wgDisableOutputCompression = true;
1855  }
1856  while ( $status = ob_get_status() ) {
1857  if ( isset( $status['flags'] ) ) {
1858  $flags = PHP_OUTPUT_HANDLER_CLEANABLE | PHP_OUTPUT_HANDLER_REMOVABLE;
1859  $deleteable = ( $status['flags'] & $flags ) === $flags;
1860  } elseif ( isset( $status['del'] ) ) {
1861  $deleteable = $status['del'];
1862  } else {
1863  // Guess that any PHP-internal setting can't be removed.
1864  $deleteable = $status['type'] !== 0; /* PHP_OUTPUT_HANDLER_INTERNAL */
1865  }
1866  if ( !$deleteable ) {
1867  // Give up, and hope the result doesn't break
1868  // output behavior.
1869  break;
1870  }
1871  if ( $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier' ) {
1872  // Unit testing barrier to prevent this function from breaking PHPUnit.
1873  break;
1874  }
1875  if ( !ob_end_clean() ) {
1876  // Could not remove output buffer handler; abort now
1877  // to avoid getting in some kind of infinite loop.
1878  break;
1879  }
1880  if ( $resetGzipEncoding ) {
1881  if ( $status['name'] == 'ob_gzhandler' ) {
1882  // Reset the 'Content-Encoding' field set by this handler
1883  // so we can start fresh.
1884  header_remove( 'Content-Encoding' );
1885  break;
1886  }
1887  }
1888  }
1889 }
1890 
1903 function wfClearOutputBuffers() {
1904  wfResetOutputBuffers( false );
1905 }
1906 
1915 function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1916  # No arg means accept anything (per HTTP spec)
1917  if ( !$accept ) {
1918  return [ $def => 1.0 ];
1919  }
1920 
1921  $prefs = [];
1922 
1923  $parts = explode( ',', $accept );
1924 
1925  foreach ( $parts as $part ) {
1926  # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
1927  $values = explode( ';', trim( $part ) );
1928  $match = [];
1929  if ( count( $values ) == 1 ) {
1930  $prefs[$values[0]] = 1.0;
1931  } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
1932  $prefs[$values[0]] = floatval( $match[1] );
1933  }
1934  }
1935 
1936  return $prefs;
1937 }
1938 
1951 function mimeTypeMatch( $type, $avail ) {
1952  if ( array_key_exists( $type, $avail ) ) {
1953  return $type;
1954  } else {
1955  $mainType = explode( '/', $type )[0];
1956  if ( array_key_exists( "$mainType/*", $avail ) ) {
1957  return "$mainType/*";
1958  } elseif ( array_key_exists( '*/*', $avail ) ) {
1959  return '*/*';
1960  } else {
1961  return null;
1962  }
1963  }
1964 }
1965 
1979 function wfNegotiateType( $cprefs, $sprefs ) {
1980  $combine = [];
1981 
1982  foreach ( array_keys( $sprefs ) as $type ) {
1983  $subType = explode( '/', $type )[1];
1984  if ( $subType != '*' ) {
1985  $ckey = mimeTypeMatch( $type, $cprefs );
1986  if ( $ckey ) {
1987  $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1988  }
1989  }
1990  }
1991 
1992  foreach ( array_keys( $cprefs ) as $type ) {
1993  $subType = explode( '/', $type )[1];
1994  if ( $subType != '*' && !array_key_exists( $type, $sprefs ) ) {
1995  $skey = mimeTypeMatch( $type, $sprefs );
1996  if ( $skey ) {
1997  $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1998  }
1999  }
2000  }
2001 
2002  $bestq = 0;
2003  $besttype = null;
2004 
2005  foreach ( array_keys( $combine ) as $type ) {
2006  if ( $combine[$type] > $bestq ) {
2007  $besttype = $type;
2008  $bestq = $combine[$type];
2009  }
2010  }
2011 
2012  return $besttype;
2013 }
2014 
2021 function wfSuppressWarnings( $end = false ) {
2022  Wikimedia\suppressWarnings( $end );
2023 }
2024 
2029 function wfRestoreWarnings() {
2030  Wikimedia\restoreWarnings();
2031 }
2032 
2041 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
2042  $ret = MWTimestamp::convert( $outputtype, $ts );
2043  if ( $ret === false ) {
2044  wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
2045  }
2046  return $ret;
2047 }
2048 
2057 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
2058  if ( is_null( $ts ) ) {
2059  return null;
2060  } else {
2061  return wfTimestamp( $outputtype, $ts );
2062  }
2063 }
2064 
2070 function wfTimestampNow() {
2071  # return NOW
2072  return MWTimestamp::now( TS_MW );
2073 }
2074 
2080 function wfIsWindows() {
2081  static $isWindows = null;
2082  if ( $isWindows === null ) {
2083  $isWindows = strtoupper( substr( PHP_OS, 0, 3 ) ) === 'WIN';
2084  }
2085  return $isWindows;
2086 }
2087 
2093 function wfIsHHVM() {
2094  return defined( 'HHVM_VERSION' );
2095 }
2096 
2103 function wfIsCLI() {
2104  return PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg';
2105 }
2106 
2118 function wfTempDir() {
2120 
2121  if ( $wgTmpDirectory !== false ) {
2122  return $wgTmpDirectory;
2123  }
2124 
2126 }
2127 
2137 function wfMkdirParents( $dir, $mode = null, $caller = null ) {
2139 
2140  if ( FileBackend::isStoragePath( $dir ) ) { // sanity
2141  throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
2142  }
2143 
2144  if ( !is_null( $caller ) ) {
2145  wfDebug( "$caller: called wfMkdirParents($dir)\n" );
2146  }
2147 
2148  if ( strval( $dir ) === '' || is_dir( $dir ) ) {
2149  return true;
2150  }
2151 
2152  $dir = str_replace( [ '\\', '/' ], DIRECTORY_SEPARATOR, $dir );
2153 
2154  if ( is_null( $mode ) ) {
2155  $mode = $wgDirectoryMode;
2156  }
2157 
2158  // Turn off the normal warning, we're doing our own below
2159  Wikimedia\suppressWarnings();
2160  $ok = mkdir( $dir, $mode, true ); // PHP5 <3
2161  Wikimedia\restoreWarnings();
2162 
2163  if ( !$ok ) {
2164  // directory may have been created on another request since we last checked
2165  if ( is_dir( $dir ) ) {
2166  return true;
2167  }
2168 
2169  // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
2170  wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
2171  }
2172  return $ok;
2173 }
2174 
2180 function wfRecursiveRemoveDir( $dir ) {
2181  wfDebug( __FUNCTION__ . "( $dir )\n" );
2182  // taken from https://secure.php.net/manual/en/function.rmdir.php#98622
2183  if ( is_dir( $dir ) ) {
2184  $objects = scandir( $dir );
2185  foreach ( $objects as $object ) {
2186  if ( $object != "." && $object != ".." ) {
2187  if ( filetype( $dir . '/' . $object ) == "dir" ) {
2188  wfRecursiveRemoveDir( $dir . '/' . $object );
2189  } else {
2190  unlink( $dir . '/' . $object );
2191  }
2192  }
2193  }
2194  reset( $objects );
2195  rmdir( $dir );
2196  }
2197 }
2198 
2205 function wfPercent( $nr, $acc = 2, $round = true ) {
2206  $ret = sprintf( "%.${acc}f", $nr );
2207  return $round ? round( $ret, $acc ) . '%' : "$ret%";
2208 }
2209 
2233 function wfIniGetBool( $setting ) {
2234  return wfStringToBool( ini_get( $setting ) );
2235 }
2236 
2249 function wfStringToBool( $val ) {
2250  $val = strtolower( $val );
2251  // 'on' and 'true' can't have whitespace around them, but '1' can.
2252  return $val == 'on'
2253  || $val == 'true'
2254  || $val == 'yes'
2255  || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
2256 }
2257 
2270 function wfEscapeShellArg( /*...*/ ) {
2271  $args = func_get_args();
2272 
2273  return call_user_func_array( Shell::class . '::escape', $args );
2274 }
2275 
2299 function wfShellExec( $cmd, &$retval = null, $environ = [],
2300  $limits = [], $options = []
2301 ) {
2302  if ( Shell::isDisabled() ) {
2303  $retval = 1;
2304  // Backwards compatibility be upon us...
2305  return 'Unable to run external programs, proc_open() is disabled.';
2306  }
2307 
2308  if ( is_array( $cmd ) ) {
2309  $cmd = Shell::escape( $cmd );
2310  }
2311 
2312  $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
2313  $profileMethod = isset( $options['profileMethod'] ) ? $options['profileMethod'] : wfGetCaller();
2314 
2315  try {
2316  $result = Shell::command( [] )
2317  ->unsafeParams( (array)$cmd )
2318  ->environment( $environ )
2319  ->limits( $limits )
2320  ->includeStderr( $includeStderr )
2321  ->profileMethod( $profileMethod )
2322  // For b/c
2323  ->restrict( Shell::RESTRICT_NONE )
2324  ->execute();
2325  } catch ( ProcOpenError $ex ) {
2326  $retval = -1;
2327  return '';
2328  }
2329 
2330  $retval = $result->getExitCode();
2331 
2332  return $result->getStdout();
2333 }
2334 
2352 function wfShellExecWithStderr( $cmd, &$retval = null, $environ = [], $limits = [] ) {
2353  return wfShellExec( $cmd, $retval, $environ, $limits,
2354  [ 'duplicateStderr' => true, 'profileMethod' => wfGetCaller() ] );
2355 }
2356 
2371 function wfShellWikiCmd( $script, array $parameters = [], array $options = [] ) {
2372  global $wgPhpCli;
2373  // Give site config file a chance to run the script in a wrapper.
2374  // The caller may likely want to call wfBasename() on $script.
2375  Hooks::run( 'wfShellWikiCmd', [ &$script, &$parameters, &$options ] );
2376  $cmd = isset( $options['php'] ) ? [ $options['php'] ] : [ $wgPhpCli ];
2377  if ( isset( $options['wrapper'] ) ) {
2378  $cmd[] = $options['wrapper'];
2379  }
2380  $cmd[] = $script;
2381  // Escape each parameter for shell
2382  return Shell::escape( array_merge( $cmd, $parameters ) );
2383 }
2384 
2396 function wfMerge( $old, $mine, $yours, &$result, &$mergeAttemptResult = null ) {
2397  global $wgDiff3;
2398 
2399  # This check may also protect against code injection in
2400  # case of broken installations.
2401  Wikimedia\suppressWarnings();
2402  $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
2403  Wikimedia\restoreWarnings();
2404 
2405  if ( !$haveDiff3 ) {
2406  wfDebug( "diff3 not found\n" );
2407  return false;
2408  }
2409 
2410  # Make temporary files
2411  $td = wfTempDir();
2412  $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2413  $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
2414  $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
2415 
2416  # NOTE: diff3 issues a warning to stderr if any of the files does not end with
2417  # a newline character. To avoid this, we normalize the trailing whitespace before
2418  # creating the diff.
2419 
2420  fwrite( $oldtextFile, rtrim( $old ) . "\n" );
2421  fclose( $oldtextFile );
2422  fwrite( $mytextFile, rtrim( $mine ) . "\n" );
2423  fclose( $mytextFile );
2424  fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
2425  fclose( $yourtextFile );
2426 
2427  # Check for a conflict
2428  $cmd = Shell::escape( $wgDiff3, '-a', '--overlap-only', $mytextName,
2429  $oldtextName, $yourtextName );
2430  $handle = popen( $cmd, 'r' );
2431 
2432  $mergeAttemptResult = '';
2433  do {
2434  $data = fread( $handle, 8192 );
2435  if ( strlen( $data ) == 0 ) {
2436  break;
2437  }
2438  $mergeAttemptResult .= $data;
2439  } while ( true );
2440  pclose( $handle );
2441 
2442  $conflict = $mergeAttemptResult !== '';
2443 
2444  # Merge differences
2445  $cmd = Shell::escape( $wgDiff3, '-a', '-e', '--merge', $mytextName,
2446  $oldtextName, $yourtextName );
2447  $handle = popen( $cmd, 'r' );
2448  $result = '';
2449  do {
2450  $data = fread( $handle, 8192 );
2451  if ( strlen( $data ) == 0 ) {
2452  break;
2453  }
2454  $result .= $data;
2455  } while ( true );
2456  pclose( $handle );
2457  unlink( $mytextName );
2458  unlink( $oldtextName );
2459  unlink( $yourtextName );
2460 
2461  if ( $result === '' && $old !== '' && !$conflict ) {
2462  wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
2463  $conflict = true;
2464  }
2465  return !$conflict;
2466 }
2467 
2479 function wfDiff( $before, $after, $params = '-u' ) {
2480  if ( $before == $after ) {
2481  return '';
2482  }
2483 
2484  global $wgDiff;
2485  Wikimedia\suppressWarnings();
2486  $haveDiff = $wgDiff && file_exists( $wgDiff );
2487  Wikimedia\restoreWarnings();
2488 
2489  # This check may also protect against code injection in
2490  # case of broken installations.
2491  if ( !$haveDiff ) {
2492  wfDebug( "diff executable not found\n" );
2493  $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
2494  $format = new UnifiedDiffFormatter();
2495  return $format->format( $diffs );
2496  }
2497 
2498  # Make temporary files
2499  $td = wfTempDir();
2500  $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
2501  $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
2502 
2503  fwrite( $oldtextFile, $before );
2504  fclose( $oldtextFile );
2505  fwrite( $newtextFile, $after );
2506  fclose( $newtextFile );
2507 
2508  // Get the diff of the two files
2509  $cmd = "$wgDiff " . $params . ' ' . Shell::escape( $oldtextName, $newtextName );
2510 
2511  $h = popen( $cmd, 'r' );
2512  if ( !$h ) {
2513  unlink( $oldtextName );
2514  unlink( $newtextName );
2515  throw new Exception( __METHOD__ . '(): popen() failed' );
2516  }
2517 
2518  $diff = '';
2519 
2520  do {
2521  $data = fread( $h, 8192 );
2522  if ( strlen( $data ) == 0 ) {
2523  break;
2524  }
2525  $diff .= $data;
2526  } while ( true );
2527 
2528  // Clean up
2529  pclose( $h );
2530  unlink( $oldtextName );
2531  unlink( $newtextName );
2532 
2533  // Kill the --- and +++ lines. They're not useful.
2534  $diff_lines = explode( "\n", $diff );
2535  if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
2536  unset( $diff_lines[0] );
2537  }
2538  if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
2539  unset( $diff_lines[1] );
2540  }
2541 
2542  $diff = implode( "\n", $diff_lines );
2543 
2544  return $diff;
2545 }
2546 
2565 function wfUsePHP( $req_ver ) {
2566  wfDeprecated( __FUNCTION__, '1.30' );
2567  $php_ver = PHP_VERSION;
2568 
2569  if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
2570  throw new MWException( "PHP $req_ver required--this is only $php_ver" );
2571  }
2572 }
2573 
2596 function wfUseMW( $req_ver ) {
2598 
2599  if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
2600  throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
2601  }
2602 }
2603 
2616 function wfBaseName( $path, $suffix = '' ) {
2617  if ( $suffix == '' ) {
2618  $encSuffix = '';
2619  } else {
2620  $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
2621  }
2622 
2623  $matches = [];
2624  if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
2625  return $matches[1];
2626  } else {
2627  return '';
2628  }
2629 }
2630 
2640 function wfRelativePath( $path, $from ) {
2641  // Normalize mixed input on Windows...
2642  $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
2643  $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
2644 
2645  // Trim trailing slashes -- fix for drive root
2646  $path = rtrim( $path, DIRECTORY_SEPARATOR );
2647  $from = rtrim( $from, DIRECTORY_SEPARATOR );
2648 
2649  $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
2650  $against = explode( DIRECTORY_SEPARATOR, $from );
2651 
2652  if ( $pieces[0] !== $against[0] ) {
2653  // Non-matching Windows drive letters?
2654  // Return a full path.
2655  return $path;
2656  }
2657 
2658  // Trim off common prefix
2659  while ( count( $pieces ) && count( $against )
2660  && $pieces[0] == $against[0] ) {
2661  array_shift( $pieces );
2662  array_shift( $against );
2663  }
2664 
2665  // relative dots to bump us to the parent
2666  while ( count( $against ) ) {
2667  array_unshift( $pieces, '..' );
2668  array_shift( $against );
2669  }
2670 
2671  array_push( $pieces, wfBaseName( $path ) );
2672 
2673  return implode( DIRECTORY_SEPARATOR, $pieces );
2674 }
2675 
2682 function wfResetSessionID() {
2683  wfDeprecated( __FUNCTION__, '1.27' );
2684  $session = SessionManager::getGlobalSession();
2685  $delay = $session->delaySave();
2686 
2687  $session->resetId();
2688 
2689  // Make sure a session is started, since that's what the old
2690  // wfResetSessionID() did.
2691  if ( session_id() !== $session->getId() ) {
2692  wfSetupSession( $session->getId() );
2693  }
2694 
2695  ScopedCallback::consume( $delay );
2696 }
2697 
2707 function wfSetupSession( $sessionId = false ) {
2708  wfDeprecated( __FUNCTION__, '1.27' );
2709 
2710  if ( $sessionId ) {
2711  session_id( $sessionId );
2712  }
2713 
2714  $session = SessionManager::getGlobalSession();
2715  $session->persist();
2716 
2717  if ( session_id() !== $session->getId() ) {
2718  session_id( $session->getId() );
2719  }
2720  Wikimedia\quietCall( 'session_start' );
2721 }
2722 
2729 function wfGetPrecompiledData( $name ) {
2730  global $IP;
2731 
2732  $file = "$IP/serialized/$name";
2733  if ( file_exists( $file ) ) {
2734  $blob = file_get_contents( $file );
2735  if ( $blob ) {
2736  return unserialize( $blob );
2737  }
2738  }
2739  return false;
2740 }
2741 
2749 function wfMemcKey( /*...*/ ) {
2750  return call_user_func_array(
2751  [ ObjectCache::getLocalClusterInstance(), 'makeKey' ],
2752  func_get_args()
2753  );
2754 }
2755 
2766 function wfForeignMemcKey( $db, $prefix /*...*/ ) {
2767  $args = array_slice( func_get_args(), 2 );
2768  $keyspace = $prefix ? "$db-$prefix" : $db;
2769  return call_user_func_array(
2770  [ ObjectCache::getLocalClusterInstance(), 'makeKeyInternal' ],
2771  [ $keyspace, $args ]
2772  );
2773 }
2774 
2787 function wfGlobalCacheKey( /*...*/ ) {
2788  return call_user_func_array(
2789  [ ObjectCache::getLocalClusterInstance(), 'makeGlobalKey' ],
2790  func_get_args()
2791  );
2792 }
2793 
2800 function wfWikiID() {
2802  if ( $wgDBprefix ) {
2803  return "$wgDBname-$wgDBprefix";
2804  } else {
2805  return $wgDBname;
2806  }
2807 }
2808 
2816 function wfSplitWikiID( $wiki ) {
2817  $bits = explode( '-', $wiki, 2 );
2818  if ( count( $bits ) < 2 ) {
2819  $bits[] = '';
2820  }
2821  return $bits;
2822 }
2823 
2849 function wfGetDB( $db, $groups = [], $wiki = false ) {
2850  return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
2851 }
2852 
2862 function wfGetLB( $wiki = false ) {
2863  if ( $wiki === false ) {
2864  return MediaWikiServices::getInstance()->getDBLoadBalancer();
2865  } else {
2866  $factory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2867  return $factory->getMainLB( $wiki );
2868  }
2869 }
2870 
2878 function wfGetLBFactory() {
2879  return MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
2880 }
2881 
2890 function wfFindFile( $title, $options = [] ) {
2891  return RepoGroup::singleton()->findFile( $title, $options );
2892 }
2893 
2901 function wfLocalFile( $title ) {
2902  return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
2903 }
2904 
2911 function wfQueriesMustScale() {
2913  return $wgMiserMode
2914  || ( SiteStats::pages() > 100000
2915  && SiteStats::edits() > 1000000
2916  && SiteStats::users() > 10000 );
2917 }
2918 
2927 function wfScript( $script = 'index' ) {
2929  if ( $script === 'index' ) {
2930  return $wgScript;
2931  } elseif ( $script === 'load' ) {
2932  return $wgLoadScript;
2933  } else {
2934  return "{$wgScriptPath}/{$script}.php";
2935  }
2936 }
2937 
2943 function wfGetScriptUrl() {
2944  if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
2945  /* as it was called, minus the query string.
2946  *
2947  * Some sites use Apache rewrite rules to handle subdomains,
2948  * and have PHP set up in a weird way that causes PHP_SELF
2949  * to contain the rewritten URL instead of the one that the
2950  * outside world sees.
2951  *
2952  * If in this mode, use SCRIPT_URL instead, which mod_rewrite
2953  * provides containing the "before" URL.
2954  */
2955  return $_SERVER['SCRIPT_NAME'];
2956  } else {
2957  return $_SERVER['URL'];
2958  }
2959 }
2960 
2968 function wfBoolToStr( $value ) {
2969  return $value ? 'true' : 'false';
2970 }
2971 
2977 function wfGetNull() {
2978  return wfIsWindows() ? 'NUL' : '/dev/null';
2979 }
2980 
3003 function wfWaitForSlaves(
3004  $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
3005 ) {
3006  if ( $timeout === null ) {
3007  $timeout = wfIsCLI() ? 60 : 10;
3008  }
3009 
3010  if ( $cluster === '*' ) {
3011  $cluster = false;
3012  $wiki = false;
3013  } elseif ( $wiki === false ) {
3014  $wiki = wfWikiID();
3015  }
3016 
3017  try {
3018  $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
3019  $lbFactory->waitForReplication( [
3020  'wiki' => $wiki,
3021  'cluster' => $cluster,
3022  'timeout' => $timeout,
3023  // B/C: first argument used to be "max seconds of lag"; ignore such values
3024  'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
3025  ] );
3026  } catch ( DBReplicationWaitError $e ) {
3027  return false;
3028  }
3029 
3030  return true;
3031 }
3032 
3042 function wfCountDown( $seconds ) {
3043  wfDeprecated( __FUNCTION__, '1.31' );
3044  for ( $i = $seconds; $i >= 0; $i-- ) {
3045  if ( $i != $seconds ) {
3046  echo str_repeat( "\x08", strlen( $i + 1 ) );
3047  }
3048  echo $i;
3049  flush();
3050  if ( $i ) {
3051  sleep( 1 );
3052  }
3053  }
3054  echo "\n";
3055 }
3056 
3065 function wfStripIllegalFilenameChars( $name ) {
3067  $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
3068  $name = preg_replace(
3069  "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
3070  '-',
3071  $name
3072  );
3073  // $wgIllegalFileChars may not include '/' and '\', so we still need to do this
3074  $name = wfBaseName( $name );
3075  return $name;
3076 }
3077 
3083 function wfMemoryLimit() {
3085  $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
3086  if ( $memlimit != -1 ) {
3087  $conflimit = wfShorthandToInteger( $wgMemoryLimit );
3088  if ( $conflimit == -1 ) {
3089  wfDebug( "Removing PHP's memory limit\n" );
3090  Wikimedia\suppressWarnings();
3091  ini_set( 'memory_limit', $conflimit );
3092  Wikimedia\restoreWarnings();
3093  return $conflimit;
3094  } elseif ( $conflimit > $memlimit ) {
3095  wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
3096  Wikimedia\suppressWarnings();
3097  ini_set( 'memory_limit', $conflimit );
3098  Wikimedia\restoreWarnings();
3099  return $conflimit;
3100  }
3101  }
3102  return $memlimit;
3103 }
3104 
3111 function wfTransactionalTimeLimit() {
3113 
3114  $timeLimit = ini_get( 'max_execution_time' );
3115  // Note that CLI scripts use 0
3116  if ( $timeLimit > 0 && $wgTransactionalTimeLimit > $timeLimit ) {
3117  set_time_limit( $wgTransactionalTimeLimit );
3118  }
3119 
3120  ignore_user_abort( true ); // ignore client disconnects
3121 
3122  return $timeLimit;
3123 }
3124 
3132 function wfShorthandToInteger( $string = '', $default = -1 ) {
3133  $string = trim( $string );
3134  if ( $string === '' ) {
3135  return $default;
3136  }
3137  $last = $string[strlen( $string ) - 1];
3138  $val = intval( $string );
3139  switch ( $last ) {
3140  case 'g':
3141  case 'G':
3142  $val *= 1024;
3143  // break intentionally missing
3144  case 'm':
3145  case 'M':
3146  $val *= 1024;
3147  // break intentionally missing
3148  case 'k':
3149  case 'K':
3150  $val *= 1024;
3151  }
3152 
3153  return $val;
3154 }
3155 
3166 function wfBCP47( $code ) {
3167  return LanguageCode::bcp47( $code );
3168 }
3169 
3176 function wfGetCache( $cacheType ) {
3177  return ObjectCache::getInstance( $cacheType );
3178 }
3179 
3185 function wfGetMainCache() {
3187  return ObjectCache::getInstance( $wgMainCacheType );
3188 }
3189 
3195 function wfGetMessageCacheStorage() {
3197  return ObjectCache::getInstance( $wgMessageCacheType );
3198 }
3199 
3206 function wfGetParserCacheStorage() {
3208  return ObjectCache::getInstance( $wgParserCacheType );
3209 }
3210 
3221 function wfRunHooks( $event, array $args = [], $deprecatedVersion = null ) {
3222  wfDeprecated( __METHOD__, '1.25' );
3223  return Hooks::run( $event, $args, $deprecatedVersion );
3224 }
3225 
3240 function wfUnpack( $format, $data, $length = false ) {
3241  if ( $length !== false ) {
3242  $realLen = strlen( $data );
3243  if ( $realLen < $length ) {
3244  throw new MWException( "Tried to use wfUnpack on a "
3245  . "string of length $realLen, but needed one "
3246  . "of at least length $length."
3247  );
3248  }
3249  }
3250 
3251  Wikimedia\suppressWarnings();
3252  $result = unpack( $format, $data );
3253  Wikimedia\restoreWarnings();
3254 
3255  if ( $result === false ) {
3256  // If it cannot extract the packed data.
3257  throw new MWException( "unpack could not unpack binary data" );
3258  }
3259  return $result;
3260 }
3261 
3276 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
3277  # Handle redirects; callers almost always hit wfFindFile() anyway,
3278  # so just use that method because it has a fast process cache.
3279  $file = wfFindFile( $name ); // get the final name
3280  $name = $file ? $file->getTitle()->getDBkey() : $name;
3281 
3282  # Run the extension hook
3283  $bad = false;
3284  if ( !Hooks::run( 'BadImage', [ $name, &$bad ] ) ) {
3285  return (bool)$bad;
3286  }
3287 
3289  $key = $cache->makeKey(
3290  'bad-image-list', ( $blacklist === null ) ? 'default' : md5( $blacklist )
3291  );
3292  $badImages = $cache->get( $key );
3293 
3294  if ( $badImages === false ) { // cache miss
3295  if ( $blacklist === null ) {
3296  $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
3297  }
3298  # Build the list now
3299  $badImages = [];
3300  $lines = explode( "\n", $blacklist );
3301  foreach ( $lines as $line ) {
3302  # List items only
3303  if ( substr( $line, 0, 1 ) !== '*' ) {
3304  continue;
3305  }
3306 
3307  # Find all links
3308  $m = [];
3309  if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
3310  continue;
3311  }
3312 
3313  $exceptions = [];
3314  $imageDBkey = false;
3315  foreach ( $m[1] as $i => $titleText ) {
3316  $title = Title::newFromText( $titleText );
3317  if ( !is_null( $title ) ) {
3318  if ( $i == 0 ) {
3319  $imageDBkey = $title->getDBkey();
3320  } else {
3321  $exceptions[$title->getPrefixedDBkey()] = true;
3322  }
3323  }
3324  }
3325 
3326  if ( $imageDBkey !== false ) {
3327  $badImages[$imageDBkey] = $exceptions;
3328  }
3329  }
3330  $cache->set( $key, $badImages, 60 );
3331  }
3332 
3333  $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
3334  $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
3335 
3336  return $bad;
3337 }
3338 
3346 function wfCanIPUseHTTPS( $ip ) {
3347  $canDo = true;
3348  Hooks::run( 'CanIPUseHTTPS', [ $ip, &$canDo ] );
3349  return !!$canDo;
3350 }
3351 
3359 function wfIsInfinity( $str ) {
3360  // These are hardcoded elsewhere in MediaWiki (e.g. mediawiki.special.block.js).
3361  $infinityValues = [ 'infinite', 'indefinite', 'infinity', 'never' ];
3362  return in_array( $str, $infinityValues );
3363 }
3364 
3379 function wfThumbIsStandard( File $file, array $params ) {
3381 
3382  $multipliers = [ 1 ];
3383  if ( $wgResponsiveImages ) {
3384  // These available sizes are hardcoded currently elsewhere in MediaWiki.
3385  // @see Linker::processResponsiveImages
3386  $multipliers[] = 1.5;
3387  $multipliers[] = 2;
3388  }
3389 
3390  $handler = $file->getHandler();
3391  if ( !$handler || !isset( $params['width'] ) ) {
3392  return false;
3393  }
3394 
3395  $basicParams = [];
3396  if ( isset( $params['page'] ) ) {
3397  $basicParams['page'] = $params['page'];
3398  }
3399 
3400  $thumbLimits = [];
3401  $imageLimits = [];
3402  // Expand limits to account for multipliers
3403  foreach ( $multipliers as $multiplier ) {
3404  $thumbLimits = array_merge( $thumbLimits, array_map(
3405  function ( $width ) use ( $multiplier ) {
3406  return round( $width * $multiplier );
3407  }, $wgThumbLimits )
3408  );
3409  $imageLimits = array_merge( $imageLimits, array_map(
3410  function ( $pair ) use ( $multiplier ) {
3411  return [
3412  round( $pair[0] * $multiplier ),
3413  round( $pair[1] * $multiplier ),
3414  ];
3415  }, $wgImageLimits )
3416  );
3417  }
3418 
3419  // Check if the width matches one of $wgThumbLimits
3420  if ( in_array( $params['width'], $thumbLimits ) ) {
3421  $normalParams = $basicParams + [ 'width' => $params['width'] ];
3422  // Append any default values to the map (e.g. "lossy", "lossless", ...)
3423  $handler->normaliseParams( $file, $normalParams );
3424  } else {
3425  // If not, then check if the width matchs one of $wgImageLimits
3426  $match = false;
3427  foreach ( $imageLimits as $pair ) {
3428  $normalParams = $basicParams + [ 'width' => $pair[0], 'height' => $pair[1] ];
3429  // Decide whether the thumbnail should be scaled on width or height.
3430  // Also append any default values to the map (e.g. "lossy", "lossless", ...)
3431  $handler->normaliseParams( $file, $normalParams );
3432  // Check if this standard thumbnail size maps to the given width
3433  if ( $normalParams['width'] == $params['width'] ) {
3434  $match = true;
3435  break;
3436  }
3437  }
3438  if ( !$match ) {
3439  return false; // not standard for description pages
3440  }
3441  }
3442 
3443  // Check that the given values for non-page, non-width, params are just defaults
3444  foreach ( $params as $key => $value ) {
3445  if ( !isset( $normalParams[$key] ) || $normalParams[$key] != $value ) {
3446  return false;
3447  }
3448  }
3449 
3450  return true;
3451 }
3452 
3465 function wfArrayPlus2d( array $baseArray, array $newValues ) {
3466  // First merge items that are in both arrays
3467  foreach ( $baseArray as $name => &$groupVal ) {
3468  if ( isset( $newValues[$name] ) ) {
3469  $groupVal += $newValues[$name];
3470  }
3471  }
3472  // Now add items that didn't exist yet
3473  $baseArray += $newValues;
3474 
3475  return $baseArray;
3476 }
3477 
3486 function wfGetRusage() {
3487  if ( !function_exists( 'getrusage' ) ) {
3488  return false;
3489  } elseif ( defined( 'HHVM_VERSION' ) && PHP_OS === 'Linux' ) {
3490  return getrusage( 2 /* RUSAGE_THREAD */ );
3491  } else {
3492  return getrusage( 0 /* RUSAGE_SELF */ );
3493  }
3494 }
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:790
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:1623
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:2610
$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.
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:41
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:2019
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:733
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:2195
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:2252
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.
global $wgDebugRawPage
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.
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:2017
wfGlobalCacheKey()
Make a cache key with database-agnostic prefix.
if($line===false) $args
Definition: cdb.php:64
static getUsableTempDirectory()
Definition: TempFSFile.php:85
global $wgDebugLogGroups
$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:94
$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.
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:2019
$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:941
wfIsCLI()
Check if we are running from the commandline.
wfLoadSkins(array $skins)
Load multiple skins at once.
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)
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
global $wgDebugTimestamps
wfUsePHP($req_ver)
This function works like "use VERSION" in Perl, the program will die with a backtrace if the current ...
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
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:61
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:790
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:935
wfGetMainCache()
Get the main cache object.
static pages()
Definition: SiteStats.php:112
wfArrayToCgi($array1, $array2=null, $prefix= '')
This function takes one or two arrays as input, and returns a CGI-style string, e.g.
global $wgCommandLineMode
$wgDBprefix
Table name prefix.
wfRestoreWarnings()
wfReportTime($nonce=null)
Returns a script tag that stores the amount of time it took MediaWiki to handle the request in millis...
$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:2636
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.
$wgServer
URL of the server.
wfMemcKey()
Make a cache key for the local wiki.
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. 'ContentSecurityPolicyDefaultSource':Modify the allowed CSP load sources.This affects all directives except for the script directive.If you want to add a script source, see ContentSecurityPolicyScriptSource hook.&$defaultSrc:Array of Content-Security-Policy allowed sources $policyConfig:Current configuration for the Content-Security-Policy header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyDirectives':Modify the content security policy directives.Use this only if ContentSecurityPolicyDefaultSource and ContentSecurityPolicyScriptSource do not meet your needs.&$directives:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header 'ContentSecurityPolicyScriptSource':Modify the allowed CSP script sources.Note that you also have to use ContentSecurityPolicyDefaultSource if you want non-script sources to be loaded from whatever you add.&$scriptSrc:Array of CSP directives $policyConfig:Current configuration for the CSP header $mode:ContentSecurityPolicy::REPORT_ONLY_MODE or ContentSecurityPolicy::FULL_MODE depending on type of header '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). 'DeleteUnknownPreferences':Called by the cleanupPreferences.php maintenance script to build a WHERE clause with which to delete preferences that are not known about.This hook is used by extensions that have dynamically-named preferences that should not be deleted in the usual cleanup process.For example, the Gadgets extension creates preferences prefixed with 'gadget-', and so anything with that prefix is excluded from the deletion.&where:An array that will be passed as the $cond parameter to IDatabase::select() to determine what will be deleted from the user_properties table.$db:The IDatabase object, useful for accessing $db->buildLike() etc. '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:1287
$wgOut
Definition: Setup.php:900
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.
static makeVariablesScript($data, $nonce=null)
Definition: Skin.php:407
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:790
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.
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:2636
wfFindFile($title, $options=[])
Find a file.
static users()
Definition: SiteStats.php:121
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...