Source for file debug.php

Documentation is available at debug.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Plugin
  4.  * @subpackage  System.Debug
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE
  8.  */
  9.  
  10. defined('_JEXEC'or die;
  11.  
  12. /**
  13.  * Joomla! Debug plugin
  14.  *
  15.  * @package     Joomla.Plugin
  16.  * @subpackage  System.Debug
  17.  * @since       1.5
  18.  */
  19. class PlgSystemDebug extends JPlugin
  20. {
  21.     protected $linkFormat = '';
  22.  
  23.     /**
  24.      * True if debug lang is on.
  25.      *
  26.      * @var    boolean 
  27.      * @since  3.0
  28.      */
  29.     private $debugLang false;
  30.  
  31.     /**
  32.      * Holds log entries handled by the plugin.
  33.      *
  34.      * @var    array 
  35.      * @since  3.1
  36.      */
  37.     private $logEntries array();
  38.  
  39.     /**
  40.      * Holds SHOW PROFILES of queries
  41.      *
  42.      * @var    array 
  43.      * @since  3.1.2
  44.      */
  45.     private $sqlShowProfiles array();
  46.  
  47.     /**
  48.      * Holds all SHOW PROFILE FOR QUERY n, indexed by n-1
  49.      *
  50.      * @var    array 
  51.      * @since  3.1.2
  52.      */
  53.     private $sqlShowProfileEach array();
  54.  
  55.     /**
  56.      * Holds all EXPLAIN EXTENDED for all queries
  57.      *
  58.      * @var    array 
  59.      * @since  3.1.2
  60.      */
  61.     private $explains array();
  62.  
  63.     /**
  64.      * Constructor.
  65.      *
  66.      * @param   object  &$subject  The object to observe
  67.      * @param   array   $config    An array that holds the plugin configuration
  68.      *
  69.      * @since   1.5
  70.      */
  71.     public function __construct(&$subject$config)
  72.     {
  73.         parent::__construct($subject$config);
  74.  
  75.         // Log the deprecated API.
  76.         if ($this->params->get('log-deprecated'))
  77.         {
  78.             JLog::addLogger(array('text_file' => 'deprecated.php')JLog::ALLarray('deprecated'));
  79.         }
  80.  
  81.         $this->debugLang JFactory::getApplication()->getCfg('debug_lang');
  82.  
  83.         // Only if debugging or language debug is enabled
  84.         if (JDEBUG || $this->debugLang)
  85.         {
  86.             JFactory::getConfig()->set('gzip'0);
  87.             ob_start();
  88.             ob_implicit_flush(false);
  89.         }
  90.  
  91.         $this->linkFormat = ini_get('xdebug.file_link_format');
  92.  
  93.         if ($this->params->get('logs'1))
  94.         {
  95.             $priority 0;
  96.  
  97.             foreach ($this->params->get('log_priorities'array()) as $p)
  98.             {
  99.                 $const 'JLog::' strtoupper($p);
  100.  
  101.                 if (!defined($const))
  102.                 {
  103.                     continue;
  104.                 }
  105.  
  106.                 $priority |= constant($const);
  107.             }
  108.  
  109.             // Split into an array at any character other than alphabet, numbers, _, ., or -
  110.             $categories array_filter(preg_split('/[^A-Z0-9_\.-]/i'$this->params->get('log_categories''')));
  111.             $mode $this->params->get('log_category_mode'0);
  112.  
  113.             JLog::addLogger(array('logger' => 'callback''callback' => array($this'logger'))$priority$categories$mode);
  114.         }
  115.  
  116.         // Prepare disconnect-handler for SQL profiling:
  117.         $db JFactory::getDbo();
  118.         $db->addDisconnectHandler(array($this'mysqlDisconnectHandler'));
  119.     }
  120.  
  121.     /**
  122.      * Add the CSS for debug. We can't do this in the constructor because
  123.      * stuff breaks.
  124.      *
  125.      * @return  void 
  126.      *
  127.      * @since   2.5
  128.      */
  129.     public function onAfterDispatch()
  130.     {
  131.         // Only if debugging or language debug is enabled
  132.         if ((JDEBUG || $this->debugLang&& $this->isAuthorisedDisplayDebug())
  133.         {
  134.             JHtml::_('stylesheet''cms/debug.css'array()true);
  135.         }
  136.  
  137.         // Only if debugging is enabled for SQL queries popovers
  138.         if (JDEBUG && $this->isAuthorisedDisplayDebug())
  139.         {
  140.             JHtml::_('bootstrap.tooltip');
  141.             JHtml::_('bootstrap.popover''.hasPopover'array('placement' => 'top'));
  142.         }
  143.     }
  144.  
  145.     /**
  146.      * Show the debug info
  147.      *
  148.      * @since  1.6
  149.      */
  150.     public function __destruct()
  151.     {
  152.         // Do not render if debugging or language debug is not enabled
  153.         if (!JDEBUG && !$this->debugLang)
  154.         {
  155.             return;
  156.         }
  157.  
  158.         // User has to be authorised to see the debug information
  159.         if (!$this->isAuthorisedDisplayDebug())
  160.         {
  161.             return;
  162.         }
  163.  
  164.         // Only render for HTML output
  165.         if (JFactory::getDocument()->getType(!== 'html')
  166.         {
  167.             return;
  168.         }
  169.  
  170.         // Capture output
  171.         $contents ob_get_contents();
  172.  
  173.         if ($contents)
  174.         {
  175.             ob_end_clean();
  176.         }
  177.  
  178.         // No debug for Safari and Chrome redirection
  179.         if (strstr(strtolower($_SERVER['HTTP_USER_AGENT'])'webkit'!== false
  180.             && substr($contents050== '<html><head><meta http-equiv="refresh" content="0;'
  181.         )
  182.         {
  183.             echo $contents;
  184.             return;
  185.         }
  186.  
  187.         // Load language
  188.         $this->loadLanguage();
  189.  
  190.         $html array();
  191.  
  192.         // Some "mousewheel protecting" JS
  193.         $html["<script>function toggleContainer(name)
  194.         {
  195.             var e = document.getElementById(name);// MooTools might not be available ;)
  196.             e.style.display = (e.style.display == 'none') ? 'block' : 'none';
  197.         }</script>";
  198.  
  199.         $html['<div id="system-debug" class="profiler">';
  200.  
  201.         $html['<h1>' JText::_('PLG_DEBUG_TITLE''</h1>';
  202.  
  203.         if (JDEBUG)
  204.         {
  205.             if (JError::getErrors())
  206.             {
  207.                 $html[$this->display('errors');
  208.             }
  209.  
  210.             $html[$this->display('session');
  211.  
  212.             if ($this->params->get('profile'1))
  213.             {
  214.                 $html[$this->display('profile_information');
  215.             }
  216.  
  217.             if ($this->params->get('memory'1))
  218.             {
  219.                 $html[$this->display('memory_usage');
  220.             }
  221.  
  222.             if ($this->params->get('queries'1))
  223.             {
  224.                 $html[$this->display('queries');
  225.             }
  226.  
  227.             if ($this->params->get('logs'1&& !empty($this->logEntries))
  228.             {
  229.                 $html[$this->display('logs');
  230.             }
  231.         }
  232.  
  233.         if ($this->debugLang)
  234.         {
  235.             if ($this->params->get('language_errorfiles'1))
  236.             {
  237.                 $languageErrors JFactory::getLanguage()->getErrorFiles();
  238.                 $html[$this->display('language_files_in_error'$languageErrors);
  239.             }
  240.  
  241.             if ($this->params->get('language_files'1))
  242.             {
  243.                 $html[$this->display('language_files_loaded');
  244.             }
  245.  
  246.             if ($this->params->get('language_strings'))
  247.             {
  248.                 $html[$this->display('untranslated_strings');
  249.             }
  250.         }
  251.  
  252.         $html['</div>';
  253.  
  254.         echo str_replace('</body>'implode(''$html'</body>'$contents);
  255.     }
  256.  
  257.     /**
  258.      * Method to check if the current user is allowed to see the debug information or not.
  259.      *
  260.      * @return  boolean  True is access is allowed
  261.      *
  262.      * @since   3.0
  263.      */
  264.     private function isAuthorisedDisplayDebug()
  265.     {
  266.         static $result null;
  267.  
  268.         if (!is_null($result))
  269.         {
  270.             return $result;
  271.         }
  272.  
  273.         // If the user is not allowed to view the output then end here
  274.         $filterGroups = (array) $this->params->get('filter_groups'null);
  275.  
  276.         if (!empty($filterGroups))
  277.         {
  278.             $userGroups JFactory::getUser()->get('groups');
  279.  
  280.             if (!array_intersect($filterGroups$userGroups))
  281.             {
  282.                 $result false;
  283.  
  284.                 return false;
  285.             }
  286.         }
  287.  
  288.         $result true;
  289.  
  290.         return true;
  291.     }
  292.  
  293.     /**
  294.      * General display method.
  295.      *
  296.      * @param   string  $item    The item to display
  297.      * @param   array   $errors  Errors occured during execution
  298.      *
  299.      * @return  string 
  300.      *
  301.      * @since   2.5
  302.      */
  303.     protected function display($itemarray $errors array())
  304.     {
  305.         $title JText::_('PLG_DEBUG_' strtoupper($item));
  306.  
  307.         $status '';
  308.  
  309.         if (count($errors))
  310.         {
  311.             $status ' dbg-error';
  312.         }
  313.  
  314.         $fncName 'display' ucfirst(str_replace('_'''$item));
  315.  
  316.         if (!method_exists($this$fncName))
  317.         {
  318.             return __METHOD__ . ' -- Unknown method: ' $fncName '<br />';
  319.         }
  320.  
  321.         $html '';
  322.  
  323.         $js "toggleContainer('dbg_container_" $item "');";
  324.  
  325.         $class 'dbg-header' $status;
  326.  
  327.         $html['<div class="' $class '" onclick="' $js '"><a href="javascript:void(0);"><h3>' $title '</h3></a></div>';
  328.  
  329.         // @todo set with js.. ?
  330.         $style ' style="display: none;"';
  331.  
  332.         $html['<div ' $style ' class="dbg-container" id="dbg_container_' $item '">';
  333.         $html[$this->$fncName();
  334.         $html['</div>';
  335.  
  336.         return implode(''$html);
  337.     }
  338.  
  339.     /**
  340.      * Display session information.
  341.      *
  342.      * Called recursive.
  343.      *
  344.      * @param   string   $key      A session key
  345.      * @param   mixed    $session  The session array, initially null
  346.      * @param   integer  $id       The id is used for JS toggling the div
  347.      *
  348.      * @return  string 
  349.      *
  350.      * @since   2.5
  351.      */
  352.     protected function displaySession($key ''$session null$id 0)
  353.     {
  354.         if (!$session)
  355.         {
  356.             $session $_SESSION;
  357.         }
  358.  
  359.         $html array();
  360.         static $id;
  361.  
  362.         if (!is_array($session))
  363.         {
  364.             $html[$key ' &rArr;' $session PHP_EOL;
  365.         }
  366.         else
  367.         {
  368.             foreach ($session as $sKey => $entries)
  369.             {
  370.                 $display true;
  371.  
  372.                 if (is_array($entries&& $entries)
  373.                 {
  374.                     $display false;
  375.                 }
  376.  
  377.                 if (is_object($entries))
  378.                 {
  379.                     $o JArrayHelper::fromObject($entries);
  380.  
  381.                     if ($o)
  382.                     {
  383.                         $entries $o;
  384.                         $display false;
  385.                     }
  386.                 }
  387.  
  388.                 if (!$display)
  389.                 {
  390.                     $js "toggleContainer('dbg_container_session" $id '_' $sKey "');";
  391.  
  392.                     $html['<div class="dbg-header" onclick="' $js '"><a href="javascript:void(0);"><h3>' $sKey '</h3></a></div>';
  393.  
  394.                     // @todo set with js.. ?
  395.                     $style ' style="display: none;"';
  396.  
  397.                     $html['<div ' $style ' class="dbg-container" id="dbg_container_session' $id '_' $sKey '">';
  398.                     $id++;
  399.  
  400.                     // Recurse...
  401.                     $this->displaySession($sKey$entries$id);
  402.  
  403.                     $html['</div>';
  404.  
  405.                     continue;
  406.                 }
  407.  
  408.                 if (is_array($entries))
  409.                 {
  410.                     $entries implode($entries);
  411.                 }
  412.  
  413.                 if (is_string($entries))
  414.                 {
  415.                     $html['<code>';
  416.                     $html[$sKey ' &rArr; ' $entries '<br />';
  417.                     $html['</code>';
  418.                 }
  419.             }
  420.         }
  421.  
  422.         return implode(''$html);
  423.     }
  424.  
  425.     /**
  426.      * Display errors.
  427.      *
  428.      * @return  string 
  429.      *
  430.      * @since   2.5
  431.      */
  432.     protected function displayErrors()
  433.     {
  434.         $html array();
  435.  
  436.         $html['<ol>';
  437.  
  438.         while ($error JError::getError(true))
  439.         {
  440.             $col (E_WARNING == $error->get('level')) 'red' 'orange';
  441.  
  442.             $html['<li>';
  443.             $html['<b style="color: ' $col '">' $error->getMessage('</b><br />';
  444.  
  445.             $info $error->get('info');
  446.  
  447.             if ($info)
  448.             {
  449.                 $html['<pre>' print_r($infotrue'</pre><br />';
  450.             }
  451.  
  452.             $html[$this->renderBacktrace($error);
  453.             $html['</li>';
  454.         }
  455.  
  456.         $html['</ol>';
  457.  
  458.         return implode(''$html);
  459.     }
  460.  
  461.     /**
  462.      * Display profile information.
  463.      *
  464.      * @return  string 
  465.      *
  466.      * @since   2.5
  467.      */
  468.     protected function displayProfileInformation()
  469.     {
  470.         $html array();
  471.  
  472.         $htmlMarks array();
  473.  
  474.         $totalTime 0;
  475.         $totalMem 0;
  476.         $marks array();
  477.  
  478.         foreach (JProfiler::getInstance('Application')->getMarks(as $mark)
  479.         {
  480.             $totalTime += $mark->time;
  481.             $totalMem += $mark->memory;
  482.             $htmlMark sprintf(
  483.                 JText::_('PLG_DEBUG_TIME'': <span class="label label-time">%.1f&nbsp;ms</span> / <span class="label">%.1f&nbsp;ms</span>'
  484.                 . ' ' JText::_('PLG_DEBUG_MEMORY'': <span class="label label-memory">%0.3f MB</span> / <span class="label">%0.2f MB</span>'
  485.                 . ' %s: %s',
  486.                 $mark->time,
  487.                 $mark->totalTime,
  488.                 $mark->memory,
  489.                 $mark->totalMemory,
  490.                 $mark->prefix,
  491.                 $mark->label
  492.             );
  493.  
  494.             $marks[= (object) array(
  495.                 'time' => $mark->time,
  496.                 'memory' => $mark->memory,
  497.                 'html' => $htmlMark,
  498.                 'tip' => $mark->label
  499.             );
  500.         }
  501.  
  502.         $avgTime $totalTime count($marks);
  503.         $avgMem $totalMem count($marks);
  504.  
  505.         foreach ($marks as $mark)
  506.         {
  507.             if ($mark->time > $avgTime 1.5)
  508.             {
  509.                 $barClass 'bar-danger';
  510.                 $labelClass 'label-important';
  511.             }
  512.             elseif ($mark->time < $avgTime 1.5)
  513.             {
  514.                 $barClass 'bar-success';
  515.                 $labelClass 'label-success';
  516.             }
  517.             else
  518.             {
  519.                 $barClass 'bar-warning';
  520.                 $labelClass 'label-warning';
  521.             }
  522.  
  523.             if ($mark->memory $avgMem 1.5)
  524.             {
  525.                 $barClassMem 'bar-danger';
  526.                 $labelClassMem 'label-important';
  527.             }
  528.             elseif ($mark->memory $avgMem 1.5)
  529.             {
  530.                 $barClassMem 'bar-success';
  531.                 $labelClassMem 'label-success';
  532.             }
  533.             else
  534.             {
  535.                 $barClassMem 'bar-warning';
  536.                 $labelClassMem 'label-warning';
  537.             }
  538.  
  539.             $bars[= (object) array(
  540.                 'width' => round($mark->time / ($totalTime 100)4),
  541.                 'class' => $barClass,
  542.                 'tip' => $mark->tip
  543.             );
  544.  
  545.             $barsMem[= (object) array(
  546.                 'width' => round($mark->memory ($totalMem 100)4),
  547.                 'class' => $barClassMem,
  548.                 'tip' => $mark->tip
  549.             );
  550.  
  551.             $htmlMarks['<div>' str_replace('label-time'$labelClassstr_replace('label-memory'$labelClassMem$mark->html)) '</div>';
  552.         }
  553.  
  554.         $html['<h4>' JText::_('PLG_DEBUG_TIME''</h4>';
  555.         $html[$this->renderBars($bars'profile');
  556.         $html['<h4>' JText::_('PLG_DEBUG_MEMORY''</h4>';
  557.         $html[$this->renderBars($barsMem'profile');
  558.  
  559.         $html['<div class="dbg-profile-list">' implode(''$htmlMarks'</div>';
  560.  
  561.         $db JFactory::getDbo();
  562.  
  563.         $log $db->getLog();
  564.  
  565.         if ($log)
  566.         {
  567.             $timings $db->getTimings();
  568.  
  569.             if ($timings)
  570.             {
  571.                 $totalQueryTime 0.0;
  572.                 $lastStart null;
  573.  
  574.                 foreach ($timings as $k => $v)
  575.                 {
  576.                     if (!($k 2))
  577.                     {
  578.                         $lastStart $v;
  579.                     }
  580.                     else
  581.                     {
  582.                         $totalQueryTime += $v $lastStart;
  583.                     }
  584.                 }
  585.  
  586.                 $totalQueryTime $totalQueryTime 1000;
  587.  
  588.                 if ($totalQueryTime ($totalTime 0.25))
  589.                 {
  590.                     $labelClass 'label-important';
  591.                 }
  592.                 elseif ($totalQueryTime ($totalTime 0.15))
  593.                 {
  594.                     $labelClass 'label-success';
  595.                 }
  596.                 else
  597.                 {
  598.                     $labelClass 'label-warning';
  599.                 }
  600.  
  601.                 $html['<br /><div>' JText::sprintf(
  602.                         'PLG_DEBUG_QUERIES_TIME',
  603.                         sprintf('<span class="label ' $labelClass '">%.1f&nbsp;ms</span>'$totalQueryTime)
  604.                     '</div>';
  605.             }
  606.         }
  607.  
  608.         return implode(''$html);
  609.     }
  610.  
  611.     /**
  612.      * Display memory usage
  613.      *
  614.      * @return  string 
  615.      *
  616.      * @since   2.5
  617.      */
  618.     protected function displayMemoryUsage()
  619.     {
  620.         $bytes memory_get_usage();
  621.  
  622.         return '<span class="label">' JHtml::_('number.bytes'$bytes'</span>'
  623.             . ' (<span class="label">' number_format($bytes' ' JText::_('PLG_DEBUG_BYTES''</span>)';
  624.     }
  625.  
  626.     /**
  627.      * Display logged queries.
  628.      *
  629.      * @return  string 
  630.      *
  631.      * @since   2.5
  632.      */
  633.     protected function displayQueries()
  634.     {
  635.         $db JFactory::getDbo();
  636.  
  637.         $log $db->getLog();
  638.  
  639.         if (!$log)
  640.         {
  641.             return null;
  642.         }
  643.  
  644.         $timings $db->getTimings();
  645.         $callStacks $db->getCallStacks();
  646.  
  647.         $db->setDebug(false);
  648.  
  649.         $selectQueryTypeTicker array();
  650.         $otherQueryTypeTicker array();
  651.  
  652.         $timing array();
  653.         $maxtime 0;
  654.  
  655.         if (isset($timings[0]))
  656.         {
  657.             $startTime $timings[0];
  658.             $endTime $timings[count($timings1];
  659.             $totalBargraphTime $endTime $startTime;
  660.  
  661.             if ($totalBargraphTime 0)
  662.             {
  663.                 foreach ($log as $id => $query)
  664.                 {
  665.                     if (isset($timings[$id 1]))
  666.                     {
  667.                         // Compute the query time: $timing[$k] = array( queryTime, timeBetweenQueries ):
  668.                         $timing[$idarray(($timings[$id 1$timings[$id 2]1000$id ($timings[$id 2$timings[$id 1]1000 0);
  669.                         $maxtime max($maxtime$timing[$id]['0']);
  670.                     }
  671.                 }
  672.             }
  673.         }
  674.         else
  675.         {
  676.             $startTime null;
  677.             $totalBargraphTime 1;
  678.         }
  679.  
  680.         $bars array();
  681.         $info array();
  682.         $totalQueryTime 0;
  683.         $duplicates array();
  684.  
  685.         foreach ($log as $id => $query)
  686.         {
  687.             $did md5($query);
  688.  
  689.             if (!isset($duplicates[$did]))
  690.             {
  691.                 $duplicates[$didarray();
  692.             }
  693.  
  694.             $duplicates[$did][$id;
  695.  
  696.             if ($timings && isset($timings[$id 1]))
  697.             {
  698.                 // Compute the query time:
  699.                 $queryTime ($timings[$id 1$timings[$id 2]1000;
  700.                 $totalQueryTime += $queryTime;
  701.  
  702.                 // Run an EXPLAIN EXTENDED query on the SQL query if possible:
  703.                 $hasWarnings false;
  704.                 $hasWarningsInProfile false;
  705.  
  706.                 if (isset($this->explains[$id]))
  707.                 {
  708.                     $explain $this->tableToHtml($this->explains[$id]$hasWarnings);
  709.                 }
  710.                 else
  711.                 {
  712.                     $explain JText::sprintf('PLG_DEBUG_QUERY_EXPLAIN_NOT_POSSIBLE'htmlspecialchars($query));
  713.                 }
  714.  
  715.                 // Run a SHOW PROFILE query:
  716.                 $profile '';
  717.  
  718.                 if (in_array($db->namearray('mysqli''mysql')))
  719.                 {
  720.                     if (isset($this->sqlShowProfileEach[$id]))
  721.                     {
  722.                         $profileTable $this->sqlShowProfileEach[$id];
  723.                         $profile $this->tableToHtml($profileTable$hasWarningsInProfile);
  724.                     }
  725.                 }
  726.  
  727.                 $ratio 0.5// how heavy should the string length count: 0 - 1
  728.                 $timeScore $queryTime (strlen($query$ratio200;
  729.  
  730.                 // Determine color of bargraph depending on query speed and presence of warnings in EXPLAIN:
  731.                 if ($timeScore 10)
  732.                 {
  733.                     $barClass 'bar-danger';
  734.                     $labelClass 'label-important';
  735.                 }
  736.                 elseif ($hasWarnings || $timeScore 5)
  737.                 {
  738.                     $barClass 'bar-warning';
  739.                     $labelClass 'label-warning';
  740.                 }
  741.                 else
  742.                 {
  743.                     $barClass 'bar-success';
  744.                     $labelClass 'label-success';
  745.                 }
  746.  
  747.                 // Computes bargraph as follows: Position begin and end of the bar relatively to whole execution time:
  748.                 $prevBar ($id && isset($bars[$id 1])) $bars[$id 10;
  749.  
  750.                 $barPre round($timing[$id][1($totalBargraphTime 10)4);
  751.                 $barWidth round($timing[$id][0($totalBargraphTime 10)4);
  752.                 $minWidth 0.3;
  753.  
  754.                 if ($barWidth $minWidth)
  755.                 {
  756.                     $barPre -= ($minWidth $barWidth);
  757.  
  758.                     if ($barPre 0)
  759.                     {
  760.                         $minWidth += $barPre;
  761.                         $barPre 0;
  762.                     }
  763.  
  764.                     $barWidth $minWidth;
  765.                 }
  766.  
  767.                 $bars[$id= (object) array(
  768.                     'class' => $barClass,
  769.                     'width' => $barWidth,
  770.                     'pre' => $barPre,
  771.                     'tip' => sprintf('%.2f&nbsp;ms'$queryTime)
  772.                 );
  773.                 $info[$id= (object) array(
  774.                     'class' => $labelClass,
  775.                     'explain' => $explain,
  776.                     'profile' => $profile,
  777.                     'hasWarnings' => $hasWarnings
  778.                 );
  779.             }
  780.         }
  781.  
  782.         // Remove single queries from $duplicates
  783.         $total_duplicates 0;
  784.  
  785.         foreach ($duplicates as $did => $dups)
  786.         {
  787.             if (count($dups2)
  788.             {
  789.                 unset($duplicates[$did]);
  790.             }
  791.             else
  792.             {
  793.                 $total_duplicates += count($dups);
  794.             }
  795.         }
  796.  
  797.         // Fix first bar width
  798.         $minWidth 0.3;
  799.  
  800.         if ($bars[0]->width $minWidth && isset($bars[1]))
  801.         {
  802.             $bars[1]->pre -= ($minWidth $bars[0]->width);
  803.  
  804.             if ($bars[1]->pre 0)
  805.             {
  806.                 $minWidth += $bars[1]->pre;
  807.                 $bars[1]->pre 0;
  808.             }
  809.  
  810.             $bars[0]->width $minWidth;
  811.         }
  812.  
  813.         $memoryUsageNow memory_get_usage();
  814.         $list array();
  815.  
  816.         foreach ($log as $id => $query)
  817.         {
  818.             // Start Query Type Ticker Additions
  819.             $fromStart stripos($query'from');
  820.             $whereStart stripos($query'where'$fromStart);
  821.  
  822.             if ($whereStart === false)
  823.             {
  824.                 $whereStart stripos($query'order by'$fromStart);
  825.             }
  826.  
  827.             if ($whereStart === false)
  828.             {
  829.                 $whereStart strlen($query1;
  830.             }
  831.  
  832.             $fromString substr($query0$whereStart);
  833.             $fromString str_replace("\t"" "$fromString);
  834.             $fromString str_replace("\n"" "$fromString);
  835.             $fromString trim($fromString);
  836.  
  837.             // Initialize the select/other query type counts the first time:
  838.             if (!isset($selectQueryTypeTicker[$fromString]))
  839.             {
  840.                 $selectQueryTypeTicker[$fromString0;
  841.             }
  842.  
  843.             if (!isset($otherQueryTypeTicker[$fromString]))
  844.             {
  845.                 $otherQueryTypeTicker[$fromString0;
  846.             }
  847.  
  848.             // Increment the count:
  849.             if (stripos($query'select'=== 0)
  850.             {
  851.                 $selectQueryTypeTicker[$fromString$selectQueryTypeTicker[$fromString1;
  852.                 unset($otherQueryTypeTicker[$fromString]);
  853.             }
  854.             else
  855.             {
  856.                 $otherQueryTypeTicker[$fromString$otherQueryTypeTicker[$fromString1;
  857.                 unset($selectQueryTypeTicker[$fromString]);
  858.             }
  859.  
  860.             $text $this->highlightQuery($query);
  861.  
  862.             if ($timings && isset($timings[$id 1]))
  863.             {
  864.                 // Compute the query time:
  865.                 $queryTime ($timings[$id 1$timings[$id 2]1000;
  866.  
  867.                 // Timing
  868.                 // Formats the output for the query time with EXPLAIN query results as tooltip:
  869.                 $htmlTiming '<div style="margin: 0px 0 5px;"><span class="dbg-query-time">' JText::sprintf('PLG_DEBUG_QUERY_TIME'sprintf('<span class="label ' $info[$id]->class '">%.2f&nbsp;ms</span>'$timing[$id]['0']));
  870.  
  871.                 if ($timing[$id]['1'])
  872.                 {
  873.                     $htmlTiming .= ' ' JText::sprintf('PLG_DEBUG_QUERY_AFTER_LAST'sprintf('<span class="label">%.2f&nbsp;ms</span>'$timing[$id]['1']));
  874.                 }
  875.  
  876.                 $htmlTiming .= '</span>';
  877.  
  878.                 if (isset($callStacks[$id][0]['memory']))
  879.                 {
  880.                     $memoryUsed $callStacks[$id][0]['memory'][1$callStacks[$id][0]['memory'][0];
  881.                     $memoryBeforeQuery $callStacks[$id][0]['memory'][0];
  882.  
  883.                     // Determine color of query memory usage:
  884.                     if ($memoryUsed 0.1 $memoryUsageNow)
  885.                     {
  886.                         $labelClass 'label-important';
  887.                     }
  888.                     elseif ($memoryUsed 0.05 $memoryUsageNow)
  889.                     {
  890.                         $labelClass 'label-warning';
  891.                     }
  892.                     else
  893.                     {
  894.                         $labelClass 'label-success';
  895.                     }
  896.  
  897.                     $htmlTiming .= ' ' '<span class="dbg-query-memory">' JText::sprintf('PLG_DEBUG_MEMORY_USED_FOR_QUERY',
  898.                             sprintf('<span class="label ' $labelClass '">%.3f&nbsp;MB</span>'$memoryUsed 1048576),
  899.                             sprintf('<span class="label">%.3f&nbsp;MB</span>'$memoryBeforeQuery 1048576)
  900.                         )
  901.                         . '</span>';
  902.  
  903.                     if ($callStacks[$id][0]['memory'][2!== null)
  904.                     {
  905.                         // Determine color of number or results:
  906.                         $resultsReturned $callStacks[$id][0]['memory'][2];
  907.  
  908.                         if ($resultsReturned 3000)
  909.                         {
  910.                             $labelClass 'label-important';
  911.                         }
  912.                         elseif ($resultsReturned 1000)
  913.                         {
  914.                             $labelClass 'label-warning';
  915.                         }
  916.                         elseif ($resultsReturned == 0)
  917.                         {
  918.                             $labelClass '';
  919.                         }
  920.                         else
  921.                         {
  922.                             $labelClass 'label-success';
  923.                         }
  924.  
  925.                         $htmlResultsReturned '<span class="label ' $labelClass '">' . (int) $resultsReturned '</span>';
  926.                         $htmlTiming .= ' ' '<span class="dbg-query-rowsnumber">' JText::sprintf('PLG_DEBUG_ROWS_RETURNED_BY_QUERY'$htmlResultsReturned'</span>';
  927.                     }
  928.                 }
  929.  
  930.                 $htmlTiming .= '</div>';
  931.  
  932.                 // Bar
  933.                 $htmlBar $this->renderBars($bars'query'$id);
  934.  
  935.                 // Profile Query
  936.                 $title JText::_('PLG_DEBUG_PROFILE');
  937.  
  938.                 if (!$info[$id]->profile)
  939.                 {
  940.                     $title '<span class="dbg-noprofile">' $title '</span>';
  941.                 }
  942.  
  943.                 $htmlProfile ($info[$id]->profile $info[$id]->profile JText::_('PLG_DEBUG_NO_PROFILE'));
  944.  
  945.                 // Backtrace/Called
  946.                 $htmlCallStack '';
  947.  
  948.                 if (isset($callStacks[$id]))
  949.                 {
  950.                     $htmlCallStackElements array();
  951.  
  952.                     foreach ($callStacks[$idas $functionCall)
  953.                     {
  954.                         if (isset($functionCall['file']&& isset($functionCall['line']&& (strpos($functionCall['file']'/libraries/joomla/database/'=== false))
  955.                         {
  956.                             $htmlFile htmlspecialchars($functionCall['file']);
  957.                             $htmlLine htmlspecialchars($functionCall['line']);
  958.                             $htmlCallStackElements['<span class="dbg-log-called-from">' $this->formatLink($htmlFile$htmlLine'</span>';
  959.                         }
  960.                     }
  961.  
  962.                     $htmlCallStack '<div class="dbg-query-table"><div>' implode('</div><div>'$htmlCallStackElements'</div></div>';
  963.  
  964.                     if (!$this->linkFormat)
  965.                     {
  966.                         $htmlCallStack .= '<div>[<a href="http://xdebug.org/docs/all_settings#file_link_format" target="_blank">' JText::_('PLG_DEBUG_LINK_FORMAT''</a>]</div>';
  967.                     }
  968.                 }
  969.  
  970.                 $htmlAccordions JHtml::_(
  971.                     'bootstrap.startAccordion''dbg_query_' $idarray(
  972.                         'active' => ($info[$id]->hasWarnings ('dbg_query_explain_' $id'')
  973.                     )
  974.                 );
  975.  
  976.                 $htmlAccordions .= JHtml::_('bootstrap.addSlide''dbg_query_' $idJText::_('PLG_DEBUG_EXPLAIN')'dbg_query_explain_' $id)
  977.                     . $info[$id]->explain
  978.                     . JHtml::_('bootstrap.endSlide');
  979.  
  980.                 $htmlAccordions .= JHtml::_('bootstrap.addSlide''dbg_query_' $id$title'dbg_query_profile_' $id)
  981.                     . $htmlProfile
  982.                     . JHtml::_('bootstrap.endSlide');
  983.  
  984.                 if ($htmlCallStack)
  985.                 {
  986.                     $htmlAccordions .= JHtml::_('bootstrap.addSlide''dbg_query_' $idJText::_('PLG_DEBUG_CALL_STACK')'dbg_query_callstack_' $id)
  987.                         . $htmlCallStack
  988.                         . JHtml::_('bootstrap.endSlide');
  989.                 }
  990.  
  991.                 $htmlAccordions .= JHtml::_('bootstrap.endAccordion');
  992.  
  993.                 $did md5($query);
  994.  
  995.                 if (isset($duplicates[$did]))
  996.                 {
  997.                     $dups array();
  998.  
  999.                     foreach ($duplicates[$didas $dup)
  1000.                     {
  1001.                         if ($dup != $id)
  1002.                         {
  1003.                             $dups['<a href="#dbg-query-' ($dup 1'">#' ($dup 1'</a>';
  1004.                         }
  1005.                     }
  1006.  
  1007.                     $htmlQuery '<div class="alert alert-error">' JText::_('PLG_DEBUG_QUERY_DUPLICATES'': ' implode('&nbsp; '$dups'</div>'
  1008.                         . '<pre class="alert hasTooltip" title="' JHtml::tooltipText('PLG_DEBUG_QUERY_DUPLICATES_FOUND''">' $text '</pre>';
  1009.                 }
  1010.                 else
  1011.                 {
  1012.                     $htmlQuery '<pre>' $text '</pre>';
  1013.                 }
  1014.  
  1015.                 $list['<a name="dbg-query-' ($id 1'"></a>'
  1016.                     . $htmlTiming
  1017.                     . $htmlBar
  1018.                     . $htmlQuery
  1019.                     . $htmlAccordions;
  1020.             }
  1021.             else
  1022.             {
  1023.                 $list['<pre>' $text '</pre>';
  1024.             }
  1025.         }
  1026.  
  1027.         $totalTime 0;
  1028.  
  1029.         foreach (JProfiler::getInstance('Application')->getMarks(as $mark)
  1030.         {
  1031.             $totalTime += $mark->time;
  1032.         }
  1033.  
  1034.         if ($totalQueryTime ($totalTime 0.25))
  1035.         {
  1036.             $labelClass 'label-important';
  1037.         }
  1038.         elseif ($totalQueryTime ($totalTime 0.15))
  1039.         {
  1040.             $labelClass 'label-success';
  1041.         }
  1042.         else
  1043.         {
  1044.             $labelClass 'label-warning';
  1045.         }
  1046.  
  1047.         $html array();
  1048.  
  1049.         $html['<h4>' JText::sprintf('PLG_DEBUG_QUERIES_LOGGED'$db->getCount())
  1050.             . sprintf(' <span class="label ' $labelClass '">%.1f&nbsp;ms</span>'($totalQueryTime)) '</h4><br />';
  1051.  
  1052.         if ($total_duplicates)
  1053.         {
  1054.             $html['<div class="alert alert-error">'
  1055.                 . '<h4>' JText::sprintf('PLG_DEBUG_QUERY_DUPLICATES_TOTAL_NUMBER'$total_duplicates'</h4>';
  1056.  
  1057.             foreach ($duplicates as $dups)
  1058.             {
  1059.                 $links array();
  1060.  
  1061.                 foreach ($dups as $dup)
  1062.                 {
  1063.                     $links['<a href="#dbg-query-' ($dup 1'">#' ($dup 1'</a>';
  1064.                 }
  1065.  
  1066.                 $html['<div>' JText::sprintf('PLG_DEBUG_QUERY_DUPLICATES_NUMBER'count($links)) ': ' implode('&nbsp; '$links'</div>';
  1067.             }
  1068.  
  1069.             $html['</div>';
  1070.         }
  1071.  
  1072.         $html['<ol><li>' implode('<hr /></li><li>'$list'<hr /></li></ol>';
  1073.  
  1074.         if (!$this->params->get('query_types'1))
  1075.         {
  1076.             return implode(''$html);
  1077.         }
  1078.  
  1079.         // Get the totals for the query types:
  1080.         $totalSelectQueryTypes count($selectQueryTypeTicker);
  1081.         $totalOtherQueryTypes count($otherQueryTypeTicker);
  1082.         $totalQueryTypes $totalSelectQueryTypes $totalOtherQueryTypes;
  1083.  
  1084.         $html['<h4>' JText::sprintf('PLG_DEBUG_QUERY_TYPES_LOGGED'$totalQueryTypes'</h4>';
  1085.  
  1086.         if ($totalSelectQueryTypes)
  1087.         {
  1088.             $html['<h5>' JText::_('PLG_DEBUG_SELECT_QUERIES''</h5>';
  1089.  
  1090.             arsort($selectQueryTypeTicker);
  1091.  
  1092.             $list array();
  1093.  
  1094.             foreach ($selectQueryTypeTicker as $query => $occurrences)
  1095.             {
  1096.                 $list['<pre>'
  1097.                     . JText::sprintf('PLG_DEBUG_QUERY_TYPE_AND_OCCURRENCES'$this->highlightQuery($query)$occurrences)
  1098.                     . '</pre>';
  1099.             }
  1100.  
  1101.             $html['<ol><li>' implode('</li><li>'$list'</li></ol>';
  1102.         }
  1103.  
  1104.         if ($totalOtherQueryTypes)
  1105.         {
  1106.             $html['<h5>' JText::_('PLG_DEBUG_OTHER_QUERIES''</h5>';
  1107.  
  1108.             arsort($otherQueryTypeTicker);
  1109.  
  1110.             $list array();
  1111.  
  1112.             foreach ($otherQueryTypeTicker as $query => $occurrences)
  1113.             {
  1114.                 $list['<pre>'
  1115.                     . JText::sprintf('PLG_DEBUG_QUERY_TYPE_AND_OCCURRENCES'$this->highlightQuery($query)$occurrences)
  1116.                     . '</pre>';
  1117.             }
  1118.  
  1119.             $html['<ol><li>' implode('</li><li>'$list'</li></ol>';
  1120.         }
  1121.  
  1122.         return implode(''$html);
  1123.     }
  1124.  
  1125.     /**
  1126.      * @param   array    &$bars  Array of bar data
  1127.      * @param   string   $class  Optional class for items
  1128.      * @param   integer  $id     Id if the bar to highlight
  1129.      *
  1130.      * @return  string 
  1131.      *
  1132.      * @since   3.1.2
  1133.      */
  1134.     protected function renderBars(&$bars$class ''$id null)
  1135.     {
  1136.         $html array();
  1137.  
  1138.         foreach ($bars as $i => $bar)
  1139.         {
  1140.             if (isset($bar->pre&& $bar->pre)
  1141.             {
  1142.                 $html['<div class="dbg-bar-spacer" style="width:' $bar->pre '%;"></div>';
  1143.             }
  1144.  
  1145.             $barClass trim('bar dbg-bar ' (isset($bar->class$bar->class ''));
  1146.  
  1147.             if ($id !== null && $i == $id)
  1148.             {
  1149.                 $barClass .= ' dbg-bar-active';
  1150.             }
  1151.  
  1152.             $tip '';
  1153.  
  1154.             if (isset($bar->tip&& $bar->tip)
  1155.             {
  1156.                 $barClass .= ' hasTooltip';
  1157.                 $tip JHtml::tooltipText($bar->tip''0);
  1158.             }
  1159.  
  1160.             $html['<a class="bar dbg-bar ' $barClass '" title="' $tip '" style="width: ' $bar->width '%;" href="#dbg-' $class '-' ($i 1'"></a>';
  1161.         }
  1162.  
  1163.         return '<div class="progress dbg-bars dbg-bars-' $class '">' implode(''$html'</div>';
  1164.     }
  1165.  
  1166.     /**
  1167.      * @param   array    $table 
  1168.      * @param   boolean  $hasWarnings  Changes value to true if warnings are displayed, otherwise untouched
  1169.      *
  1170.      * @return  string 
  1171.      *
  1172.      * @since   3.1.2
  1173.      */
  1174.     protected function tableToHtml($table&$hasWarnings)
  1175.     {
  1176.         if (!$table)
  1177.         {
  1178.             return null;
  1179.         }
  1180.  
  1181.         $html array();
  1182.  
  1183.         $html['<table class="table table-striped dbg-query-table"><tr>';
  1184.  
  1185.         foreach (array_keys($table[0]as $k)
  1186.         {
  1187.             $html['<th>' htmlspecialchars($k'</th>';
  1188.         }
  1189.  
  1190.         $html['</tr>';
  1191.  
  1192.         $durations array();
  1193.  
  1194.         foreach ($table as $tr)
  1195.         {
  1196.             if (isset($tr['Duration']))
  1197.             {
  1198.                 $durations[$tr['Duration'];
  1199.             }
  1200.         }
  1201.  
  1202.         rsort($durationsSORT_NUMERIC);
  1203.  
  1204.         foreach ($table as $tr)
  1205.         {
  1206.             $html['<tr>';
  1207.  
  1208.             foreach ($tr as $k => $td)
  1209.             {
  1210.                 if ($td === null)
  1211.                 {
  1212.                     // Display null's as 'NULL':
  1213.                     $td 'NULL';
  1214.                 }
  1215.  
  1216.                 // Treat special columns:
  1217.                 if ($k == 'Duration')
  1218.                 {
  1219.                     if ($td >= 0.001 && ($td == $durations[0|| (isset($durations[1]&& $td == $durations[1])))
  1220.                     {
  1221.                         // Duration column with duration value of more than 1 ms and within 2 top duration in SQL engine: Highlight warning:
  1222.                         $html['<td class="dbg-warning">';
  1223.                         $hasWarnings true;
  1224.                     }
  1225.                     else
  1226.                     {
  1227.                         $html['<td>';
  1228.                     }
  1229.  
  1230.                     // Display duration in ms with the unit instead of seconds:
  1231.                     $html[sprintf('%.1f&nbsp;ms'$td 1000);
  1232.                 }
  1233.                 elseif ($k == 'Error')
  1234.                 {
  1235.                     // An error in the EXPLAIN query occured, display it instead of the result (means original query had syntax error most probably):
  1236.                     $html['<td class="dbg-warning">' htmlspecialchars($td);
  1237.                     $hasWarnings true;
  1238.                 }
  1239.                 elseif ($k == 'key')
  1240.                 {
  1241.                     if ($td === 'NULL')
  1242.                     {
  1243.                         // Displays query parts which don't use a key with warning:
  1244.                         $html['<td><strong>' '<span class="dbg-warning hasTooltip" title="' JHtml::tooltipText('PLG_DEBUG_WARNING_NO_INDEX_DESC''">' JText::_('PLG_DEBUG_WARNING_NO_INDEX''</span>' '</strong>';
  1245.                         $hasWarnings true;
  1246.                     }
  1247.                     else
  1248.                     {
  1249.                         $html['<td><strong>' htmlspecialchars($td'</strong>';
  1250.                     }
  1251.                 }
  1252.                 elseif ($k == 'Extra')
  1253.                 {
  1254.                     $htmlTd htmlspecialchars($td);
  1255.  
  1256.                     // Replace spaces with nbsp for less tall tables displayed:
  1257.                     $htmlTd preg_replace('/([^;]) /''\1&nbsp;'$htmlTd);
  1258.  
  1259.                     // Displays warnings for "Using filesort":
  1260.                     $htmlTdWithWarnings str_replace('Using&nbsp;filesort''<span class="dbg-warning hasTooltip" title="' JHtml::tooltipText('PLG_DEBUG_WARNING_USING_FILESORT_DESC''">' JText::_('PLG_DEBUG_WARNING_USING_FILESORT''</span>'$htmlTd);
  1261.  
  1262.                     if ($htmlTdWithWarnings !== $htmlTd)
  1263.                     {
  1264.                         $hasWarnings true;
  1265.                     }
  1266.  
  1267.                     $html['<td>' $htmlTdWithWarnings;
  1268.                 }
  1269.                 else
  1270.                 {
  1271.                     $html['<td>' htmlspecialchars($td);
  1272.                 }
  1273.  
  1274.                 $html['</td>';
  1275.             }
  1276.  
  1277.             $html['</tr>';
  1278.         }
  1279.  
  1280.         $html['</table>';
  1281.  
  1282.         return implode(''$html);
  1283.     }
  1284.  
  1285.     /**
  1286.      * Disconnect-handler for database to collect profiling and explain information
  1287.      *
  1288.      * @param   JDatabaseDriver  &$db  Database object
  1289.      *
  1290.      * @return  void 
  1291.      *
  1292.      * @since   3.1.2
  1293.      */
  1294.     public function mysqlDisconnectHandler(&$db)
  1295.     {
  1296.         $db->setDebug(false);
  1297.  
  1298.         $dbVersion5037 (strncmp($db->name'mysql'5== 0&& version_compare($db->getVersion()'5.0.37''>=');
  1299.  
  1300.         if ($dbVersion5037)
  1301.         {
  1302.             try
  1303.             {
  1304.                 // Check if profiling is enabled:
  1305.                 $db->setQuery("SHOW VARIABLES LIKE 'have_profiling'");
  1306.                 $hasProfiling $db->loadResult();
  1307.  
  1308.                 if ($hasProfiling)
  1309.                 {
  1310.                     // Run a SHOW PROFILE query:
  1311.                     $db->setQuery('SHOW PROFILES');
  1312.                     $this->sqlShowProfiles $db->loadAssocList();
  1313.  
  1314.                     if ($this->sqlShowProfiles)
  1315.                     {
  1316.                         foreach ($this->sqlShowProfiles as $qn)
  1317.                         {
  1318.                             // Run SHOW PROFILE FOR QUERY for each query where a profile is available (max 100):
  1319.                             $db->setQuery('SHOW PROFILE FOR QUERY ' . (int) ($qn['Query_ID']));
  1320.                             $this->sqlShowProfileEach[(int) ($qn['Query_ID'1)$db->loadAssocList();
  1321.                         }
  1322.                     }
  1323.                 }
  1324.                 else
  1325.                 {
  1326.                     $this->sqlShowProfileEach[0array(array('Error' => 'MySql have_profiling = off'));
  1327.                 }
  1328.             }
  1329.             catch (Exception $e{
  1330.                 $this->sqlShowProfileEach[0array(array('Error' => $e->getMessage()));
  1331.             }
  1332.         }
  1333.  
  1334.         if (in_array($db->namearray('mysqli''mysql''postgresql')))
  1335.         {
  1336.             $log $db->getLog();
  1337.  
  1338.             foreach ($log as $k => $query)
  1339.             {
  1340.                 $dbVersion56 (strncmp($db->name'mysql'5== 0&& version_compare($db->getVersion()'5.6''>=');
  1341.  
  1342.                 if ((stripos($query'select'=== 0|| ($dbVersion56 && ((stripos($query'delete'=== 0|| (stripos($query'update'=== 0))))
  1343.                 {
  1344.                     try
  1345.                     {
  1346.                         $db->setQuery('EXPLAIN ' ($dbVersion56 'EXTENDED ' ''$query);
  1347.                         $this->explains[$k$db->loadAssocList();
  1348.                     }
  1349.                     catch (Exception $e{
  1350.                         $this->explains[$karray(array('Error' => $e->getMessage()));
  1351.                     }
  1352.                 }
  1353.             }
  1354.         }
  1355.     }
  1356.  
  1357.     /**
  1358.      * Displays errors in language files.
  1359.      *
  1360.      * @return  string 
  1361.      *
  1362.      * @since   2.5
  1363.      */
  1364.     protected function displayLanguageFilesInError()
  1365.     {
  1366.         $errorfiles JFactory::getLanguage()->getErrorFiles();
  1367.  
  1368.         if (!count($errorfiles))
  1369.         {
  1370.             return '<p>' JText::_('JNONE''</p>';
  1371.         }
  1372.  
  1373.         $html array();
  1374.  
  1375.         $html['<ul>';
  1376.  
  1377.         foreach ($errorfiles as $file => $error)
  1378.         {
  1379.             $html['<li>' $this->formatLink($filestr_replace($file''$error'</li>';
  1380.         }
  1381.  
  1382.         $html['</ul>';
  1383.  
  1384.         return implode(''$html);
  1385.     }
  1386.  
  1387.     /**
  1388.      * Display loaded language files.
  1389.      *
  1390.      * @return  string 
  1391.      *
  1392.      * @since   2.5
  1393.      */
  1394.     protected function displayLanguageFilesLoaded()
  1395.     {
  1396.         $html array();
  1397.  
  1398.         $html['<ul>';
  1399.  
  1400.         foreach (JFactory::getLanguage()->getPaths(as /* $extension => */ $files)
  1401.         {
  1402.             foreach ($files as $file => $status)
  1403.             {
  1404.                 $html['<li>';
  1405.  
  1406.                 $html[($status)
  1407.                     ? JText::_('PLG_DEBUG_LANG_LOADED')
  1408.                     : JText::_('PLG_DEBUG_LANG_NOT_LOADED');
  1409.  
  1410.                 $html[' : ';
  1411.                 $html[$this->formatLink($file);
  1412.                 $html['</li>';
  1413.             }
  1414.         }
  1415.  
  1416.         $html['</ul>';
  1417.  
  1418.         return implode(''$html);
  1419.     }
  1420.  
  1421.     /**
  1422.      * Display untranslated language strings.
  1423.      *
  1424.      * @return  string 
  1425.      *
  1426.      * @since   2.5
  1427.      */
  1428.     protected function displayUntranslatedStrings()
  1429.     {
  1430.         $stripFirst $this->params->get('strip-first');
  1431.         $stripPref $this->params->get('strip-prefix');
  1432.         $stripSuff $this->params->get('strip-suffix');
  1433.  
  1434.         $orphans JFactory::getLanguage()->getOrphans();
  1435.  
  1436.         if (!count($orphans))
  1437.         {
  1438.             return '<p>' JText::_('JNONE''</p>';
  1439.         }
  1440.  
  1441.         ksort($orphansSORT_STRING);
  1442.  
  1443.         $guesses array();
  1444.  
  1445.         foreach ($orphans as $key => $occurance)
  1446.         {
  1447.             if (is_array($occurance&& isset($occurance[0]))
  1448.             {
  1449.                 $info $occurance[0];
  1450.                 $file ($info['file']$info['file''';
  1451.  
  1452.                 if (!isset($guesses[$file]))
  1453.                 {
  1454.                     $guesses[$filearray();
  1455.                 }
  1456.  
  1457.                 // Prepare the key
  1458.                 if (($pos strpos($info['string']'=')) 0)
  1459.                 {
  1460.                     $parts explode('='$info['string']);
  1461.                     $key $parts[0];
  1462.                     $guess $parts[1];
  1463.                 }
  1464.                 else
  1465.                 {
  1466.                     $guess str_replace('_'' '$info['string']);
  1467.  
  1468.                     if ($stripFirst)
  1469.                     {
  1470.                         $parts explode(' '$guess);
  1471.  
  1472.                         if (count($parts1)
  1473.                         {
  1474.                             array_shift($parts);
  1475.                             $guess implode(' '$parts);
  1476.                         }
  1477.                     }
  1478.  
  1479.                     $guess trim($guess);
  1480.  
  1481.                     if ($stripPref)
  1482.                     {
  1483.                         $guess trim(preg_replace(chr(1'^' $stripPref chr(1'i'''$guess));
  1484.                     }
  1485.  
  1486.                     if ($stripSuff)
  1487.                     {
  1488.                         $guess trim(preg_replace(chr(1$stripSuff '$' chr(1'i'''$guess));
  1489.                     }
  1490.                 }
  1491.  
  1492.                 $key trim(strtoupper($key));
  1493.                 $key preg_replace('#\s+#''_'$key);
  1494.                 $key preg_replace('#\W#'''$key);
  1495.  
  1496.                 // Prepare the text
  1497.                 $guesses[$file][$key '="' $guess '"';
  1498.             }
  1499.         }
  1500.  
  1501.         $html array();
  1502.  
  1503.         foreach ($guesses as $file => $keys)
  1504.         {
  1505.             $html["\n\n# " ($file $this->formatLink($fileJText::_('PLG_DEBUG_UNKNOWN_FILE')) "\n\n";
  1506.             $html[implode("\n"$keys);
  1507.         }
  1508.  
  1509.         return '<pre>' implode(''$html'</pre>';
  1510.     }
  1511.  
  1512.     /**
  1513.      * Simple highlight for SQL queries.
  1514.      *
  1515.      * @param   string  $query  The query to highlight
  1516.      *
  1517.      * @return  string 
  1518.      *
  1519.      * @since   2.5
  1520.      */
  1521.     protected function highlightQuery($query)
  1522.     {
  1523.         $newlineKeywords '#\b(FROM|LEFT|INNER|OUTER|WHERE|SET|VALUES|ORDER|GROUP|HAVING|LIMIT|ON|AND|CASE)\b#i';
  1524.  
  1525.         $query htmlspecialchars($queryENT_QUOTES);
  1526.  
  1527.         $query preg_replace($newlineKeywords'<br />&#160;&#160;\\0'$query);
  1528.  
  1529.         $regex array(
  1530.  
  1531.             // Tables are identified by the prefix
  1532.             '/(=)/'
  1533.             => '<b class="dbg-operator">$1</b>',
  1534.  
  1535.             // All uppercase words have a special meaning
  1536.             '/(?<!\w|>)([A-Z_]{2,})(?!\w)/x'
  1537.             => '<span class="dbg-command">$1</span>',
  1538.  
  1539.             // Tables are identified by the prefix
  1540.             '/(' JFactory::getDbo()->getPrefix('[a-z_0-9]+)/'
  1541.             => '<span class="dbg-table">$1</span>'
  1542.  
  1543.         );
  1544.  
  1545.         $query preg_replace(array_keys($regex)array_values($regex)$query);
  1546.  
  1547.         $query str_replace('*''<b style="color: red;">*</b>'$query);
  1548.  
  1549.         return $query;
  1550.     }
  1551.  
  1552.     /**
  1553.      * Render the backtrace.
  1554.      *
  1555.      * Stolen from JError to prevent it's removal.
  1556.      *
  1557.      * @param   Exception  $error  The error
  1558.      *
  1559.      * @return  string     Contents of the backtrace
  1560.      *
  1561.      * @since   2.5
  1562.      */
  1563.     protected function renderBacktrace($error)
  1564.     {
  1565.         $backtrace $error->getTrace();
  1566.  
  1567.         $html array();
  1568.  
  1569.         if (is_array($backtrace))
  1570.         {
  1571.             $j 1;
  1572.  
  1573.             $html['<table cellpadding="0" cellspacing="0">';
  1574.  
  1575.             $html['<tr>';
  1576.             $html['<td colspan="3"><strong>Call stack</strong></td>';
  1577.             $html['</tr>';
  1578.  
  1579.             $html['<tr>';
  1580.             $html['<th>#</th>';
  1581.             $html['<th>Function</th>';
  1582.             $html['<th>Location</th>';
  1583.             $html['</tr>';
  1584.  
  1585.             for ($i count($backtrace1$i >= 0$i--)
  1586.             {
  1587.                 $link '&#160;';
  1588.  
  1589.                 if (isset($backtrace[$i]['file']))
  1590.                 {
  1591.                     $link $this->formatLink($backtrace[$i]['file']$backtrace[$i]['line']);
  1592.                 }
  1593.  
  1594.                 $html['<tr>';
  1595.                 $html['<td>' $j '</td>';
  1596.  
  1597.                 if (isset($backtrace[$i]['class']))
  1598.                 {
  1599.                     $html['<td>' $backtrace[$i]['class'$backtrace[$i]['type'$backtrace[$i]['function''()</td>';
  1600.                 }
  1601.                 else
  1602.                 {
  1603.                     $html['<td>' $backtrace[$i]['function''()</td>';
  1604.                 }
  1605.  
  1606.                 $html['<td>' $link '</td>';
  1607.  
  1608.                 $html['</tr>';
  1609.                 $j++;
  1610.             }
  1611.  
  1612.             $html['</table>';
  1613.         }
  1614.  
  1615.         return implode(''$html);
  1616.     }
  1617.  
  1618.     /**
  1619.      * Replaces the Joomla! root with "JROOT" to improve readability.
  1620.      * Formats a link with a special value xdebug.file_link_format
  1621.      * from the php.ini file.
  1622.      *
  1623.      * @param   string  $file  The full path to the file.
  1624.      * @param   string  $line  The line number.
  1625.      *
  1626.      * @return  string 
  1627.      *
  1628.      * @since   2.5
  1629.      */
  1630.     protected function formatLink($file$line '')
  1631.     {
  1632.         $link str_replace(JPATH_ROOT'JROOT'$file);
  1633.         $link .= ($line':' $line '';
  1634.  
  1635.         if ($this->linkFormat)
  1636.         {
  1637.             $href $this->linkFormat;
  1638.             $href str_replace('%f'$file$href);
  1639.             $href str_replace('%l'$line$href);
  1640.  
  1641.             $html '<a href="' $href '">' $link '</a>';
  1642.         }
  1643.         else
  1644.         {
  1645.             $html $link;
  1646.         }
  1647.  
  1648.         return $html;
  1649.     }
  1650.  
  1651.     /**
  1652.      * Store log messages so they can be displayed later.
  1653.      * This function is passed log entries by JLogLoggerCallback.
  1654.      *
  1655.      * @param   JLogEntry  $entry  A log entry.
  1656.      *
  1657.      * @since   3.1
  1658.      */
  1659.     public function logger(JLogEntry $entry)
  1660.     {
  1661.         $this->logEntries[$entry;
  1662.     }
  1663.  
  1664.     /**
  1665.      * Display log messages
  1666.      *
  1667.      * @return  string 
  1668.      *
  1669.      * @since   3.1
  1670.      */
  1671.     protected function displayLogs()
  1672.     {
  1673.         $priorities array(
  1674.             JLog::EMERGENCY => 'EMERGENCY',
  1675.             JLog::ALERT => 'ALERT',
  1676.             JLog::CRITICAL => 'CRITICAL',
  1677.             JLog::ERROR => 'ERROR',
  1678.             JLog::WARNING => 'WARNING',
  1679.             JLog::NOTICE => 'NOTICE',
  1680.             JLog::INFO => 'INFO',
  1681.             JLog::DEBUG => 'DEBUG'
  1682.         );
  1683.  
  1684.         $out array();
  1685.  
  1686.         foreach ($this->logEntries as $entry)
  1687.         {
  1688.             $out['<h5>' $priorities[$entry->priority' - ' $entry->category ' </h5><code>' $entry->message '</code>';
  1689.         }
  1690.  
  1691.         return implode('<br /><br />'$out);
  1692.     }
  1693. }

Documentation generated on Tue, 19 Nov 2013 14:58:03 +0100 by phpDocumentor 1.4.3