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