Seditio Source
Root |
./othercms/xenForo 2.2.8/src/XF/Debugger.php
<?php

namespace XF;

use
Symfony\Component\VarDumper\VarDumper;
use
XF\Db\AbstractAdapter;

use function
count, is_array, strlen, strval;

class
Debugger
{
   
/**
     * @var App
     */
   
protected $app;

    public function
__construct(App $app)
    {
       
$this->app = $app;
    }

    public function
dump($var)
    {
       
VarDumper::dump($var);
    }

    public function
dumpSimple($var, $echo = true, $escape = true)
    {
       
$switchHtmlErrors = $escape;
       
$htmlErrors = @ini_get('html_errors');

        if (
$htmlErrors === false)
        {
           
// reading failed, so just skip trying to switch later on.
           
$switchHtmlErrors = false;
        }

        if (
$switchHtmlErrors)
        {
            @
ini_set('html_errors', '0');
        }

       
ob_start();
       
var_dump($var);
       
$dump = ob_get_clean();

       
$dump = preg_replace("/\]\=\>\n(\s+)/m", "] => ", $dump);
       
$dump = utf8_bad_replace($dump, "\xEF\xBF\xBD");

        if (
PHP_SAPI == 'cli')
        {
           
$output = $dump;
        }
        else
        {
            if (
$escape)
            {
               
$output = '<pre>' . htmlspecialchars($dump) . '</pre>';
            }
            else
            {
               
$output = $dump;
            }
        }

        if (
$echo)
        {
            echo
$output;
        }

        if (
$switchHtmlErrors)
        {
            @
ini_set('html_errors', $htmlErrors);
        }

        return
$output;
    }

   
/**
     * @deprecated will be removed in XF 2.3.
     */
   
public function dumpConsole($var, $type = 'log')
    {
       
$method = null;

        switch (
$type)
        {
            case
'log':
            case
'warn':
            case
'error':
            case
'info':
               
$method = $type;
                break;
            default:
               
$method = 'log';
                break;
        }

        return \
ChromePhp::$method($var);
    }

    public function
dumpToFile($var, $logName = null)
    {
        if (
$logName === null)
        {
           
$logName = 'log_' . strval(\XF::$time - (\XF::$time % 86400));
        }

       
$dump = $this->dumpSimple($var, false, false);

        return \
XF\Util\File::log($logName, $dump);
    }

    public function
getDebugPageHtml(App $app = null)
    {
        if (!
$app)
        {
           
$app = $this->app;
        }

       
$pageTime = microtime(true) - $app['time.granular'];
       
$memoryUsage = memory_get_usage();
       
$memoryUsagePeak = memory_get_peak_usage();
       
$dbDebug = $this->getDatabaseDebugInfo($app['db']);
       
$dbPercent = ($dbDebug['totalQueryRunTime'] / $pageTime) * 100;

       
$includedFiles = $this->getIncludedFilesDebugInfo(get_included_files());

       
$return = "<h1>Page Time: " . number_format($pageTime, 4) . "s</h1>"
           
. "<h2>Memory: " . number_format($memoryUsage / 1024 / 1024, 4) . " MB "
           
. "(Peak: " . number_format($memoryUsagePeak / 1024 / 1024, 4) . " MB)</h2>"
           
. "<h2>Queries ($dbDebug[queryCount], time: " . number_format($dbDebug['totalQueryRunTime'], 4) . "s, "
           
. number_format($dbPercent, 1) . "%)</h2>"
           
. $dbDebug['queryHtml']
            .
"<h2>Included Files ($includedFiles[includedFileCount], XenForo Classes: $includedFiles[includedXenForoClasses])</h2>"
           
. $includedFiles['includedFileHtml'];

        if (
$dbDebug['connectionStatsHtml'])
        {
           
$return .= "\n<h2>DB Connection Stats</h2>" . $dbDebug['connectionStatsHtml'];
        }

        return
$this->getDebugPageWrapperHtml($return);
    }

    public function
getDebugPageWrapperHtml($debugHtml)
    {
        return <<<DEBUG
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="robots" content="noindex, nofollow" />
    <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
    <title>XenForo Debug Output</title>
</head>
<body>
$debugHtml
</body>
</html>
DEBUG;
    }

   
/**
     * Gets database debug information, including query count and run time and
     * the actual queries that were run.
     *
     * @param AbstractAdapter $db
     *
     * @return array Keys: queryCount, totalQueryRunTime, queryHtml
     */
   
public static function getDatabaseDebugInfo(AbstractAdapter $db)
    {
       
$return = [
           
'queryCount' => 0,
           
'totalQueryRunTime' => 0,
           
'queryHtml' => '',
           
'connectionStats' => null,
           
'connectionStatsHtml' => ''
       
];

       
$return['queryCount'] = $db->getQueryCount();

       
$rootDir = \XF::getRootDirectory() . \XF::$DS;

        if (
$return['queryCount'])
        {
           
$return['queryHtml'] .= '<ol>';

           
$queries = $db->getQueryLog();
            foreach (
$queries AS $query)
            {
               
$queryText = rtrim($query['query']);
                if (
preg_match('#(^|\n)(\t+)([ ]*)(?=\S)#', $queryText, $match))
                {
                   
$queryText = preg_replace('#(^|\n)\t{1,' . strlen($match[2]) . '}#', '$1', $queryText);
                }

               
$boundParams = [];
                if (
is_array($query['params']))
                {
                    foreach (
$query['params'] AS $param)
                    {
                       
$boundParams[] = htmlspecialchars($param);
                    }
                }

               
$explainOutput = '';

                if (
preg_match('#^\s*SELECT\s#i', $queryText) && is_array($query['params']))
                {
                   
$explainQuery = $db->query(
                       
'EXPLAIN ' . $query['query'],
                       
$query['params']
                    );
                   
$explainRows = $explainQuery->fetchAll();
                    if (
$explainRows)
                    {
                       
$explainOutput .= '<table border="1">'
                           
. '<tr>'
                           
. '<th>Select Type</th><th>Table</th><th>Type</th><th>Possible Keys</th>'
                           
. '<th>Key</th><th>Key Len</th><th>Ref</th><th>Rows</th><th>Extra</th>'
                           
. '</tr>';

                        foreach (
$explainRows AS $explainRow)
                        {
                            foreach (
$explainRow AS $key => $value)
                            {
                                if (
trim($value) === '')
                                {
                                   
$explainRow[$key] = '&nbsp;';
                                }
                                else
                                {
                                   
$explainRow[$key] = htmlspecialchars($value);
                                }
                            }

                           
$explainOutput .= '<tr>'
                               
. '<td>' . $explainRow['select_type'] . '</td>'
                               
. '<td>' . $explainRow['table'] . '</td>'
                               
. '<td>' . $explainRow['type'] . '</td>'
                               
. '<td>' . $explainRow['possible_keys'] . '</td>'
                               
. '<td>' . $explainRow['key'] . '</td>'
                               
. '<td>' . $explainRow['key_len'] . '</td>'
                               
. '<td>' . $explainRow['ref'] . '</td>'
                               
. '<td>' . $explainRow['rows'] . '</td>'
                               
. '<td>' . $explainRow['Extra'] . '</td>'
                               
. '</tr>';
                        }

                       
$explainOutput .= '</table>';
                    }
                }

               
$traceHtml = '';
                if (
is_array($query['trace']))
                {
                    foreach (
$query['trace'] AS $traceEntry)
                    {
                       
$function = (isset($traceEntry['class']) ? $traceEntry['class'] . $traceEntry['type'] : '') . $traceEntry['function'];
                        if (isset(
$traceEntry['file']))
                        {
                           
$file = str_replace('\\', '/', str_replace($rootDir, '', $traceEntry['file']));
                        }
                        else
                        {
                           
$file = '';
                        }
                       
$traceHtml .= "\t<li><b>" . htmlspecialchars($function) . "()</b>" . (isset($traceEntry['file']) && isset($traceEntry['line']) ? ' <span>in</span> <b>' . $file . "</b> <span>at line</span> <b>$traceEntry[line]</b>" : '') . "</li>\n";
                    }

                   
$traceHtml = "<br /><ol>$traceHtml</ol>";
                }

               
$queryComplete = $query['complete'] ?? $query['start'];
               
$queryTime = $queryComplete - $query['start'];

               
$return['queryHtml'] .= '<li>'
                   
. '<pre>' . htmlspecialchars($queryText) . '</pre>'
                   
. ($boundParams ? '<div><strong>Params:</strong> ' . implode(', ', $boundParams) . '</div>' : '')
                    .
'<div><strong>Run Time:</strong> ' . number_format($queryTime, 6) . '</div>'
                   
. $explainOutput
                   
. $traceHtml
                   
. "</li>\n";

               
$return['totalQueryRunTime'] += $queryTime;
            }

           
$return['queryHtml'] .= '</ol>';
        }

       
$return['connectionStats'] = $db->getConnectionStats();
        if (
$return['connectionStats'])
        {
           
$statsHtml = "<table>\n";

            foreach (
$return['connectionStats'] AS $statName => $statValue)
            {
               
$statsHtml .= "<tr><td>"
                   
. htmlspecialchars($statName) . "</td><td>"
                   
. htmlspecialchars($statValue) . "</td></tr>\n";
            }

           
$statsHtml .= "</table>\n";

           
$return['connectionStatsHtml'] = $statsHtml;
        }

        return
$return;
    }

   
/**
     * Gets included files debug info.
     *
     * @param array $includedFiles
     *
     * @return array Keys: includedFileCount, incldedFileHtml, includedForoClasses
     */
   
public static function getIncludedFilesDebugInfo(array $includedFiles)
    {
       
$return = [
           
'includedFileCount' => count($includedFiles),
           
'includedFileHtml' => '<ol>',
           
'includedXenForoClasses' => 0
       
];

       
$baseDir = dirname(reset($includedFiles));

        foreach (
$includedFiles AS $file)
        {
           
$file = preg_replace('#^' . preg_quote($baseDir, '#') . '(\\\\|/)#', '', $file);
           
$file = htmlspecialchars($file);

            if (
preg_match('#^library(/|\\\\)XenForo(/|\\\\)|src(/|\\\\)XF(/|\\\\)#', $file))
            {
               
$return['includedXenForoClasses']++;
            }
           
$file = preg_replace('#^library(/|\\\\)XenForo(/|\\\\)|src(/|\\\\)XF(/|\\\\)#', '<b>$0</b>', $file);

           
$return['includedFileHtml'] .= '<li>' . $file . '</li>' . "\n";
        }
       
$return['includedFileHtml'] .= '</ol>';

        return
$return;
    }
}