<?php
/**
 * Xoops Logger handlers - component main class file
 *
 * You may not change or alter any portion of this comment or credits
 * of supporting developers from this source code or any supporting source code
 * which is considered copyrighted (c) material of the original comment or credit authors.
 * 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.
 *
 * @copyright       The XOOPS Project http://sourceforge.net/projects/xoops/
 * @license         http://www.fsf.org/copyleft/gpl.html GNU public license
 * @package         kernel
 * @subpackage      logger
 * @since           2.3.0
 * @author          Kazumi Ono  <onokazu@xoops.org>
 * @author          Skalpa Keo <skalpa@xoops.org>
 * @author          Taiwen Jiang <phppp@users.sourceforge.net>
 * @version         $Id: xoopslogger.php 2939 2009-03-10 13:50:37Z phppp $
 *
 * @todo            Not well written, just keep as it is. Refactored in 3.0
 */


if (!defined('XOOPS_ROOT_PATH')) {
    exit();
}

/**
 * Collects information for a page request
 *
 * Records information about database queries, blocks, and execution time
 * and can display it as HTML. It also catches php runtime errors.
 * @package kernel
 */
class XoopsLogger
{
    /**#@+
     * @var array
     */
    var $queries = array();
    var $blocks = array();
    var $extra = array();
    var $logstart = array();
    var $logend = array();
    var $errors = array();
    /**#@-*/

    var $usePopup = false;
    var $activated = true;

    /**@access protected*/
    var $renderingEnabled = false;

    function __construct()
    {
    }

    function XoopsLogger() {
    }
    /**
     * Get a reference to the only instance of this class
     * @return  object XoopsLogger  reference to the only instance
     */
    function &instance()
    {
        static $instance;

        if ( !isset( $instance ) ) {
            $instance = new XoopsLogger();
            // Always catch errors, for security reasons
            set_error_handler( 'XoopsErrorHandler_HandleError' );
        }
        return $instance;
    }
    /**
     * Enable logger output rendering
     * When output rendering is enabled, the logger will insert its output within the page content.
     * If the string <!--{xo-logger-output}--> is found in the page content, the logger output will
     * replace it, otherwise it will be inserted after all the page output.
     */
    function enableRendering()
    {
        if ( !$this->renderingEnabled ) {
            ob_start( array( &$this, 'render' ) );
            $this->renderingEnabled = true;
        }
    }
    /**
     * Returns the current microtime in seconds.
     * @return float
     */
    function microtime()
    {
        $now = explode( ' ', microtime() );
        return (float)$now[0] + (float)$now[1];
    }
    /**
     * Start a timer
     * @param   string  $name   name of the timer
     */
    function startTime($name = 'XOOPS')
    {
        $this->logstart[$name] = $this->microtime();
    }
    /**
     * Stop a timer
     * @param   string  $name   name of the timer
     */
    function stopTime($name = 'XOOPS')
    {
        $this->logend[$name] = $this->microtime();
    }
    /**
     * Log a database query
     * @param   string  $sql    SQL string
     * @param   string  $error  error message (if any)
     * @param   int     $errno  error number (if any)
     */
    function addQuery($sql, $error=null, $errno=null)
    {
        if ( $this->activated ) $this->queries[] = array('sql' => $sql, 'error' => $error, 'errno' => $errno);
    }
    /**
     * Log display of a block
     * @param   string  $name       name of the block
     * @param   bool    $cached     was the block cached?
     * @param   int     $cachetime  cachetime of the block
     */
    function addBlock($name, $cached = false, $cachetime = 0)
    {
        if ( $this->activated ) $this->blocks[] = array('name' => $name, 'cached' => $cached, 'cachetime' => $cachetime);
    }
    /**
     * Log extra information
     * @param   string  $name       name for the entry
     * @param   int     $msg  text message for the entry
     */
    function addExtra($name, $msg)
    {
        if ( $this->activated ) $this->extra[] = array('name' => $name, 'msg' => $msg);
    }

    /**
     * Error handling callback (called by the zend engine)
     */
    function handleError( $errno, $errstr, $errfile, $errline )
    {
        if ( $this->activated && ( $errno & error_reporting() ) ) {
            // NOTE: we only store relative pathnames
            $this->errors[] = compact( 'errno', 'errstr', 'errfile', 'errline' );
        }
        if ( $errno == E_USER_ERROR ) {
            $trace = true;
            if ( substr( $errstr, 0, '8' ) == 'notrace:' ) {
                $trace = false;
                $errstr = substr( $errstr, 8 );
            }
            echo 'This page cannot be displayed due to an internal error.<br/><br/>';
            echo "You can provide the following information to the administrators of ";
            echo "this site to help them solve the problem:<br /><br />";
            echo "Error: $errstr<br />";
            if ( $trace && function_exists( 'debug_backtrace' ) ) {
                echo "<div style='color:#fff;background-color:#fff'>Backtrace:<br />";
                $trace = debug_backtrace();
                array_shift( $trace );
                foreach ( $trace as $step ) {
                    if ( isset( $step['file'] ) ) {
                        echo $this->sanitizePath( $step['file'] );
                        echo ' (' . $step['line'] . ")\n<br />";
                    }
                }
                echo '</div>';
            }
            exit();
        }
    }
    /**
     * @access protected
     */
    function sanitizePath( $path )
    {
        $path = str_replace(
            array( '\\', XOOPS_ROOT_PATH, str_replace( '\\', '/', realpath( XOOPS_ROOT_PATH ) ) ),
            array( '/', '', '' ),
            $path
        );
        return $path;
    }

    /**
     * Output buffering callback inserting logger dump in page output
     */
    function render( $output )
    {
        global $xoopsUser;
        if ( !$this->activated ) {
            return $output;
        }


        $log = $this->dump( $this->usePopup ? 'popup' : '' );
        $this->renderingEnabled = $this->activated = false;

        $pattern = '<!--{xo-logger-output}-->';
        $pos = strpos( $output, $pattern );
        if ( $pos !== false ) {
            return substr( $output, 0, $pos ) . $log . substr( $output, $pos + strlen( $pattern ) );
        } else {
            return $output . $log;
        }
    }
    /**#@+
     * @protected
     */
    function dump( $mode = '' )
    {
        include XOOPS_ROOT_PATH . '/class/logger/render.php';
        return $ret;
    }
    /**
     * get the current execution time of a timer
     *
     * @param   string  $name   name of the counter
     * @return  float   current execution time of the counter
     */
    function dumpTime( $name = 'XOOPS' )
    {
        if ( !isset($this->logstart[$name]) ) {
            return 0;
        }
        $stop = isset( $this->logend[$name] ) ? $this->logend[$name] : $this->microtime();
        return $stop - $this->logstart[$name];
    }
    /**#@-*/
    /**#@+
     * @deprecated
     */
    function dumpAll() {            return $this->dump( '' );           }
    function dumpBlocks() {         return $this->dump( 'blocks' );     }
    function dumpExtra() {          return $this->dump( 'extra' );      }
    function dumpQueries() {        return $this->dump( 'queries' );    }
    /**#@-*/
}

/*
* PHP Error handler
*
* NB: You're not supposed to call this function directly, if you dont understand why, then
* you'd better spend some time reading your PHP manual before you hurt somebody
*
* @internal: Using a function and not calling the handler method directly coz old PHP versions
* set_error_handler() have problems with the array( obj,methodname ) syntax
*/
function XoopsErrorHandler_HandleError( $errNo, $errStr, $errFile, $errLine, $errContext = null )
{
    $logger =& XoopsLogger::instance();
    $logger->handleError( $errNo, $errStr, $errFile, $errLine, $errContext );
}

?>
