Merge "(bug 37180) Removed hard coded parentheses in SpecialVersion.php"
[lhc/web/wiklou.git] / includes / debug / Debug.php
index 181b81d..0c0052f 100644 (file)
@@ -1,10 +1,34 @@
 <?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 {
 
@@ -30,42 +54,49 @@ 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 ) {
@@ -73,13 +104,106 @@ class MWDebug {
                        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 ) {
@@ -93,6 +217,7 @@ class MWDebug {
        /**
         * Begins profiling on a database query
         *
+        * @since 1.19
         * @param $sql string
         * @param $function string
         * @param $isMaster bool
@@ -108,7 +233,7 @@ class MWDebug {
                        'sql' => $sql,
                        'function' => $function,
                        'master' => (bool) $isMaster,
-                       'time' > 0.0,
+                       'time' => 0.0,
                        '_start' => microtime( true ),
                );
 
@@ -118,6 +243,7 @@ class MWDebug {
        /**
         * Calculates how long a query took.
         *
+        * @since 1.19
         * @param $id int
         */
        public static function queryTime( $id ) {
@@ -129,37 +255,20 @@ class MWDebug {
                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 ),
                        );
                }
 
@@ -169,48 +278,88 @@ 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() {
+       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
+}