34use Wikimedia\WrappedString;
35use Wikimedia\WrappedStringList;
54 protected static $log = [];
93 public static function setup() {
120 public static function init() {
121 self::$enabled =
true;
130 self::$enabled =
false;
141 if ( self::$enabled ) {
152 public static function log( $str ) {
153 if ( !self::$enabled ) {
156 if ( !is_string( $str ) ) {
157 $str = print_r( $str,
true );
160 'msg' => htmlspecialchars( $str ),
181 self::$deprecationWarnings = [];
195 public static function warning( $msg, $callerOffset = 1, $level = E_USER_NOTICE,
$log =
'auto' ) {
202 if (
$log ===
'debug' ) {
206 $callerDescription = self::getCallerDescription( $callerOffset );
209 self::formatCallerDescription( $msg, $callerDescription ),
228 public static function deprecated( $function, $version =
false,
229 $component =
false, $callerOffset = 2
232 $component = $component ?:
'MediaWiki';
233 $msg =
"Use of $function was deprecated in $component $version.";
235 $msg =
"Use of $function is deprecated.";
263 $component =
false, $callerOffset = 2
265 $reflectionMethod =
new ReflectionMethod( $instance, $method );
266 $declaringClass = $reflectionMethod->getDeclaringClass()->getName();
268 if ( $declaringClass === $class ) {
274 $component = $component ?:
'MediaWiki';
275 $msg =
"$declaringClass overrides $method which was deprecated in $component $version.";
311 $component =
false, $callerOffset = 2
313 if ( $callerOffset ===
false ) {
317 $callerDescription = self::getCallerDescription( $callerOffset );
318 $callerFunc = $callerDescription[
'func'];
319 $rawMsg = self::formatCallerDescription( $msg, $callerDescription );
325 if ( isset( self::$deprecationWarnings[$msg][$callerFunc] ) ) {
327 } elseif ( isset( self::$deprecationWarnings[$msg] ) ) {
328 if ( self::$enabled ) {
335 self::$deprecationWarnings[$msg][$callerFunc] =
true;
340 $component = $component ?:
'MediaWiki';
342 # Strip -* off the end of $version so that branches can use the
343 # format #.##-branchname to avoid issues if the branch is merged into
344 # a version of MediaWiki later than what it was branched from
345 $comparableVersion = preg_replace(
'/-.*$/',
'', $version );
347 # If the comparableVersion is larger than our release limit then
348 # skip the warning message for the deprecation
375 foreach ( self::$deprecationFilters as $filter => $callback ) {
376 if ( preg_match( $filter, $msg ) ) {
377 if ( is_callable( $callback ) ) {
385 trigger_error( $msg, E_USER_DEPRECATED );
397 string $regex, ?callable $callback =
null
399 if ( !defined(
'MW_PHPUNIT_TEST' ) && !defined(
'MW_PARSER_TEST' ) ) {
400 throw new LogicException( __METHOD__ .
' can only be used in tests' );
402 self::$deprecationFilters[$regex] = $callback;
409 self::$deprecationFilters = [];
419 private static function getCallerDescription( $callerOffset ) {
422 if ( isset( $callers[$callerOffset] ) ) {
423 $callerfile = $callers[$callerOffset];
424 if ( isset( $callerfile[
'file'] ) && isset( $callerfile[
'line'] ) ) {
425 $file = $callerfile[
'file'] .
' at line ' . $callerfile[
'line'];
427 $file =
'(internal function)';
430 $file =
'(unknown location)';
433 if ( isset( $callers[$callerOffset + 1] ) ) {
434 $callerfunc = $callers[$callerOffset + 1];
436 if ( isset( $callerfunc[
'class'] ) ) {
437 $func .= $callerfunc[
'class'] .
'::';
439 if ( isset( $callerfunc[
'function'] ) ) {
440 $func .= $callerfunc[
'function'];
446 return [
'file' => $file,
'func' => $func ];
456 private static function formatCallerDescription( $msg, $caller ) {
458 return $msg .
' [Called from ' . $caller[
'func'] .
' in ' . $caller[
'file'] .
']';
475 preg_match(
'/(.*) \[Called from ([^ ]+) in ([^ ]+) at line (\d+)\]$/', $msg, $match );
478 'message' =>
"{$match[1]} [Called from {$match[2]}]",
496 private static function sendMessage( $msg, $group, $level ) {
497 if ( $level !==
false ) {
498 trigger_error( $msg, $level );
514 public static function debugMsg( $str, $context = [] ) {
520 if ( isset( $context[
'prefix'] ) ) {
521 $prefix = $context[
'prefix'];
522 } elseif ( isset( $context[
'channel'] ) && $context[
'channel'] !==
'wfDebug' ) {
523 $prefix =
"[{$context['channel']}] ";
525 if ( isset( $context[
'seconds_elapsed'] ) && isset( $context[
'memory_used'] ) ) {
526 $prefix .=
"{$context['seconds_elapsed']} {$context['memory_used']} ";
528 $str = LegacyLogger::interpolate( $str, $context );
529 $str = $prefix . $str;
531 $str = rtrim( UtfNormal\Validator::cleanUp( $str ) );
532 self::$debug[] = $str;
533 if ( isset( $context[
'channel'] ) && $context[
'channel'] ===
'error' ) {
534 $message = isset( $context[
'exception'] )
535 ? $context[
'exception']->getMessage()
537 $real = self::parseCallerDescription( $message );
540 $message = $real[
'message'];
541 $caller = $real[
'func'];
543 $trace = isset( $context[
'exception'] ) ? $context[
'exception']->getTrace() : [];
544 if ( ( $trace[5][
'function'] ??
null ) ===
'wfDeprecated' ) {
547 } elseif ( ( $trace[1][
'function'] ??
null ) ===
'trigger_error' ) {
554 $frame = $trace[$offset] ?? $trace[0];
555 $caller = ( isset( $frame[
'class'] ) ? $frame[
'class'] .
'::' :
'' )
556 . $frame[
'function'];
560 'msg' => htmlspecialchars( $message ),
578 public static function query( $sql, $function, $runTime, $dbhost ) {
579 if ( !self::$enabled ) {
587 [\xC0-\xC1] # Invalid UTF-8 Bytes
588 | [\xF5-\xFF] # Invalid UTF-8 Bytes
589 | \xE0[\x80-\x9F] # Overlong encoding of prior code point
590 | \xF0[\x80-\x8F] # Overlong encoding of prior code point
591 | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
592 | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
593 | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
594 | (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
595 | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]
596 | [\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
597 | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
598 | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
599 | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
606 $sql = UtfNormal\Validator::cleanUp( $sql );
609 'sql' =>
"$dbhost: $sql",
610 'function' => $function,
624 $files = get_included_files();
626 foreach ( $files as $file ) {
628 $size = @filesize( $file );
629 if ( $size ===
false ) {
637 'size' => $context->
getLanguage()->formatSize( $size ),
656 if ( self::$enabled ) {
657 self::log(
'MWDebug output complete' );
658 $debugInfo = self::getDebugInfo( $context );
668 $html[] =
'<!-- Debug output:';
669 foreach ( self::$debug as $line ) {
670 $html[] = htmlspecialchars( $line, ENT_NOQUOTES );
675 return WrappedString::join(
"\n", $html );
691 $html[] = Html::openElement(
'div', [
'id' =>
'mw-html-debug-log' ] );
692 $html[] =
"<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">";
694 foreach ( self::$debug as $line ) {
695 $display = nl2br( htmlspecialchars( trim( $line ) ) );
697 $html[] =
"<li><code>$display</code></li>";
703 return WrappedString::join(
"\n", $html );
713 if ( !self::$enabled ) {
719 $obContents = ob_get_contents();
721 $obContentArray = explode(
'<br />', $obContents );
722 foreach ( $obContentArray as $obContent ) {
723 if ( trim( $obContent ) ) {
724 self::debugMsg( Sanitizer::stripAllTags( $obContent ) );
729 self::log(
'MWDebug output complete' );
730 $debugInfo = self::getDebugInfo( $context );
737 $result->
addValue(
null,
'debuginfo', $debugInfo );
747 if ( !self::$enabled ) {
753 $branch = GitInfo::currentBranch();
754 if ( GitInfo::isSHA1( $branch ) ) {
762 'phpEngine' =>
'PHP',
763 'phpVersion' => PHP_VERSION,
764 'gitRevision' => GitInfo::headSHA1(),
765 'gitBranch' => $branch,
766 'gitViewUrl' => GitInfo::headViewUrl(),
767 'time' => $request->getElapsedTime(),
769 'debugLog' => self::$debug,
770 'queries' => self::$query,
772 'method' => $request->getMethod(),
773 'url' => $request->getRequestURL(),
774 'headers' => $request->getAllHeaders(),
775 'params' => $request->getValues(),
777 'memory' => $context->
getLanguage()->formatSize( memory_get_usage() ),
778 'memoryPeak' => $context->
getLanguage()->formatSize( memory_get_peak_usage() ),
779 'includes' => self::getFilesIncluded( $context ),
785class_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 class represents the result of the API operations.
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
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.