21use Wikimedia\WrappedString;
22use Wikimedia\WrappedStringList;
85// Easy to forget to falsify $wgDebugToolbar for static caches. 86// If file cache or CDN cache is on, just disable this (DWIMD). 89// Keep MWDebug off on CLI. This prevents MWDebug from eating up 90// all the memory for logging SQL queries in maintenance scripts. 108 self::$enabled =
true;
117 self::$enabled =
false;
128if ( self::$enabled ) {
139publicstaticfunctionlog( $str ) {
140if ( !self::$enabled ) {
143if ( !is_string( $str ) ) {
144 $str = print_r( $str,
true );
147'msg' => htmlspecialchars( $str ),
168 self::$deprecationWarnings = [];
182publicstaticfunctionwarning( $msg, $callerOffset = 1, $level = E_USER_NOTICE,
$log =
'auto' ) {
189if (
$log ===
'debug' ) {
193 $callerDescription = self::getCallerDescription( $callerOffset );
196 self::formatCallerDescription( $msg, $callerDescription ),
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();
255if ( $declaringClass === $class ) {
256// not overridden, nothing to do 261 $component = $component ?:
'MediaWiki';
262 $msg =
"$declaringClass overrides $method which was deprecated in $component $version.";
298 $component =
false, $callerOffset = 2
300if ( $callerOffset ===
false ) {
304 $callerDescription = self::getCallerDescription( $callerOffset );
305 $callerFunc = $callerDescription[
'func'];
306 $rawMsg = self::formatCallerDescription( $msg, $callerDescription );
311// Check to see if there already was a warning about this function 312if ( isset( self::$deprecationWarnings[$msg][$callerFunc] ) ) {
314 } elseif ( isset( self::$deprecationWarnings[$msg] ) ) {
315if ( 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 362foreach ( self::$deprecationFilters as $filter => $callback ) {
363if ( preg_match( $filter, $msg ) ) {
364if ( is_callable( $callback ) ) {
372 trigger_error( $msg, E_USER_DEPRECATED );
384string $regex, ?callable $callback =
null 386if ( !defined(
'MW_PHPUNIT_TEST' ) ) {
387thrownew LogicException( __METHOD__ .
' can only be used in tests' );
389 self::$deprecationFilters[$regex] = $callback;
396 self::$deprecationFilters = [];
406privatestaticfunction getCallerDescription( $callerOffset ) {
409if ( isset( $callers[$callerOffset] ) ) {
410 $callerfile = $callers[$callerOffset];
411if ( isset( $callerfile[
'file'] ) && isset( $callerfile[
'line'] ) ) {
412 $file = $callerfile[
'file'] .
' at line ' . $callerfile[
'line'];
414 $file =
'(internal function)';
417 $file =
'(unknown location)';
420if ( isset( $callers[$callerOffset + 1] ) ) {
421 $callerfunc = $callers[$callerOffset + 1];
423if ( isset( $callerfunc[
'class'] ) ) {
424 $func .= $callerfunc[
'class'] .
'::';
426if ( isset( $callerfunc[
'function'] ) ) {
427 $func .= $callerfunc[
'function'];
433return [
'file' => $file,
'func' => $func ];
443privatestaticfunction formatCallerDescription( $msg, $caller ) {
444// When changing this, update the below parseCallerDescription() method as well. 445return $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],
484privatestaticfunction sendMessage( $msg, $group, $level ) {
485if ( $level !==
false ) {
486 trigger_error( $msg, $level );
502publicstaticfunctiondebugMsg( $str, $context = [] ) {
508if ( isset( $context[
'prefix'] ) ) {
509 $prefix = $context[
'prefix'];
510 } elseif ( isset( $context[
'channel'] ) && $context[
'channel'] !==
'wfDebug' ) {
511 $prefix =
"[{$context['channel']}] ";
513if ( 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;
521if ( isset( $context[
'channel'] ) && $context[
'channel'] ===
'error' ) {
522 $message = isset( $context[
'exception'] )
523 ? $context[
'exception']->getMessage()
525 $real = self::parseCallerDescription( $message );
527// from wfLogWarning() 528 $message = $real[
'message'];
529 $caller = $real[
'func'];
531 $trace = isset( $context[
'exception'] ) ? $context[
'exception']->getTrace() : [];
532if ( ( $trace[5][
'function'] ??
null ) ===
'wfDeprecated' ) {
533// from MWExceptionHandler/trigger_error/MWDebug/MWDebug/MWDebug/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 ),
566publicstaticfunctionquery( $sql, $function, $runTime, $dbhost ) {
567if ( !self::$enabled ) {
571// Replace invalid UTF-8 chars with a square UTF-8 character 572// This prevents json_encode from erroring out due to binary SQL data 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) 593// last check for invalid utf8 594 $sql = UtfNormal\Validator::cleanUp( $sql );
597'sql' =>
"$dbhost: $sql",
598'function' => $function,
612 $files = get_included_files();
614foreach ( $files as $file ) {
615// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged 616 $size = @filesize( $file );
617if ( $size ===
false ) {
618// Certain files that have been included might then be deleted. This is especially likely to happen 619// in tests, see T351986. 620// Just use a size of 0, but include these files here to try and be as useful as possible. 625'size' => $context->
getLanguage()->formatSize( $size ),
644if ( self::$enabled ) {
645 self::log(
'MWDebug output complete' );
646 $debugInfo = self::getDebugInfo( $context );
648// Cannot use OutputPage::addJsConfigVars because those are already outputted 649// by the time this method is called. 652 . FormatJson::encode( [
'debugInfo' => $debugInfo ] )
658 $html[] =
'<!-- Debug output:';
659foreach ( self::$debug as $line ) {
660 $html[] = htmlspecialchars( $line, ENT_NOQUOTES );
665return 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\">";
684foreach ( self::$debug as $line ) {
685 $display = nl2br( htmlspecialchars( trim( $line ) ) );
687 $html[] =
"<li><code>$display</code></li>";
693return WrappedString::join(
"\n", $html );
703if ( !self::$enabled ) {
707// output errors as debug info, when display_errors is on 708// this is necessary for all non html output of the api, because that clears all errors first 709 $obContents = ob_get_contents();
711 $obContentArray = explode(
'<br />', $obContents );
712foreach ( $obContentArray as $obContent ) {
713if ( 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 );
737if ( !self::$enabled ) {
743 $branch = GitInfo::currentBranch();
744if ( GitInfo::isSHA1( $branch ) ) {
745// If it's a detached HEAD, the SHA1 will already be 746// included in the MW version, so don't show it. 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 class represents the result of the API operations.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.
static init()
Enabled the debugger and load resource module.
static parseCallerDescription( $msg)
Append a caller description to an error message.
static clearDeprecationFilters()
Clear all deprecation filters.
static query( $sql, $function, $runTime, $dbhost)
Begins profiling on a database query.
static appendDebugInfoToApiResult(IContextSource $context, ApiResult $result)
Append the debug info to given ApiResult.
static getLog()
Returns internal log array.
static log( $str)
Adds a line to the log.
static array $log
Log lines.
static warning( $msg, $callerOffset=1, $level=E_USER_NOTICE, $log='auto')
Adds a warning entry to the log.
static debugMsg( $str, $context=[])
This method receives messages from LoggerFactory, wfDebugLog, and MWExceptionHandler.
static array $deprecationWarnings
Array of functions that have already been warned, formatted function-caller to prevent a buttload of ...
static getFilesIncluded(IContextSource $context)
Returns a list of files included, along with their size.
static addModules(OutputPage $out)
Add ResourceLoader modules to the OutputPage object if debugging is enabled.
static array $deprecationFilters
Keys are regexes, values are optional callbacks to call if the filter is hit.
static detectDeprecatedOverride( $instance, $class, $method, $version=false, $component=false, $callerOffset=2)
Show a warning if $method declared in $class is overridden in $instance.
static filterDeprecationForTest(string $regex, ?callable $callback=null)
Deprecation messages matching the supplied regex will be suppressed.
static deprecated( $function, $version=false, $component=false, $callerOffset=2)
Show a warning that $function is deprecated.
static getDebugInfo(IContextSource $context)
Returns the HTML to add to the page for the toolbar.
static sendRawDeprecated( $msg, $sendToLog=true, $callerFunc='')
Send a raw deprecation message to the log and the debug toolbar, without filtering of duplicate messa...
static array $debug
Debug messages from wfDebug().
static deprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
static getDebugHTML(IContextSource $context)
Returns the HTML to add to the page for the toolbar.
static bool $enabled
Is the debugger enabled?
static clearLog()
Clears internal log array and deprecation tracking.
static array $query
SQL statements of the database queries.
static deinit()
Disable the debugger.
static getHTMLDebugLog()
Generate debug log in HTML for displaying at the bottom of the main content area.
This class is a collection of static functions that serve two purposes:
JSON formatter wrapper class.
PSR-3 logger that mimics the historic implementation of MediaWiki's former wfErrorLog logging impleme...
static interpolate( $message, array $context)
Interpolate placeholders in logging message.
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.
HTML sanitizer for MediaWiki.
ResourceLoader is a loading system for JavaScript and CSS resources.
static makeInlineScript( $script, $nonce=null)
Make an HTML script that runs given JS code after startup and base modules.
Fetch status information from a local git repository.
$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.