<?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
*
- * @todo Clean up HTML generated by the javascript
+ * By default, most methods do nothing ( self::$enabled = false ). You have
+ * to explicitly call MWDebug::init() to enabled them.
+ *
* @todo Profiler support
+ *
+ * @since 1.19
*/
class MWDebug {
protected static $query = array();
/**
- * Request information
+ * Is the debugger enabled?
*
- * @var array
+ * @var bool
*/
- protected static $request = array();
+ protected static $enabled = false;
/**
- * Is the debugger enabled?
+ * Array of functions that have already been warned, formatted
+ * function-caller to prevent a buttload of warnings
*
- * @var bool
+ * @var array
*/
- protected static $enabled = true;
+ protected static $deprecationWarnings = array();
/**
- * Called in Setup.php, initializes the debugger if it is enabled with
- * $wgDebugToolbar
+ * Enabled the debugger and load resource module.
+ * This is called by Setup.php when $wgDebugToolbar is true.
+ *
+ * @since 1.19
*/
public static function init() {
- global $wgDebugToolbar;
+ self::$enabled = true;
+ }
- if ( !$wgDebugToolbar ) {
- self::$enabled = false;
- return;
+ /**
+ * Add ResourceLoader modules to the OutputPage object if debugging is
+ * enabled.
+ *
+ * @since 1.19
+ * @param $out OutputPage
+ */
+ public static function addModules( OutputPage $out ) {
+ if ( self::$enabled ) {
+ $out->addModules( 'mediawiki.debug.init' );
}
-
- RequestContext::getMain()->getOutput()->addModules( 'mediawiki.debug' );
}
/**
* Adds a line to the log
*
- * This does nothing atm, there's not frontend for it
- *
- * @todo Add error and warning log type
* @todo Add support for passing objects
*
+ * @since 1.19
* @param $str string
*/
public static function log( $str ) {
return;
}
- self::$log[] = $str;
+ self::$log[] = array(
+ 'msg' => htmlspecialchars( $str ),
+ 'type' => 'log',
+ 'caller' => wfGetCaller(),
+ );
+ }
+
+ /**
+ * Returns internal log array
+ * @since 1.19
+ * @return array
+ */
+ public static function getLog() {
+ return self::$log;
+ }
+
+ /**
+ * Clears internal log array and deprecation tracking
+ * @since 1.19
+ */
+ public static function clearLog() {
+ self::$log = array();
+ self::$deprecationWarnings = array();
+ }
+
+ /**
+ * Adds a warning entry to the log
+ *
+ * @since 1.19
+ * @param $msg
+ * @param int $callerOffset
+ * @return mixed
+ */
+ public static function warning( $msg, $callerOffset = 1 ) {
+ if ( !self::$enabled ) {
+ return;
+ }
+
+ // Check to see if there was already a deprecation notice, so not to
+ // get a duplicate warning
+ $logCount = count( self::$log );
+ $caller = wfGetCaller( $callerOffset + 1 );
+ if ( $logCount ) {
+ $lastLog = self::$log[ $logCount - 1 ];
+ if ( $lastLog['type'] == 'deprecated' && $lastLog['caller'] == $caller ) {
+ return;
+ }
+ }
+
+ self::$log[] = array(
+ 'msg' => htmlspecialchars( $msg ),
+ 'type' => 'warn',
+ 'caller' => $caller,
+ );
+ }
+
+ /**
+ * Adds a depreciation entry to the log, along with a backtrace
+ *
+ * @since 1.19
+ * @param $function
+ * @param $version
+ * @param $component
+ * @return mixed
+ */
+ public static function deprecated( $function, $version, $component ) {
+ if ( !self::$enabled ) {
+ return;
+ }
+
+ // 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;
+ }
+
+ $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()
+ );
+
+ self::$deprecationWarnings[] = $functionString;
+ self::$log[] = array(
+ 'msg' => $msg,
+ 'type' => 'deprecated',
+ 'caller' => $caller,
+ );
}
/**
* 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 ) {
/**
* Begins profiling on a database query
*
+ * @since 1.19
* @param $sql string
* @param $function string
* @param $isMaster bool
'sql' => $sql,
'function' => $function,
'master' => (bool) $isMaster,
- 'time' > 0.0,
+ 'time' => 0.0,
'_start' => microtime( true ),
);
/**
* Calculates how long a query took.
*
+ * @since 1.19
* @param $id int
*/
public static function queryTime( $id ) {
unset( self::$query[$id]['_start'] );
}
- /**
- * Processes a WebRequest object
- *
- * @param $request WebRequest
- */
- public static function processRequest( WebRequest $request ) {
- if ( !self::$enabled ) {
- return;
- }
-
- self::$request = array(
- 'method' => $_SERVER['REQUEST_METHOD'],
- 'url' => $request->getRequestURL(),
- 'headers' => $request->getAllHeaders(),
- 'params' => $request->getValues(),
- );
- }
-
/**
* Returns a list of files included, along with their size
*
+ * @param $context IContextSource
* @return array
*/
- protected static function getFilesIncluded() {
+ protected static function getFilesIncluded( IContextSource $context ) {
$files = get_included_files();
$fileList = array();
foreach ( $files as $file ) {
$size = filesize( $file );
$fileList[] = array(
'name' => $file,
- 'size' => self::formatBytes( $size ),
+ 'size' => $context->getLanguage()->formatSize( $size ),
);
}
/**
* Returns the HTML to add to the page for the toolbar
*
+ * @since 1.19
+ * @param $context IContextSource
* @return string
*/
- public static function getDebugHTML() {
+ public static function getDebugHTML( IContextSource $context ) {
if ( !self::$enabled ) {
return '';
}
- global $wgVersion, $wgRequestTime;
- $debugInfo = array(
- 'mwVersion' => $wgVersion,
- 'phpVersion' => PHP_VERSION,
- 'time' => microtime( true ) - $wgRequestTime,
- 'log' => self::$log,
- 'debugLog' => self::$debug,
- 'queries' => self::$query,
- 'request' => self::$request,
- 'memory' => self::formatBytes( memory_get_usage() ),
- 'memoryPeak' => self::formatBytes( memory_get_peak_usage() ),
- 'includes' => self::getFilesIncluded(),
+ 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 ) )
+ )
);
- // TODO: Clean this up
- $html = Html::openElement( 'script' );
- $html .= 'var debugInfo = ' . Xml::encodeJsVar( $debugInfo ) . ';';
- $html .= " $(function() { mw.loader.using( 'mediawiki.debug', function() { mw.Debug.init( debugInfo ) } ); }); ";
- $html .= Html::closeElement( 'script' );
return $html;
}
/**
- * Formats raw bytes integer into a human readable format
+ * Append the debug info to given ApiResult
*
- * @author John Himmelman - http://stackoverflow.com/a/2510540/343911
- * @param $size int
- * @param $precision int
- * @return string
+ * @param $context IContextSource
+ * @param $result ApiResult
*/
- protected static function formatBytes( $size, $precision = 2 ) {
- $base = log( $size ) / log( 1024 );
- // If we ever use 1TB of RAM we're fucked
- $suffixes = array( '', 'kb', 'MB', 'GB', 'TB' );
+ public static function appendDebugInfoToApiResult( IContextSource $context, ApiResult $result ) {
+ if ( !self::$enabled ) {
+ return;
+ }
+
+ MWDebug::log( 'MWDebug output complete' );
+ $debugInfo = self::getDebugInfo( $context );
- return round( pow( 1024, $base - floor( $base ) ), $precision ) . $suffixes[floor( $base )];
+ $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();
+ 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'],
+ 'url' => $request->getRequestURL(),
+ 'headers' => $request->getAllHeaders(),
+ 'params' => $request->getValues(),
+ ),
+ 'memory' => $context->getLanguage()->formatSize( memory_get_usage() ),
+ 'memoryPeak' => $context->getLanguage()->formatSize( memory_get_peak_usage() ),
+ 'includes' => self::getFilesIncluded( $context ),
+ );
}
-}
\ No newline at end of file
+}