35use Wikimedia\WrappedString;
36use Wikimedia\WrappedStringList;
55 protected static $log = [];
94 public static function setup() {
121 public static function init() {
122 self::$enabled =
true;
131 self::$enabled =
false;
142 if ( self::$enabled ) {
153 public static function log( $str ) {
154 if ( !self::$enabled ) {
157 if ( !is_string( $str ) ) {
158 $str = print_r( $str,
true );
161 'msg' => htmlspecialchars( $str ),
182 self::$deprecationWarnings = [];
196 public static function warning( $msg, $callerOffset = 1, $level = E_USER_NOTICE,
$log =
'auto' ) {
203 if (
$log ===
'debug' ) {
207 $callerDescription = self::getCallerDescription( $callerOffset );
210 self::formatCallerDescription( $msg, $callerDescription ),
229 public static function deprecated( $function, $version =
false,
230 $component =
false, $callerOffset = 2
233 $component = $component ?:
'MediaWiki';
234 $msg =
"Use of $function was deprecated in $component $version.";
236 $msg =
"Use of $function is deprecated.";
264 $component =
false, $callerOffset = 2
266 $reflectionMethod =
new ReflectionMethod( $instance, $method );
267 $declaringClass = $reflectionMethod->getDeclaringClass()->getName();
269 if ( $declaringClass === $class ) {
275 $component = $component ?:
'MediaWiki';
276 $msg =
"$declaringClass overrides $method which was deprecated in $component $version.";
312 $component =
false, $callerOffset = 2
314 if ( $callerOffset ===
false ) {
318 $callerDescription = self::getCallerDescription( $callerOffset );
319 $callerFunc = $callerDescription[
'func'];
320 $rawMsg = self::formatCallerDescription( $msg, $callerDescription );
326 if ( isset( self::$deprecationWarnings[$msg][$callerFunc] ) ) {
328 } elseif ( isset( self::$deprecationWarnings[$msg] ) ) {
329 if ( self::$enabled ) {
336 self::$deprecationWarnings[$msg][$callerFunc] =
true;
341 $component = $component ?:
'MediaWiki';
343 # Strip -* off the end of $version so that branches can use the
344 # format #.##-branchname to avoid issues if the branch is merged into
345 # a version of MediaWiki later than what it was branched from
346 $comparableVersion = preg_replace(
'/-.*$/',
'', $version );
348 # If the comparableVersion is larger than our release limit then
349 # skip the warning message for the deprecation
376 foreach ( self::$deprecationFilters as $filter => $callback ) {
377 if ( preg_match( $filter, $msg ) ) {
378 if ( is_callable( $callback ) ) {
386 trigger_error( $msg, E_USER_DEPRECATED );
398 string $regex, ?callable $callback =
null
400 if ( !defined(
'MW_PHPUNIT_TEST' ) && !defined(
'MW_PARSER_TEST' ) ) {
401 throw new LogicException( __METHOD__ .
' can only be used in tests' );
403 self::$deprecationFilters[$regex] = $callback;
410 self::$deprecationFilters = [];
420 private static function getCallerDescription( $callerOffset ) {
423 if ( isset( $callers[$callerOffset] ) ) {
424 $callerfile = $callers[$callerOffset];
425 if ( isset( $callerfile[
'file'] ) && isset( $callerfile[
'line'] ) ) {
426 $file = $callerfile[
'file'] .
' at line ' . $callerfile[
'line'];
428 $file =
'(internal function)';
431 $file =
'(unknown location)';
434 if ( isset( $callers[$callerOffset + 1] ) ) {
435 $callerfunc = $callers[$callerOffset + 1];
437 if ( isset( $callerfunc[
'class'] ) ) {
438 $func .= $callerfunc[
'class'] .
'::';
440 if ( isset( $callerfunc[
'function'] ) ) {
441 $func .= $callerfunc[
'function'];
447 return [
'file' => $file,
'func' => $func ];
457 private static function formatCallerDescription( $msg, $caller ) {
459 return $msg .
' [Called from ' . $caller[
'func'] .
' in ' . $caller[
'file'] .
']';
476 preg_match(
'/(.*) \[Called from ([^ ]+) in ([^ ]+) at line (\d+)\]$/', $msg, $match );
479 'message' =>
"{$match[1]} [Called from {$match[2]}]",
497 private static function sendMessage( $msg, $group, $level ) {
498 if ( $level !==
false ) {
499 trigger_error( $msg, $level );
515 public static function debugMsg( $str, $context = [] ) {
521 if ( isset( $context[
'prefix'] ) ) {
522 $prefix = $context[
'prefix'];
523 } elseif ( isset( $context[
'channel'] ) && $context[
'channel'] !==
'wfDebug' ) {
524 $prefix =
"[{$context['channel']}] ";
526 if ( isset( $context[
'seconds_elapsed'] ) && isset( $context[
'memory_used'] ) ) {
527 $prefix .=
"{$context['seconds_elapsed']} {$context['memory_used']} ";
529 $str = LegacyLogger::interpolate( $str, $context );
530 $str = $prefix . $str;
532 $str = rtrim( UtfNormal\Validator::cleanUp( $str ) );
533 self::$debug[] = $str;
534 if ( isset( $context[
'channel'] ) && $context[
'channel'] ===
'error' ) {
535 $message = isset( $context[
'exception'] )
536 ? $context[
'exception']->getMessage()
538 $real = self::parseCallerDescription( $message );
541 $message = $real[
'message'];
542 $caller = $real[
'func'];
544 $trace = isset( $context[
'exception'] ) ? $context[
'exception']->getTrace() : [];
545 if ( ( $trace[5][
'function'] ??
null ) ===
'wfDeprecated' ) {
548 } elseif ( ( $trace[1][
'function'] ??
null ) ===
'trigger_error' ) {
555 $frame = $trace[$offset] ?? $trace[0];
556 $caller = ( isset( $frame[
'class'] ) ? $frame[
'class'] .
'::' :
'' )
557 . $frame[
'function'];
561 'msg' => htmlspecialchars( $message ),
579 public static function query( $sql, $function, $runTime, $dbhost ) {
580 if ( !self::$enabled ) {
588 [\xC0-\xC1] # Invalid UTF-8 Bytes
589 | [\xF5-\xFF] # Invalid UTF-8 Bytes
590 | \xE0[\x80-\x9F] # Overlong encoding of prior code point
591 | \xF0[\x80-\x8F] # Overlong encoding of prior code point
592 | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start
593 | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start
594 | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start
595 | (?<=[\x0-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle
596 | (?<![\xC2-\xDF]|[\xE0-\xEF]|[\xE0-\xEF][\x80-\xBF]|[\xF0-\xF4]
597 | [\xF0-\xF4][\x80-\xBF]|[\xF0-\xF4][\x80-\xBF]{2})[\x80-\xBF] # Overlong Sequence
598 | (?<=[\xE0-\xEF])[\x80-\xBF](?![\x80-\xBF]) # Short 3 byte sequence
599 | (?<=[\xF0-\xF4])[\x80-\xBF](?![\x80-\xBF]{2}) # Short 4 byte sequence
600 | (?<=[\xF0-\xF4][\x80-\xBF])[\x80-\xBF](?![\x80-\xBF]) # Short 4 byte sequence (2)
607 $sql = UtfNormal\Validator::cleanUp( $sql );
610 'sql' =>
"$dbhost: $sql",
611 'function' => $function,
625 $files = get_included_files();
627 foreach ( $files as $file ) {
629 $size = @filesize( $file );
630 if ( $size ===
false ) {
638 'size' => $context->
getLanguage()->formatSize( $size ),
657 if ( self::$enabled ) {
658 self::log(
'MWDebug output complete' );
659 $debugInfo = self::getDebugInfo( $context );
665 . FormatJson::encode( [
'debugInfo' => $debugInfo ] )
671 $html[] =
'<!-- Debug output:';
672 foreach ( self::$debug as $line ) {
673 $html[] = htmlspecialchars( $line, ENT_NOQUOTES );
678 return WrappedString::join(
"\n", $html );
694 $html[] = Html::openElement(
'div', [
'id' =>
'mw-html-debug-log' ] );
695 $html[] =
"<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">";
697 foreach ( self::$debug as $line ) {
698 $display = nl2br( htmlspecialchars( trim( $line ) ) );
700 $html[] =
"<li><code>$display</code></li>";
706 return WrappedString::join(
"\n", $html );
716 if ( !self::$enabled ) {
722 $obContents = ob_get_contents();
724 $obContentArray = explode(
'<br />', $obContents );
725 foreach ( $obContentArray as $obContent ) {
726 if ( trim( $obContent ) ) {
727 self::debugMsg( Sanitizer::stripAllTags( $obContent ) );
732 self::log(
'MWDebug output complete' );
733 $debugInfo = self::getDebugInfo( $context );
740 $result->
addValue(
null,
'debuginfo', $debugInfo );
750 if ( !self::$enabled ) {
756 $branch = GitInfo::currentBranch();
757 if ( GitInfo::isSHA1( $branch ) ) {
765 'phpEngine' =>
'PHP',
766 'phpVersion' => PHP_VERSION,
767 'gitRevision' => GitInfo::headSHA1(),
768 'gitBranch' => $branch,
769 'gitViewUrl' => GitInfo::headViewUrl(),
770 'time' => $request->getElapsedTime(),
772 'debugLog' => self::$debug,
773 'queries' => self::$query,
775 'method' => $request->getMethod(),
776 'url' => $request->getRequestURL(),
777 'headers' => $request->getAllHeaders(),
778 'params' => $request->getValues(),
780 'memory' => $context->
getLanguage()->formatSize( memory_get_usage() ),
781 'memoryPeak' => $context->
getLanguage()->formatSize( memory_get_peak_usage() ),
782 'includes' => self::getFilesIncluded( $context ),
788class_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.