summaryrefslogtreecommitdiff
path: root/includes/debug/Debug.php
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2013-01-18 16:46:04 +0100
committerPierre Schmitz <pierre@archlinux.de>2013-01-18 16:46:04 +0100
commit63601400e476c6cf43d985f3e7b9864681695ed4 (patch)
treef7846203a952e38aaf66989d0a4702779f549962 /includes/debug/Debug.php
parent8ff01378c9e0207f9169b81966a51def645b6a51 (diff)
Update to MediaWiki 1.20.2
this update includes: * adjusted Arch Linux skin * updated FluxBBAuthPlugin * patch for https://bugzilla.wikimedia.org/show_bug.cgi?id=44024
Diffstat (limited to 'includes/debug/Debug.php')
-rw-r--r--includes/debug/Debug.php371
1 files changed, 309 insertions, 62 deletions
diff --git a/includes/debug/Debug.php b/includes/debug/Debug.php
index de50ccac..d02bcf53 100644
--- a/includes/debug/Debug.php
+++ b/includes/debug/Debug.php
@@ -1,4 +1,24 @@
<?php
+/**
+ * Debug toolbar related code
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ */
/**
* New debugger system that outputs a toolbar on page view
@@ -7,6 +27,8 @@
* to explicitly call MWDebug::init() to enabled them.
*
* @todo Profiler support
+ *
+ * @since 1.19
*/
class MWDebug {
@@ -49,6 +71,8 @@ class MWDebug {
/**
* Enabled the debugger and load resource module.
* This is called by Setup.php when $wgDebugToolbar is true.
+ *
+ * @since 1.19
*/
public static function init() {
self::$enabled = true;
@@ -58,6 +82,7 @@ class MWDebug {
* Add ResourceLoader modules to the OutputPage object if debugging is
* enabled.
*
+ * @since 1.19
* @param $out OutputPage
*/
public static function addModules( OutputPage $out ) {
@@ -71,6 +96,7 @@ class MWDebug {
*
* @todo Add support for passing objects
*
+ * @since 1.19
* @param $str string
*/
public static function log( $str ) {
@@ -87,6 +113,8 @@ class MWDebug {
/**
* Returns internal log array
+ * @since 1.19
+ * @return array
*/
public static function getLog() {
return self::$log;
@@ -94,6 +122,7 @@ class MWDebug {
/**
* Clears internal log array and deprecation tracking
+ * @since 1.19
*/
public static function clearLog() {
self::$log = array();
@@ -103,87 +132,178 @@ class MWDebug {
/**
* Adds a warning entry to the log
*
- * @param $msg
- * @param int $callerOffset
+ * @since 1.19
+ * @param $msg string
+ * @param $callerOffset int
* @return mixed
*/
- public static function warning( $msg, $callerOffset = 1 ) {
- if ( !self::$enabled ) {
- return;
+ public static function warning( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
+ $callerDescription = self::getCallerDescription( $callerOffset );
+
+ self::sendWarning( $msg, $callerDescription, $level );
+
+ if ( self::$enabled ) {
+ self::$log[] = array(
+ 'msg' => htmlspecialchars( $msg ),
+ 'type' => 'warn',
+ 'caller' => $callerDescription['func'],
+ );
}
+ }
+
+ /**
+ * Show a warning that $function is deprecated.
+ * This will send it to the following locations:
+ * - Debug toolbar, with one item per function and caller, if $wgDebugToolbar
+ * is set to true.
+ * - PHP's error log, with level E_USER_DEPRECATED, if $wgDevelopmentWarnings
+ * is set to true.
+ * - MediaWiki's debug log, if $wgDevelopmentWarnings is set to false.
+ *
+ * @since 1.19
+ * @param $function string: Function that is deprecated.
+ * @param $version string|bool: Version in which the function was deprecated.
+ * @param $component string|bool: Component to which the function belongs.
+ * If false, it is assumbed the function is in MediaWiki core.
+ * @param $callerOffset integer: How far up the callstack is the original
+ * caller. 2 = function that called the function that called
+ * MWDebug::deprecated() (Added in 1.20).
+ * @return mixed
+ */
+ public static function deprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
+ $callerDescription = self::getCallerDescription( $callerOffset );
+ $callerFunc = $callerDescription['func'];
+
+ $sendToLog = true;
- // Check to see if there was already a deprecation notice, so not to
- // get a duplicate warning
- $logCount = count( self::$log );
- if ( $logCount ) {
- $lastLog = self::$log[ $logCount - 1 ];
- if ( $lastLog['type'] == 'deprecated' && $lastLog['caller'] == wfGetCaller( $callerOffset + 1 ) ) {
+ // Check to see if there already was a warning about this function
+ if ( isset( self::$deprecationWarnings[$function][$callerFunc] ) ) {
+ return;
+ } elseif ( isset( self::$deprecationWarnings[$function] ) ) {
+ if ( self::$enabled ) {
+ $sendToLog = false;
+ } else {
return;
}
}
- self::$log[] = array(
- 'msg' => htmlspecialchars( $msg ),
- 'type' => 'warn',
- 'caller' => wfGetCaller( $callerOffset ),
- );
+ self::$deprecationWarnings[$function][$callerFunc] = true;
+
+ if ( $version ) {
+ global $wgDeprecationReleaseLimit;
+ if ( $wgDeprecationReleaseLimit && $component === false ) {
+ # Strip -* off the end of $version so that branches can use the
+ # format #.##-branchname to avoid issues if the branch is merged into
+ # a version of MediaWiki later than what it was branched from
+ $comparableVersion = preg_replace( '/-.*$/', '', $version );
+
+ # If the comparableVersion is larger than our release limit then
+ # skip the warning message for the deprecation
+ if ( version_compare( $wgDeprecationReleaseLimit, $comparableVersion, '<' ) ) {
+ $sendToLog = false;
+ }
+ }
+
+ $component = $component === false ? 'MediaWiki' : $component;
+ $msg = "Use of $function was deprecated in $component $version.";
+ } else {
+ $msg = "Use of $function is deprecated.";
+ }
+
+ if ( $sendToLog ) {
+ self::sendWarning( $msg, $callerDescription, E_USER_DEPRECATED );
+ }
+
+ if ( self::$enabled ) {
+ $logMsg = htmlspecialchars( $msg ) .
+ Html::rawElement( 'div', array( 'class' => 'mw-debug-backtrace' ),
+ Html::element( 'span', array(), 'Backtrace:' ) . wfBacktrace()
+ );
+
+ self::$log[] = array(
+ 'msg' => $logMsg,
+ 'type' => 'deprecated',
+ 'caller' => $callerFunc,
+ );
+ }
}
/**
- * Adds a depreciation entry to the log, along with a backtrace
+ * Get an array describing the calling function at a specified offset.
*
- * @param $function
- * @param $version
- * @param $component
- * @return mixed
+ * @param $callerOffset integer: How far up the callstack is the original
+ * caller. 0 = function that called getCallerDescription()
+ * @return array with two keys: 'file' and 'func'
*/
- public static function deprecated( $function, $version, $component ) {
- if ( !self::$enabled ) {
- return;
+ private static function getCallerDescription( $callerOffset ) {
+ $callers = wfDebugBacktrace();
+
+ if ( isset( $callers[$callerOffset] ) ) {
+ $callerfile = $callers[$callerOffset];
+ if ( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ) {
+ $file = $callerfile['file'] . ' at line ' . $callerfile['line'];
+ } else {
+ $file = '(internal function)';
+ }
+ } else {
+ $file = '(unknown location)';
}
- // Chain: This function -> wfDeprecated -> deprecatedFunction -> caller
- $caller = wfGetCaller( 4 );
-
- // Check to see if there already was a warning about this function
- $functionString = "$function-$caller";
- if ( in_array( $functionString, self::$deprecationWarnings ) ) {
- return;
+ if ( isset( $callers[$callerOffset + 1] ) ) {
+ $callerfunc = $callers[$callerOffset + 1];
+ $func = '';
+ if ( isset( $callerfunc['class'] ) ) {
+ $func .= $callerfunc['class'] . '::';
+ }
+ if ( isset( $callerfunc['function'] ) ) {
+ $func .= $callerfunc['function'];
+ }
+ } else {
+ $func = 'unknown';
}
- $version = $version === false ? '(unknown version)' : $version;
- $component = $component === false ? 'MediaWiki' : $component;
- $msg = htmlspecialchars( "Use of function $function was deprecated in $component $version" );
- $msg .= Html::rawElement( 'div', array( 'class' => 'mw-debug-backtrace' ),
- Html::element( 'span', array(), 'Backtrace:' )
- . wfBacktrace()
- );
+ return array( 'file' => $file, 'func' => $func );
+ }
- self::$deprecationWarnings[] = $functionString;
- self::$log[] = array(
- 'msg' => $msg,
- 'type' => 'deprecated',
- 'caller' => $caller,
- );
+ /**
+ * Send a warning either to the debug log or by triggering an user PHP
+ * error depending on $wgDevelopmentWarnings.
+ *
+ * @param $msg string Message to send
+ * @param $caller array caller description get from getCallerDescription()
+ * @param $level error level to use if $wgDevelopmentWarnings is true
+ */
+ private static function sendWarning( $msg, $caller, $level ) {
+ global $wgDevelopmentWarnings;
+
+ $msg .= ' [Called from ' . $caller['func'] . ' in ' . $caller['file'] . ']';
+
+ if ( $wgDevelopmentWarnings ) {
+ trigger_error( $msg, $level );
+ } else {
+ wfDebug( "$msg\n" );
+ }
}
/**
* This is a method to pass messages from wfDebug to the pretty debugger.
* Do NOT use this method, use MWDebug::log or wfDebug()
*
+ * @since 1.19
* @param $str string
*/
public static function debugMsg( $str ) {
- if ( !self::$enabled ) {
- return;
- }
+ global $wgDebugComments, $wgShowDebug;
- self::$debug[] = trim( $str );
+ if ( self::$enabled || $wgDebugComments || $wgShowDebug ) {
+ self::$debug[] = rtrim( $str );
+ }
}
/**
* Begins profiling on a database query
*
+ * @since 1.19
* @param $sql string
* @param $function string
* @param $isMaster bool
@@ -209,6 +329,7 @@ class MWDebug {
/**
* Calculates how long a query took.
*
+ * @since 1.19
* @param $id int
*/
public static function queryTime( $id ) {
@@ -243,26 +364,162 @@ class MWDebug {
/**
* Returns the HTML to add to the page for the toolbar
*
+ * @since 1.19
* @param $context IContextSource
* @return string
*/
public static function getDebugHTML( IContextSource $context ) {
- if ( !self::$enabled ) {
+ global $wgDebugComments;
+
+ $html = '';
+
+ if ( self::$enabled ) {
+ MWDebug::log( 'MWDebug output complete' );
+ $debugInfo = self::getDebugInfo( $context );
+
+ // Cannot use OutputPage::addJsConfigVars because those are already outputted
+ // by the time this method is called.
+ $html = Html::inlineScript(
+ ResourceLoader::makeLoaderConditionalScript(
+ ResourceLoader::makeConfigSetScript( array( 'debugInfo' => $debugInfo ) )
+ )
+ );
+ }
+
+ if ( $wgDebugComments ) {
+ $html .= "<!-- Debug output:\n" .
+ htmlspecialchars( implode( "\n", self::$debug ) ) .
+ "\n\n-->";
+ }
+
+ return $html;
+ }
+
+ /**
+ * Generate debug log in HTML for displaying at the bottom of the main
+ * content area.
+ * If $wgShowDebug is false, an empty string is always returned.
+ *
+ * @since 1.20
+ * @return string HTML fragment
+ */
+ public static function getHTMLDebugLog() {
+ global $wgDebugTimestamps, $wgShowDebug;
+
+ if ( !$wgShowDebug ) {
return '';
}
- global $wgVersion, $wgRequestTime;
+ $curIdent = 0;
+ $ret = "\n<hr />\n<strong>Debug data:</strong><ul id=\"mw-debug-html\">\n<li>";
+
+ foreach ( self::$debug as $line ) {
+ $pre = '';
+ if ( $wgDebugTimestamps ) {
+ $matches = array();
+ if ( preg_match( '/^(\d+\.\d+ {1,3}\d+.\dM\s{2})/', $line, $matches ) ) {
+ $pre = $matches[1];
+ $line = substr( $line, strlen( $pre ) );
+ }
+ }
+ $display = ltrim( $line );
+ $ident = strlen( $line ) - strlen( $display );
+ $diff = $ident - $curIdent;
+
+ $display = $pre . $display;
+ if ( $display == '' ) {
+ $display = "\xc2\xa0";
+ }
+
+ if ( !$ident && $diff < 0 && substr( $display, 0, 9 ) != 'Entering ' && substr( $display, 0, 8 ) != 'Exiting ' ) {
+ $ident = $curIdent;
+ $diff = 0;
+ $display = '<span style="background:yellow;">' . nl2br( htmlspecialchars( $display ) ) . '</span>';
+ } else {
+ $display = nl2br( htmlspecialchars( $display ) );
+ }
+
+ if ( $diff < 0 ) {
+ $ret .= str_repeat( "</li></ul>\n", -$diff ) . "</li><li>\n";
+ } elseif ( $diff == 0 ) {
+ $ret .= "</li><li>\n";
+ } else {
+ $ret .= str_repeat( "<ul><li>\n", $diff );
+ }
+ $ret .= "<tt>$display</tt>\n";
+
+ $curIdent = $ident;
+ }
+
+ $ret .= str_repeat( '</li></ul>', $curIdent ) . "</li>\n</ul>\n";
+
+ return $ret;
+ }
+
+ /**
+ * Append the debug info to given ApiResult
+ *
+ * @param $context IContextSource
+ * @param $result ApiResult
+ */
+ public static function appendDebugInfoToApiResult( IContextSource $context, ApiResult $result ) {
+ if ( !self::$enabled ) {
+ return;
+ }
+
+ // output errors as debug info, when display_errors is on
+ // this is necessary for all non html output of the api, because that clears all errors first
+ $obContents = ob_get_contents();
+ if( $obContents ) {
+ $obContentArray = explode( '<br />', $obContents );
+ foreach( $obContentArray as $obContent ) {
+ if( trim( $obContent ) ) {
+ self::debugMsg( Sanitizer::stripAllTags( $obContent ) );
+ }
+ }
+ }
+
MWDebug::log( 'MWDebug output complete' );
+ $debugInfo = self::getDebugInfo( $context );
+
+ $result->setIndexedTagName( $debugInfo, 'debuginfo' );
+ $result->setIndexedTagName( $debugInfo['log'], 'line' );
+ foreach( $debugInfo['debugLog'] as $index => $debugLogText ) {
+ $vals = array();
+ ApiResult::setContent( $vals, $debugLogText );
+ $debugInfo['debugLog'][$index] = $vals; //replace
+ }
+ $result->setIndexedTagName( $debugInfo['debugLog'], 'msg' );
+ $result->setIndexedTagName( $debugInfo['queries'], 'query' );
+ $result->setIndexedTagName( $debugInfo['includes'], 'queries' );
+ $result->addValue( array(), 'debuginfo', $debugInfo );
+ }
+
+ /**
+ * Returns the HTML to add to the page for the toolbar
+ *
+ * @param $context IContextSource
+ * @return array
+ */
+ public static function getDebugInfo( IContextSource $context ) {
+ if ( !self::$enabled ) {
+ return array();
+ }
+
+ global $wgVersion, $wgRequestTime;
$request = $context->getRequest();
- $debugInfo = array(
+ return array(
'mwVersion' => $wgVersion,
'phpVersion' => PHP_VERSION,
+ 'gitRevision' => GitInfo::headSHA1(),
+ 'gitBranch' => GitInfo::currentBranch(),
+ 'gitViewUrl' => GitInfo::headViewUrl(),
'time' => microtime( true ) - $wgRequestTime,
'log' => self::$log,
'debugLog' => self::$debug,
'queries' => self::$query,
'request' => array(
- 'method' => $_SERVER['REQUEST_METHOD'],
+ 'method' => $request->getMethod(),
'url' => $request->getRequestURL(),
'headers' => $request->getAllHeaders(),
'params' => $request->getValues(),
@@ -271,15 +528,5 @@ class MWDebug {
'memoryPeak' => $context->getLanguage()->formatSize( memory_get_peak_usage() ),
'includes' => self::getFilesIncluded( $context ),
);
-
- // Cannot use OutputPage::addJsConfigVars because those are already outputted
- // by the time this method is called.
- $html = Html::inlineScript(
- ResourceLoader::makeLoaderConditionalScript(
- ResourceLoader::makeConfigSetScript( array( 'debugInfo' => $debugInfo ) )
- )
- );
-
- return $html;
}
}