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