21use Wikimedia\WrappedString;
22use Wikimedia\WrappedStringList;
41 protected static $log = [];
80 public static function setup() {
107 public static function init() {
108 self::$enabled =
true;
117 self::$enabled =
false;
128 if ( self::$enabled ) {
139 public static function log( $str ) {
140 if ( !self::$enabled ) {
143 if ( !is_string( $str ) ) {
144 $str = print_r( $str,
true );
147 'msg' => htmlspecialchars( $str ),
168 self::$deprecationWarnings = [];
182 public static function warning( $msg, $callerOffset = 1, $level = E_USER_NOTICE,
$log =
'auto' ) {
189 if (
$log ===
'debug' ) {
193 $callerDescription = self::getCallerDescription( $callerOffset );
196 self::formatCallerDescription( $msg, $callerDescription ),
215 public static function deprecated( $function, $version =
false,
216 $component =
false, $callerOffset = 2
219 $component = $component ?:
'MediaWiki';
220 $msg =
"Use of $function was deprecated in $component $version.";
222 $msg =
"Use of $function is deprecated.";
250 $component =
false, $callerOffset = 2
252 $reflectionMethod =
new ReflectionMethod( $instance, $method );
253 $declaringClass = $reflectionMethod->getDeclaringClass()->getName();
255 if ( $declaringClass === $class ) {
261 $component = $component ?:
'MediaWiki';
262 $msg =
"$declaringClass overrides $method which was deprecated in $component $version.";
298 $component =
false, $callerOffset = 2
300 if ( $callerOffset ===
false ) {
304 $callerDescription = self::getCallerDescription( $callerOffset );
305 $callerFunc = $callerDescription[
'func'];
306 $rawMsg = self::formatCallerDescription( $msg, $callerDescription );
312 if ( isset( self::$deprecationWarnings[$msg][$callerFunc] ) ) {
314 } elseif ( isset( self::$deprecationWarnings[$msg] ) ) {
315 if ( self::$enabled ) {
322 self::$deprecationWarnings[$msg][$callerFunc] =
true;
327 $component = $component ?:
'MediaWiki';
329 # Strip -* off the end of $version so that branches can use the
330 # format #.##-branchname to avoid issues if the branch is merged into
331 # a version of MediaWiki later than what it was branched from
332 $comparableVersion = preg_replace(
'/-.*$/',
'', $version );
334 # If the comparableVersion is larger than our release limit then
335 # skip the warning message for the deprecation
362 foreach ( self::$deprecationFilters as $filter => $callback ) {
363 if ( preg_match( $filter, $msg ) ) {
364 if ( is_callable( $callback ) ) {
372 trigger_error( $msg, E_USER_DEPRECATED );
384 string $regex, ?callable $callback =
null
386 if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
387 throw new LogicException( __METHOD__ .
' can only be used in tests' );
389 self::$deprecationFilters[$regex] = $callback;
396 self::$deprecationFilters = [];
406 private static function getCallerDescription( $callerOffset ) {
409 if ( isset( $callers[$callerOffset] ) ) {
410 $callerfile = $callers[$callerOffset];
411 if ( isset( $callerfile[
'file'] ) && isset( $callerfile[
'line'] ) ) {
412 $file = $callerfile[
'file'] .
' at line ' . $callerfile[
'line'];
414 $file =
'(internal function)';
417 $file =
'(unknown location)';
420 if ( isset( $callers[$callerOffset + 1] ) ) {
421 $callerfunc = $callers[$callerOffset + 1];
423 if ( isset( $callerfunc[
'class'] ) ) {
424 $func .= $callerfunc[
'class'] .
'::';
426 if ( isset( $callerfunc[
'function'] ) ) {
427 $func .= $callerfunc[
'function'];
433 return [
'file' => $file,
'func' => $func ];
443 private static function formatCallerDescription( $msg, $caller ) {
445 return $msg .
' [Called from ' . $caller[
'func'] .
' in ' . $caller[
'file'] .
']';
463 preg_match(
'/(.*) \[Called from ([^ ]+) in ([^ ]+) at line (\d+)\]$/', $msg, $match );
466 'message' =>
"{$match[1]} [Called from {$match[2]}]",
469 'line' => (int)$match[4],
484 private static function sendMessage( $msg, $group, $level ) {
485 if ( $level !==
false ) {
486 trigger_error( $msg, $level );
502 public static function debugMsg( $str, $context = [] ) {
508 if ( isset( $context[
'prefix'] ) ) {
509 $prefix = $context[
'prefix'];
510 } elseif ( isset( $context[
'channel'] ) && $context[
'channel'] !==
'wfDebug' ) {
511 $prefix =
"[{$context['channel']}] ";
513 if ( isset( $context[
'seconds_elapsed'] ) && isset( $context[
'memory_used'] ) ) {
514 $prefix .=
"{$context['seconds_elapsed']} {$context['memory_used']} ";
517 $str = $prefix . $str;
519 $str = rtrim( UtfNormal\Validator::cleanUp( $str ) );
520 self::$debug[] = $str;
521 if ( isset( $context[
'channel'] ) && $context[
'channel'] ===
'error' ) {
522 $message = isset( $context[
'exception'] )
523 ? $context[
'exception']->getMessage()
525 $real = self::parseCallerDescription( $message );
528 $message = $real[
'message'];
529 $caller = $real[
'func'];
531 $trace = isset( $context[
'exception'] ) ? $context[
'exception']->getTrace() : [];
532 if ( ( $trace[5][
'function'] ??
null ) ===
'wfDeprecated' ) {
535 } elseif ( ( $trace[1][
'function'] ??
null ) ===
'trigger_error' ) {
542 $frame = $trace[$offset] ?? $trace[0];
543 $caller = ( isset( $frame[
'class'] ) ? $frame[
'class'] .
'::' :
'' )
544 . $frame[
'function'];
548 'msg' => htmlspecialchars( $message ),
566 public static function query( $sql, $function, $runTime, $dbhost ) {
567 if ( !self::$enabled ) {
575 [\xC0-\xC1] # Invalid UTF-8 Bytes
576 | [\xF5-\xFF] # Invalid UTF-8 Bytes
577 | \xE0[\x80-\x9F] # Overlong encoding of prior code point
578 | \xF0[\x80-\x8F] # Overlong encoding of prior code point
579 | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
580 | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
581 | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
582 | (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
583 | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]
584 | [\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
585 | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
586 | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
587 | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
594 $sql = UtfNormal\Validator::cleanUp( $sql );
597 'sql' =>
"$dbhost: $sql",
598 'function' => $function,
612 $files = get_included_files();
614 foreach ( $files as $file ) {
616 $size = @filesize( $file );
617 if ( $size ===
false ) {
625 'size' => $context->
getLanguage()->formatSize( $size ),
644 if ( self::$enabled ) {
645 self::log(
'MWDebug output complete' );
646 $debugInfo = self::getDebugInfo( $context );
652 . FormatJson::encode( [
'debugInfo' => $debugInfo ] )
658 $html[] =
'<!-- Debug output:';
659 foreach ( self::$debug as $line ) {
660 $html[] = htmlspecialchars( $line, ENT_NOQUOTES );
665 return WrappedString::join(
"\n", $html );
681 $html[] = Html::openElement(
'div', [
'id' =>
'mw-html-debug-log' ] );
682 $html[] =
"<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">";
684 foreach ( self::$debug as $line ) {
685 $display = nl2br( htmlspecialchars( trim( $line ) ) );
687 $html[] =
"<li><code>$display</code></li>";
693 return WrappedString::join(
"\n", $html );
703 if ( !self::$enabled ) {
709 $obContents = ob_get_contents();
711 $obContentArray = explode(
'<br />', $obContents );
712 foreach ( $obContentArray as $obContent ) {
713 if ( trim( $obContent ) ) {
714 self::debugMsg( Sanitizer::stripAllTags( $obContent ) );
719 self::log(
'MWDebug output complete' );
720 $debugInfo = self::getDebugInfo( $context );
727 $result->
addValue(
null,
'debuginfo', $debugInfo );
737 if ( !self::$enabled ) {
743 $branch = GitInfo::currentBranch();
744 if ( GitInfo::isSHA1( $branch ) ) {
752 'phpEngine' =>
'PHP',
753 'phpVersion' => PHP_VERSION,
754 'gitRevision' => GitInfo::headSHA1(),
755 'gitBranch' => $branch,
756 'gitViewUrl' => GitInfo::headViewUrl(),
757 'time' => $request->getElapsedTime(),
759 'debugLog' => self::$debug,
760 'queries' => self::$query,
762 'method' => $request->getMethod(),
763 'url' => $request->getRequestURL(),
764 'headers' => $request->getAllHeaders(),
765 'params' => $request->getValues(),
767 'memory' => $context->
getLanguage()->formatSize( memory_get_usage() ),
768 'memoryPeak' => $context->
getLanguage()->formatSize( memory_get_peak_usage() ),
769 'includes' => self::getFilesIncluded( $context ),
775class_alias( MWDebug::class,
'MWDebug' );
const MW_VERSION
The running version of MediaWiki.
wfGetCaller( $level=2)
Get the name of the function which called this function wfGetCaller( 1 ) is the function with the wfG...
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
wfDebugBacktrace( $limit=0)
Safety wrapper for debug_backtrace().
if(!defined('MW_SETUP_CALLBACK'))
This is one of the Core classes and should be read at least once by any new developers.
addModules( $modules)
Load one or more ResourceLoader modules on this page.
$wgUseCdn
Config variable stub for the UseCdn setting, for use by phpdoc and IDEs.
$wgDeprecationReleaseLimit
Config variable stub for the DeprecationReleaseLimit setting, for use by phpdoc and IDEs.
$wgDebugComments
Config variable stub for the DebugComments setting, for use by phpdoc and IDEs.
$wgShowDebug
Config variable stub for the ShowDebug setting, for use by phpdoc and IDEs.
$wgDebugToolbar
Config variable stub for the DebugToolbar setting, for use by phpdoc and IDEs.
$wgDevelopmentWarnings
Config variable stub for the DevelopmentWarnings setting, for use by phpdoc and IDEs.
$wgUseFileCache
Config variable stub for the UseFileCache setting, for use by phpdoc and IDEs.
Interface for objects which can provide a MediaWiki context on request.