| [ Index ] |
PHP Cross Reference of Joomla 2.5.4 DE |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved. 4 * @license GNU General Public License version 2 or later; see LICENSE.txt 5 */ 6 7 // no direct access 8 defined('_JEXEC') or die; 9 10 /** 11 * Joomla! Debug plugin 12 * 13 * @package Joomla.Plugin 14 * @subpackage System.debug 15 */ 16 class plgSystemDebug extends JPlugin 17 { 18 protected $linkFormat = ''; 19 20 /** 21 * Constructor. 22 * 23 * @param object &$subject The object to observe 24 * @param array $config An array that holds the plugin configuration 25 * 26 * @since 1.5 27 */ 28 public function __construct(&$subject, $config) 29 { 30 parent::__construct($subject, $config); 31 32 // Log the deprecated API. 33 if ($this->params->get('log-deprecated')) 34 { 35 JLog::addLogger(array('text_file' => 'deprecated.php'), JLog::ALL, array('deprecated')); 36 } 37 38 // Only if debugging or language debug is enabled 39 if (JDEBUG || JFactory::getApplication()->getCfg('debug_lang')) 40 { 41 JFactory::getConfig()->set('gzip', 0); 42 ob_start(); 43 ob_implicit_flush(false); 44 } 45 46 $this->linkFormat = ini_get('xdebug.file_link_format'); 47 } 48 49 /** 50 * Add the CSS for debug. We can't do this in the constructor because 51 * stuff breaks. 52 */ 53 public function onAfterDispatch() 54 { 55 // Only if debugging or language debug is enabled 56 if (JDEBUG || JFactory::getApplication()->getCfg('debug_lang')) 57 { 58 JHtml::_('stylesheet', 'cms/debug.css', array(), true); 59 } 60 } 61 62 /** 63 * Show the debug info 64 */ 65 public function __destruct() 66 { 67 // Do not render if debugging or language debug is not enabled 68 if (!JDEBUG 69 && ! JFactory::getApplication()->getCfg('debug_lang')) 70 { 71 return; 72 } 73 74 // Load the language 75 $this->loadLanguage(); 76 77 // Capture output 78 $contents = ob_get_contents(); 79 ob_end_clean(); 80 81 // No debug for Safari and Chrome redirection 82 if (strstr(strtolower($_SERVER['HTTP_USER_AGENT']), 'webkit') !== false 83 && substr($contents, 0, 50) == '<html><head><meta http-equiv="refresh" content="0;') 84 { 85 echo $contents; 86 return; 87 } 88 89 // Only render for HTML output 90 if ('html' !== JFactory::getDocument()->getType()) 91 { 92 echo $contents; 93 return; 94 } 95 96 // If the user is not allowed to view the output then end here 97 $filterGroups = (array) $this->params->get('filter_groups', null); 98 99 if (!empty($filterGroups)) 100 { 101 $userGroups = JFactory::getUser()->get('groups'); 102 103 if (!array_intersect($filterGroups, $userGroups)) 104 { 105 echo $contents; 106 return; 107 } 108 } 109 110 // Load language file 111 $this->loadLanguage('plg_system_debug'); 112 113 $html = ''; 114 115 // Some "mousewheel protecting" JS 116 $html .= "<script>function toggleContainer(name) { 117 var e = document.getElementById(name);// MooTools might not be available ;) 118 e.style.display =(e.style.display == 'none') ? 'block' : 'none'; 119 }</script>"; 120 121 $html .= '<div id="system-debug" class="profiler">'; 122 123 $html .= '<h1>'.JText::_('PLG_DEBUG_TITLE').'</h1>'; 124 125 if (JDEBUG) 126 { 127 if (JError::getErrors()) 128 { 129 $html .= $this->display('errors'); 130 } 131 132 //$html .= print_r($data[$l], 1); 133 134 $html .= $this->display('session'); 135 136 137 if ($this->params->get('profile', 1)) 138 { 139 $html .= $this->display('profile_information'); 140 } 141 142 if ($this->params->get('memory', 1)) 143 { 144 $html .= $this->display('memory_usage'); 145 } 146 147 if ($this->params->get('queries', 1)) 148 { 149 $html .= $this->display('queries'); 150 } 151 } 152 153 if (JFactory::getApplication()->getCfg('debug_lang')) 154 { 155 if ($this->params->get('language_errorfiles', 1)) 156 { 157 $languageErrors = JFactory::getLanguage()->getErrorFiles(); 158 $html .= $this->display('language_files_in_error', $languageErrors); 159 } 160 161 if ($this->params->get('language_files', 1)) 162 { 163 $html .= $this->display('language_files_loaded'); 164 } 165 166 if ($this->params->get('language_strings')) 167 { 168 $html .= $this->display('untranslated_strings'); 169 } 170 } 171 172 $html .= '</div>'; 173 174 echo str_replace('</body>', $html.'</body>', $contents); 175 } 176 177 /** 178 * General display method. 179 * 180 * @param string $item The item to display 181 * @param array $errors Errors occured during execution 182 * 183 * @return string 184 */ 185 protected function display($item, array $errors = array()) 186 { 187 $title = JText::_('PLG_DEBUG_'.strtoupper($item)); 188 189 $status = ''; 190 191 if(count($errors)) 192 { 193 $status = ' dbgerror'; 194 } 195 196 $fncName = 'display'.ucfirst(str_replace('_', '', $item)); 197 198 if ( ! method_exists($this, $fncName)) 199 { 200 return __METHOD__.' -- Unknown method: '.$fncName.'<br />'; 201 } 202 203 $html = ''; 204 205 $js = "toggleContainer('dbgContainer".$item."');"; 206 207 $class = 'dbgHeader'.$status; 208 209 $html .= '<div class="'.$class.'" onclick="'.$js.'"><a href="javascript:void(0);"><h3>'.$title.'</h3></a></div>'; 210 211 $style = ' style="display: none;"';//@todo set with js.. ? 212 213 $html .= '<div '.$style.' class="dbgContainer" id="dbgContainer'.$item.'">'; 214 $html .= $this->$fncName(); 215 $html .= '</div>'; 216 217 return $html; 218 } 219 220 /** 221 * Display session information. 222 * 223 * Called recursive. 224 * 225 * @param string $key A session key 226 * @param mixed $session The session array, initially null 227 * @param integer $id The id is used for JS toggling the div 228 * 229 * @return string 230 */ 231 protected function displaySession($key = '', $session = null, $id = 0) 232 { 233 if( ! $session) $session = $_SESSION; 234 235 static $html = ''; 236 237 if( ! is_array($session)) 238 { 239 $html .= $key.' ⇒'.$session.PHP_EOL; 240 } 241 else 242 { 243 foreach ($session as $sKey => $entries) 244 { 245 $display = true; 246 247 if(is_array($entries) && $entries) 248 { 249 $display = false; 250 } 251 252 if(is_object($entries)) 253 { 254 $o = JArrayHelper::fromObject($entries); 255 256 if($o) 257 { 258 $entries = $o; 259 $display = false; 260 } 261 } 262 263 if( ! $display) 264 { 265 $js = "toggleContainer('dbgContainer_session".$id."');"; 266 267 $html .= '<div class="dbgHeader" onclick="'.$js.'"><a href="javascript:void(0);"><h3>'.$sKey.'</h3></a></div>'; 268 269 $style = ' style="display: none;"';//@todo set with js.. ? 270 271 $html .= '<div '.$style.' class="dbgContainer" id="dbgContainer_session'.$id.'">'; 272 $id ++; 273 274 // Recurse... 275 $this->displaySession($sKey, $entries, $id); 276 277 $html .= '</div>'; 278 279 continue; 280 } 281 282 $html .= '<code>'; 283 $html .= $sKey.' ⇒ '.$entries.'<br />'; 284 $html .= '</code>'; 285 } 286 } 287 288 return $html; 289 } 290 291 /** 292 * Display errors. 293 * 294 * @return string 295 */ 296 protected function displayErrors() 297 { 298 $html = ''; 299 300 $html .= '<ol>'; 301 302 while ($error = JError::getError(true)) 303 { 304 $col =(E_WARNING == $error->get('level')) ? 'red' : 'orange'; 305 306 $html .= '<li>'; 307 $html .= '<b style="color: '.$col.'">'.$error->getMessage().'</b><br />'; 308 309 $info = $error->get('info'); 310 311 if ($info) 312 { 313 $html .= '<pre>'.print_r($info, true).'</pre><br />'; 314 } 315 316 $html .= $this->renderBacktrace($error); 317 $html .= '</li>'; 318 } 319 320 $html .= '</ol>'; 321 322 return $html; 323 } 324 325 /** 326 * Display profile information. 327 * 328 * @return string 329 */ 330 protected function displayProfileInformation() 331 { 332 $html = ''; 333 334 foreach (JProfiler::getInstance('Application')->getBuffer() as $mark) 335 { 336 $html .= '<div>'.$mark.'</div>'; 337 } 338 339 return $html; 340 } 341 342 /** 343 * Display memory usage 344 * 345 * @return string 346 */ 347 protected function displayMemoryUsage() 348 { 349 $html = ''; 350 351 $bytes = JProfiler::getInstance('Application')->getMemory(); 352 353 $html .= '<code>'; 354 $html .= JHtml::_('number.bytes', $bytes); 355 $html .= ' ('.number_format($bytes).' Bytes)'; 356 $html .= '</code>'; 357 358 return $html; 359 } 360 361 /** 362 * Display logged queries. 363 * 364 * @return string 365 */ 366 protected function displayQueries() 367 { 368 $db = JFactory::getDbo(); 369 370 $log = $db->getLog(); 371 372 if ( ! $log) 373 { 374 return; 375 } 376 377 $html = ''; 378 379 $html .= '<h4>'.JText::sprintf('PLG_DEBUG_QUERIES_LOGGED', $db->getCount()).'</h4>'; 380 381 $html .= '<ol>'; 382 383 $selectQueryTypeTicker = array(); 384 $otherQueryTypeTicker = array(); 385 386 foreach ($log as $k => $sql) 387 { 388 // Start Query Type Ticker Additions 389 $fromStart = stripos($sql, 'from'); 390 $whereStart = stripos($sql, 'where', $fromStart); 391 392 if ($whereStart === false) 393 { 394 $whereStart = stripos($sql, 'order by', $fromStart); 395 } 396 397 if ($whereStart === false) 398 { 399 $whereStart = strlen($sql) - 1; 400 } 401 402 $fromString = substr($sql, 0, $whereStart); 403 $fromString = str_replace("\t", " ", $fromString); 404 $fromString = str_replace("\n", " ", $fromString); 405 $fromString = trim($fromString); 406 407 // Initialize the select/other query type counts the first time: 408 if (!isset($selectQueryTypeTicker[$fromString])) 409 { 410 $selectQueryTypeTicker[$fromString] = 0; 411 } 412 413 if (!isset($otherQueryTypeTicker[$fromString])) 414 { 415 $otherQueryTypeTicker[$fromString] = 0; 416 } 417 418 // Increment the count: 419 if (stripos($sql, 'select') === 0) 420 { 421 $selectQueryTypeTicker[$fromString] = $selectQueryTypeTicker[$fromString] + 1; 422 unset($otherQueryTypeTicker[$fromString]); 423 } 424 else 425 { 426 $otherQueryTypeTicker[$fromString] = $otherQueryTypeTicker[$fromString] + 1; 427 unset($selectQueryTypeTicker[$fromString]); 428 } 429 430 $text = $this->highlightQuery($sql); 431 432 $html .= '<li><code>'.$text.'</code></li>'; 433 } 434 435 $html .= '</ol>'; 436 437 if ( ! $this->params->get('query_types', 1)) 438 { 439 return $html; 440 } 441 442 // Get the totals for the query types: 443 $totalSelectQueryTypes = count($selectQueryTypeTicker); 444 $totalOtherQueryTypes = count($otherQueryTypeTicker); 445 $totalQueryTypes = $totalSelectQueryTypes + $totalOtherQueryTypes; 446 447 $html .= '<h4>'.JText::sprintf('PLG_DEBUG_QUERY_TYPES_LOGGED', $totalQueryTypes) . '</h4>'; 448 449 if ($totalSelectQueryTypes) 450 { 451 $html .= '<h5>'.JText::sprintf('PLG_DEBUG_SELECT_QUERIES').'</h5>'; 452 453 arsort($selectQueryTypeTicker); 454 455 $html .= '<ol>'; 456 457 foreach ($selectQueryTypeTicker as $query => $occurrences) 458 { 459 $html .= '<li><code>'.JText::sprintf('PLG_DEBUG_QUERY_TYPE_AND_OCCURRENCES' 460 , $this->highlightQuery($query), $occurrences).'</code></li>'; 461 } 462 463 $html .= '</ol>'; 464 } 465 466 if ($totalOtherQueryTypes) 467 { 468 $html .= '<h5>'.JText::sprintf('PLG_DEBUG_OTHER_QUERIES').'</h5>'; 469 470 arsort($otherQueryTypeTicker); 471 472 $html .= '<ol>'; 473 474 foreach ($otherQueryTypeTicker as $query => $occurrences) 475 { 476 $html .= '<li><code>'.JText::sprintf('PLG_DEBUG_QUERY_TYPE_AND_OCCURRENCES' 477 , $this->highlightQuery($query), $occurrences).'</code></li>'; 478 } 479 $html .= '</ol>'; 480 } 481 482 return $html; 483 } 484 485 /** 486 * Displays errors in language files. 487 * 488 * @return string 489 */ 490 protected function displayLanguageFilesInError() 491 { 492 $html = ''; 493 494 $errorfiles = JFactory::getLanguage()->getErrorFiles(); 495 496 if ( ! count($errorfiles)) 497 { 498 $html .= '<p>' . JText::_('JNONE') . '</p>'; 499 500 return $html; 501 } 502 503 $html .= '<ul>'; 504 505 foreach ($errorfiles as $file => $error) 506 { 507 $html .= '<li>'.$this->formatLink($file) 508 .str_replace($file, '', $error).'</li>'; 509 } 510 511 $html .= '</ul>'; 512 513 return $html; 514 } 515 516 /** 517 * Display loaded language files. 518 * 519 * @return string 520 */ 521 protected function displayLanguageFilesLoaded() 522 { 523 $html = ''; 524 525 $html .= '<ul>'; 526 527 foreach (JFactory::getLanguage()->getPaths() as $extension => $files) 528 { 529 foreach ($files as $file => $status) 530 { 531 $html .= '<li>'; 532 533 $html .= ($status) 534 ? JText::_('PLG_DEBUG_LANG_LOADED') 535 : JText::_('PLG_DEBUG_LANG_NOT_LOADED'); 536 537 $html .= ' : '; 538 $html .= $this->formatLink($file); 539 $html .= '</li>'; 540 } 541 } 542 543 $html .= '</ul>'; 544 545 return $html; 546 } 547 548 /** 549 * Display untranslated language strings. 550 * 551 * @return string 552 */ 553 protected function displayUntranslatedStrings() 554 { 555 $stripFirst = $this->params->get('strip-first'); 556 $stripPref = $this->params->get('strip-prefix'); 557 $stripSuff = $this->params->get('strip-suffix'); 558 559 $orphans = JFactory::getLanguage()->getOrphans(); 560 561 $html = ''; 562 563 if ( ! count($orphans)) 564 { 565 $html .= '<p>' . JText::_('JNONE') . '</p>'; 566 567 return $html; 568 } 569 570 ksort($orphans, SORT_STRING); 571 572 $guesses = array(); 573 574 foreach ($orphans as $key => $occurance) 575 { 576 if (is_array($occurance) && isset($occurance[0])) 577 { 578 $info = $occurance[0]; 579 $file = ($info['file']) ? $info['file'] : ''; 580 581 if (!isset($guesses[$file])) 582 { 583 $guesses[$file] = array(); 584 } 585 586 // Prepare the key 587 588 if (($pos = strpos($info['string'], '=')) > 0) 589 { 590 $parts = explode('=', $info['string']); 591 $key = $parts[0]; 592 $guess = $parts[1]; 593 } 594 else 595 { 596 $guess = str_replace('_', ' ', $info['string']); 597 598 if ($stripFirst) 599 { 600 $parts = explode(' ', $guess); 601 if (count($parts) > 1) 602 { 603 array_shift($parts); 604 $guess = implode(' ', $parts); 605 } 606 } 607 608 $guess = trim($guess); 609 610 if ($stripPref) 611 { 612 $guess = trim(preg_replace(chr(1).'^'.$stripPref.chr(1).'i', '', $guess)); 613 } 614 615 if ($stripSuff) 616 { 617 $guess = trim(preg_replace(chr(1).$stripSuff.'$'.chr(1).'i', '', $guess)); 618 } 619 } 620 621 $key = trim(strtoupper($key)); 622 $key = preg_replace('#\s+#', '_', $key); 623 $key = preg_replace('#\W#', '', $key); 624 625 // Prepare the text 626 $guesses[$file][] = $key.'="'.$guess.'"'; 627 } 628 } 629 630 631 foreach ($guesses as $file => $keys) 632 { 633 $html .= "\n\n# ".($file ? $this->formatLink($file) : JText::_('PLG_DEBUG_UNKNOWN_FILE'))."\n\n"; 634 $html .= implode("\n", $keys); 635 } 636 637 return '<pre>'.$html.'</pre>'; 638 } 639 640 /** 641 * Simple highlight for SQL queries. 642 * 643 * @param string $sql The query to highlight 644 * 645 * @return string 646 */ 647 protected function highlightQuery($sql) 648 { 649 $newlineKeywords = '#\b(FROM|LEFT|INNER|OUTER|WHERE|SET|VALUES|ORDER|GROUP|HAVING|LIMIT|ON|AND|CASE)\b#i'; 650 651 $sql = htmlspecialchars($sql, ENT_QUOTES); 652 653 $sql = preg_replace($newlineKeywords, '<br />  \\0', $sql); 654 655 $regex = array( 656 657 // Tables are identified by the prefix 658 '/(=)/' 659 => '<b class="dbgOperator">$1</b>', 660 661 // All uppercase words have a special meaning 662 '/(?<!\w|>)([A-Z_]{2,})(?!\w)/x' 663 => '<span class="dbgCommand">$1</span>', 664 665 // Tables are identified by the prefix 666 '/('.JFactory::getDbo()->getPrefix().'[a-z_0-9]+)/' 667 => '<span class="dbgTable">$1</span>' 668 669 ); 670 671 $sql = preg_replace(array_keys($regex), array_values($regex), $sql); 672 673 $sql = str_replace('*', '<b style="color: red;">*</b>', $sql); 674 675 return $sql; 676 } 677 678 /** 679 * Render the backtrace. 680 * 681 * Stolen from JError to prevent it's removal. 682 * 683 * @param integer $error The error 684 * 685 * @return string Contents of the backtrace 686 */ 687 protected function renderBacktrace($error) 688 { 689 $backtrace = $error->getTrace(); 690 691 $html = ''; 692 693 if (is_array($backtrace)) 694 { 695 $j = 1; 696 697 $html .= '<table cellpadding="0" cellspacing="0">'; 698 699 $html .= '<tr>'; 700 $html .= '<td colspan="3"><strong>Call stack</strong></td>'; 701 $html .= '</tr>'; 702 703 $html .= '<tr>'; 704 $html .= '<th>#</th>'; 705 $html .= '<th>Function</th>'; 706 $html .= '<th>Location</th>'; 707 $html .= '</tr>'; 708 709 for ($i = count($backtrace) - 1; $i >= 0 ; $i--) 710 { 711 $link = ' '; 712 713 if (isset($backtrace[$i]['file'])) 714 { 715 $link = $this->formatLink($backtrace[$i]['file'], $backtrace[$i]['line']); 716 } 717 718 $html .= '<tr>'; 719 $html .= '<td>'.$j.'</td>'; 720 721 if (isset($backtrace[$i]['class'])) 722 { 723 $html .= '<td>'.$backtrace[$i]['class'].$backtrace[$i]['type'].$backtrace[$i]['function'].'()</td>'; 724 } 725 else 726 { 727 $html .= '<td>'.$backtrace[$i]['function'].'()</td>'; 728 } 729 730 $html .= '<td>'.$link.'</td>'; 731 732 $html .= '</tr>'; 733 $j++; 734 } 735 736 $html .= '</table>'; 737 } 738 739 return $html; 740 } 741 742 /** 743 * Replaces the Joomla! root with "JROOT" to improve readability. 744 * Formats a link with a special value xdebug.file_link_format 745 * from the php.ini file. 746 * 747 * @param string $file The full path to the file. 748 * @param string $line The line number. 749 * 750 * @return string 751 */ 752 protected function formatLink($file, $line = '') 753 { 754 $link = str_replace(JPATH_ROOT, 'JROOT', $file); 755 $link .=($line) ? ':'.$line : ''; 756 757 if ($this->linkFormat) 758 { 759 $href = $this->linkFormat; 760 $href = str_replace('%f', $file, $href); 761 $href = str_replace('%l', $line, $href); 762 763 $html = '<a href="'.$href.'">'.$link.'</a>'; 764 } 765 else 766 { 767 $html = $link; 768 } 769 770 return $html; 771 } 772 773 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Tue Apr 3 11:40:28 2012 | Cross-referenced by PHPXref 0.7.1 |