1. sfWebDebug.class.php
  2. /** * sfWebDebug creates debug information for easy debugging in the browser. * * @package symfony * @subpackage debug * @author Fabien Potencier * @version SVN: $Id: sfWebDebug.class.php 27284 2010-01-28 18:34:57Z Kris.Wallsmith $ */
  3. class sfWebDebug
  4. {
  5. protected
  6. $dispatcher = null,
  7. $logger = null,
  8. $options = array(),
  9. $panels = array();
  10. /**
  11. * Constructor.
  12. *
  13. * Available options:
  14. *
  15. * * image_root_path: The image root path
  16. * * request_parameters: The current request parameters
  17. *
  18. * @param sfEventDispatcher $dispatcher The event dispatcher
  19. * @param sfVarLogger $logger The logger
  20. * @param array $options An array of options
  21. */
  22. public function __construct(sfEventDispatcher $dispatcher, sfVarLogger $logger, array $options = array())
  23. {
  24. $this->dispatcher = $dispatcher;
  25. $this->logger = $logger;
  26. $this->options = $options;
  27. if (!isset($this->options['image_root_path']))
  28. {
  29. $this->options['image_root_path'] = '';
  30. }
  31. if (!isset($this->options['request_parameters']))
  32. {
  33. $this->options['request_parameters'] = array();
  34. }
  35. $this->configure();
  36. $this->dispatcher->notify(new sfEvent($this, 'debug.web.load_panels'));
  37. }
  38. /**
  39. * Configures the web debug toolbar.
  40. */
  41. public function configure()
  42. {
  43. $this->setPanel('symfony_version', new sfWebDebugPanelSymfonyVersion($this));
  44. if (sfConfig::get('sf_debug') && sfConfig::get('sf_cache'))
  45. {
  46. $this->setPanel('cache', new sfWebDebugPanelCache($this));
  47. }
  48. if (sfConfig::get('sf_logging_enabled'))
  49. {
  50. $this->setPanel('config', new sfWebDebugPanelConfig($this));
  51. $this->setPanel('view', new sfWebDebugPanelView($this));
  52. }
  53. $this->setPanel('logs', new sfWebDebugPanelLogs($this));
  54. $this->setPanel('memory', new sfWebDebugPanelMemory($this));
  55. if (sfConfig::get('sf_debug'))
  56. {
  57. $this->setPanel('time', new sfWebDebugPanelTimer($this));
  58. }
  59. $this->setPanel('mailer', new sfWebDebugPanelMailer($this));
  60. }
  61. /**
  62. * Gets the logger.
  63. *
  64. * @return sfVarLogger The logger instance
  65. */
  66. public function getLogger()
  67. {
  68. return $this->logger;
  69. }
  70. /**
  71. * Gets the event dispatcher.
  72. *
  73. * @return sfEventDispatcher The event dispatcher
  74. */
  75. public function getEventDispatcher()
  76. {
  77. return $this->dispatcher;
  78. }
  79. /**
  80. * Gets the registered panels.
  81. *
  82. * @return array The panels
  83. */
  84. public function getPanels()
  85. {
  86. return $this->panels;
  87. }
  88. /**
  89. * Sets a panel by name.
  90. *
  91. * @param string $name The panel name
  92. * @param sfWebDebugPanel $panel The panel
  93. */
  94. public function setPanel($name, sfWebDebugPanel $panel)
  95. {
  96. $this->panels[$name] = $panel;
  97. }
  98. /**
  99. * Removes a panel by name.
  100. *
  101. * @param string $name The panel name
  102. */
  103. public function removePanel($name)
  104. {
  105. unset($this->panels[$name]);
  106. }
  107. /**
  108. * Gets an option value by name.
  109. *
  110. * @param string $name The option name
  111. *
  112. * @return mixed The option value
  113. */
  114. public function getOption($name, $default = null)
  115. {
  116. return isset($this->options[$name]) ? $this->options[$name] : $default;
  117. }
  118. /**
  119. * Injects the web debug toolbar into a given HTML string.
  120. *
  121. * @param string $content The HTML content
  122. *
  123. * @return string The content with the web debug toolbar injected
  124. */
  125. public function injectToolbar($content)
  126. {
  127. $content = str_ireplace('</head>', '<style type="text/css">'.str_replace(array("\r", "\n"), ' ', $this->getStylesheet()).'</style></head>', $content);
  128. $debug = $this->asHtml();
  129. $count = 0;
  130. $content = str_ireplace('</body>', '<script type="text/javascript">'.$this->getJavascript().'</script>'.$debug.'</body>', $content, $count);
  131. if (!$count)
  132. {
  133. $content .= $debug;
  134. }
  135. return $content;
  136. }
  137. /**
  138. * Returns the web debug toolbar as HTML.
  139. *
  140. * @return string The web debug toolbar HTML
  141. */
  142. public function asHtml()
  143. {
  144. $current = isset($this->options['request_parameters']['sfWebDebugPanel']) ? $this->options['request_parameters']['sfWebDebugPanel'] : null;
  145. $titles = array();
  146. $panels = array();
  147. foreach ($this->panels as $name => $panel)
  148. {
  149. if ($title = $panel->getTitle())
  150. {
  151. if (($content = $panel->getPanelContent()) || $panel->getTitleUrl())
  152. {
  153. $id = sprintf('sfWebDebug%sDetails', $name);
  154. $titles[] = sprintf('<li%s><a title="%s" href="%s"%s>%s</a></li>',
  155. $panel->getStatus() ? ' class="sfWebDebug'.ucfirst($this->getPriority($panel->getStatus())).'"' : '',
  156. $panel->getPanelTitle(),
  157. $panel->getTitleUrl() ? $panel->getTitleUrl() : '#',
  158. $panel->getTitleUrl() ? '' : ' onclick="sfWebDebugShowDetailsFor(\''.$id.'\'); return false;"',
  159. $title
  160. );
  161. $panels[] = sprintf('<div id="%s" class="sfWebDebugTop" style="display:%s"><h1>%s</h1>%s</div>',
  162. $id,
  163. $name == $current ? 'block' : 'none',
  164. $panel->getPanelTitle(),
  165. $content
  166. );
  167. }
  168. else
  169. {
  170. $titles[] = sprintf('<li>%s</li>', $title);
  171. }
  172. }
  173. }
  174. return '
  175. <div id="sfWebDebug">
  176. <div id="sfWebDebugBar">
  177. <a href="#" onclick="sfWebDebugToggleMenu(); return false;"><img src="'.$this->options['image_root_path'].'/sf.png" alt="Debug toolbar" /></a>
  178. <ul id="sfWebDebugDetails" class="sfWebDebugMenu">
  179. '.implode("\n", $titles).'
  180. <li class="last">
  181. <a href="#" onclick="document.getElementById(\'sfWebDebug\').style.display=\'none\'; return false;"><img src="'.$this->options['image_root_path'].'/close.png" alt="Close" /></a>
  182. </li>
  183. </ul>
  184. </div>
  185. '.implode("\n", $panels).'
  186. </div>
  187. ';
  188. }
  189. /**
  190. * Converts a priority value to a string.
  191. *
  192. * @param integer $value The priority value
  193. *
  194. * @return string The priority as a string
  195. */
  196. public function getPriority($value)
  197. {
  198. if ($value >= sfLogger::INFO)
  199. {
  200. return 'info';
  201. }
  202. else if ($value >= sfLogger::WARNING)
  203. {
  204. return 'warning';
  205. }
  206. else
  207. {
  208. return 'error';
  209. }
  210. }
  211. /**
  212. * Gets the javascript code to inject in the head tag.
  213. *
  214. * @param string The javascript code
  215. */
  216. public function getJavascript()
  217. {
  218. return <<<EOF
  219. /* <![CDATA[ */
  220. function sfWebDebugGetElementsByClassName(strClass, strTag, objContElm)
  221. {
  222. // http://muffinresearch.co.uk/archives/2006/04/29/getelementsbyclassname-deluxe-edition/
  223. strTag = strTag || "*";
  224. objContElm = objContElm || document;
  225. var objColl = (strTag == '*' && document.all) ? document.all : objContElm.getElementsByTagName(strTag);
  226. var arr = new Array();
  227. var delim = strClass.indexOf('|') != -1 ? '|' : ' ';
  228. var arrClass = strClass.split(delim);
  229. var j = objColl.length;
  230. for (var i = 0; i < j; i++) {
  231. if(objColl[i].className == undefined) continue;
  232. var arrObjClass = objColl[i].className.split ? objColl[i].className.split(' ') : [];
  233. if (delim == ' ' && arrClass.length > arrObjClass.length) continue;
  234. var c = 0;
  235. comparisonLoop:
  236. {
  237. var l = arrObjClass.length;
  238. for (var k = 0; k < l; k++) {
  239. var n = arrClass.length;
  240. for (var m = 0; m < n; m++) {
  241. if (arrClass[m] == arrObjClass[k]) c++;
  242. if (( delim == '|' && c == 1) || (delim == ' ' && c == arrClass.length)) {
  243. arr.push(objColl[i]);
  244. break comparisonLoop;
  245. }
  246. }
  247. }
  248. }
  249. }
  250. return arr;
  251. }
  252. function sfWebDebugToggleMenu()
  253. {
  254. var element = document.getElementById('sfWebDebugDetails');
  255. var cacheElements = sfWebDebugGetElementsByClassName('sfWebDebugCache');
  256. var mainCacheElements = sfWebDebugGetElementsByClassName('sfWebDebugActionCache');
  257. var panelElements = sfWebDebugGetElementsByClassName('sfWebDebugTop');
  258. if (element.style.display != 'none')
  259. {
  260. for (var i = 0; i < panelElements.length; ++i)
  261. {
  262. panelElements[i].style.display = 'none';
  263. }
  264. // hide all cache information
  265. for (var i = 0; i < cacheElements.length; ++i)
  266. {
  267. cacheElements[i].style.display = 'none';
  268. }
  269. for (var i = 0; i < mainCacheElements.length; ++i)
  270. {
  271. mainCacheElements[i].style.border = 'none';
  272. }
  273. }
  274. else
  275. {
  276. for (var i = 0; i < cacheElements.length; ++i)
  277. {
  278. cacheElements[i].style.display = '';
  279. }
  280. for (var i = 0; i < mainCacheElements.length; ++i)
  281. {
  282. mainCacheElements[i].style.border = '1px solid #f00';
  283. }
  284. }
  285. sfWebDebugToggle('sfWebDebugDetails');
  286. sfWebDebugToggle('sfWebDebugShowMenu');
  287. sfWebDebugToggle('sfWebDebugHideMenu');
  288. }
  289. function sfWebDebugShowDetailsFor(element)
  290. {
  291. if (typeof element == 'string')
  292. element = document.getElementById(element);
  293. var panelElements = sfWebDebugGetElementsByClassName('sfWebDebugTop');
  294. for (var i = 0; i < panelElements.length; ++i)
  295. {
  296. if (panelElements[i] != element)
  297. {
  298. panelElements[i].style.display = 'none';
  299. }
  300. }
  301. sfWebDebugToggle(element);
  302. }
  303. function sfWebDebugToggle(element)
  304. {
  305. if (typeof element == 'string')
  306. element = document.getElementById(element);
  307. if (element)
  308. element.style.display = element.style.display == 'none' ? '' : 'none';
  309. }
  310. function sfWebDebugToggleMessages(klass)
  311. {
  312. var elements = sfWebDebugGetElementsByClassName(klass);
  313. var x = elements.length;
  314. for (var i = 0; i < x; ++i)
  315. {
  316. sfWebDebugToggle(elements[i]);
  317. }
  318. }
  319. function sfWebDebugToggleAllLogLines(show, klass)
  320. {
  321. var elements = sfWebDebugGetElementsByClassName(klass);
  322. var x = elements.length;
  323. for (var i = 0; i < x; ++i)
  324. {
  325. elements[i].style.display = show ? '' : 'none';
  326. }
  327. }
  328. function sfWebDebugShowOnlyLogLines(type)
  329. {
  330. var types = new Array();
  331. types[0] = 'info';
  332. types[1] = 'warning';
  333. types[2] = 'error';
  334. for (klass in types)
  335. {
  336. var elements = sfWebDebugGetElementsByClassName('sfWebDebug' + types[klass].substring(0, 1).toUpperCase() + types[klass].substring(1, types[klass].length));
  337. var x = elements.length;
  338. for (var i = 0; i < x; ++i)
  339. {
  340. if ('tr' == elements[i].tagName.toLowerCase())
  341. {
  342. elements[i].style.display = (type == types[klass]) ? '' : 'none';
  343. }
  344. }
  345. }
  346. }
  347. /* ]]> */
  348. EOF;
  349. }
  350. /**
  351. * Gets the stylesheet code to inject in the head tag.
  352. *
  353. * @param string The stylesheet code
  354. */
  355. public function getStylesheet()
  356. {
  357. return <<<EOF
  358. #sfWebDebug
  359. {
  360. padding: 0;
  361. margin: 0;
  362. font-family: Arial, sans-serif;
  363. font-size: 12px;
  364. color: #333;
  365. text-align: left;
  366. line-height: 12px;
  367. }
  368. #sfWebDebug a, #sfWebDebug a:hover
  369. {
  370. text-decoration: none;
  371. border: none;
  372. background-color: transparent;
  373. color: #000;
  374. }
  375. #sfWebDebug img
  376. {
  377. float: none;
  378. margin: 0;
  379. border: 0;
  380. display: inline;
  381. }
  382. #sfWebDebugBar
  383. {
  384. position: absolute;
  385. margin: 0;
  386. padding: 1px 0;
  387. right: 0px;
  388. top: 0px;
  389. opacity: 0.80;
  390. filter: alpha(opacity:80);
  391. z-index: 10000;
  392. white-space: nowrap;
  393. background-color: #ddd;
  394. }
  395. #sfWebDebugBar[id]
  396. {
  397. position: fixed;
  398. }
  399. #sfWebDebugBar img
  400. {
  401. vertical-align: middle;
  402. }
  403. #sfWebDebugBar .sfWebDebugMenu
  404. {
  405. padding: 5px;
  406. padding-left: 0;
  407. display: inline;
  408. margin: 0;
  409. }
  410. #sfWebDebugBar .sfWebDebugMenu li
  411. {
  412. display: inline;
  413. list-style: none;
  414. margin: 0;
  415. padding: 0 6px;
  416. }
  417. #sfWebDebugBar .sfWebDebugMenu li.last
  418. {
  419. margin: 0;
  420. padding: 0;
  421. border: 0;
  422. }
  423. #sfWebDebugDatabaseDetails li
  424. {
  425. margin: 0;
  426. margin-left: 30px;
  427. padding: 5px 0;
  428. }
  429. #sfWebDebugShortMessages li
  430. {
  431. margin-bottom: 10px;
  432. padding: 5px;
  433. background-color: #ddd;
  434. }
  435. #sfWebDebugShortMessages li
  436. {
  437. list-style: none;
  438. }
  439. #sfWebDebugDetails
  440. {
  441. margin-right: 7px;
  442. }
  443. #sfWebDebug pre
  444. {
  445. line-height: 1.3;
  446. margin-bottom: 10px;
  447. }
  448. #sfWebDebug h1
  449. {
  450. font-size: 16px;
  451. font-weight: bold;
  452. margin: 20px 0;
  453. padding: 0;
  454. border: 0px;
  455. background-color: #eee;
  456. }
  457. #sfWebDebug h2
  458. {
  459. font-size: 14px;
  460. font-weight: bold;
  461. margin: 10px 0;
  462. padding: 0;
  463. border: 0px;
  464. background: none;
  465. }
  466. #sfWebDebug h3
  467. {
  468. font-size: 12px;
  469. font-weight: bold;
  470. margin: 10px 0;
  471. padding: 0;
  472. border: 0px;
  473. background: none;
  474. }
  475. #sfWebDebug .sfWebDebugTop
  476. {
  477. position: absolute;
  478. left: 0px;
  479. top: 0px;
  480. width: 98%;
  481. padding: 0 1%;
  482. margin: 0;
  483. z-index: 9999;
  484. background-color: #efefef;
  485. border-bottom: 1px solid #aaa;
  486. }
  487. #sfWebDebugLog
  488. {
  489. margin: 0;
  490. padding: 3px;
  491. font-size: 11px;
  492. }
  493. #sfWebDebugLogMenu
  494. {
  495. margin-bottom: 5px;
  496. }
  497. #sfWebDebugLogMenu li
  498. {
  499. display: inline;
  500. list-style: none;
  501. margin: 0;
  502. padding: 0 5px;
  503. border-right: 1px solid #aaa;
  504. }
  505. #sfWebDebugConfigSummary
  506. {
  507. display: inline;
  508. padding: 5px;
  509. background-color: #ddd;
  510. border: 1px solid #aaa;
  511. margin: 20px 0;
  512. }
  513. #sfWebDebugConfigSummary li
  514. {
  515. list-style: none;
  516. display: inline;
  517. margin: 0;
  518. padding: 0 5px;
  519. }
  520. #sfWebDebugConfigSummary li.last
  521. {
  522. border: 0;
  523. }
  524. .sfWebDebugInfo, .sfWebDebugInfo td
  525. {
  526. background-color: #ddd;
  527. }
  528. .sfWebDebugWarning, .sfWebDebugWarning td
  529. {
  530. background-color: orange !important;
  531. }
  532. .sfWebDebugError, .sfWebDebugError td
  533. {
  534. background-color: #f99 !important;
  535. }
  536. .sfWebDebugLogNumber
  537. {
  538. width: 1%;
  539. }
  540. .sfWebDebugLogType
  541. {
  542. width: 1%;
  543. white-space: nowrap;
  544. }
  545. .sfWebDebugLogType, #sfWebDebug .sfWebDebugLogType a
  546. {
  547. color: darkgreen;
  548. }
  549. #sfWebDebug .sfWebDebugLogType a:hover
  550. {
  551. text-decoration: underline;
  552. }
  553. .sfWebDebugLogInfo
  554. {
  555. color: blue;
  556. }
  557. .ison
  558. {
  559. color: #3f3;
  560. margin-right: 5px;
  561. }
  562. .isoff
  563. {
  564. color: #f33;
  565. margin-right: 5px;
  566. text-decoration: line-through;
  567. }
  568. .sfWebDebugLogs
  569. {
  570. padding: 0;
  571. margin: 0;
  572. border: 1px solid #999;
  573. font-family: Arial;
  574. font-size: 11px;
  575. }
  576. .sfWebDebugLogs tr
  577. {
  578. padding: 0;
  579. margin: 0;
  580. border: 0;
  581. }
  582. .sfWebDebugLogs td
  583. {
  584. margin: 0;
  585. border: 0;
  586. padding: 1px 3px;
  587. vertical-align: top;
  588. }
  589. .sfWebDebugLogs th
  590. {
  591. margin: 0;
  592. border: 0;
  593. padding: 3px 5px;
  594. vertical-align: top;
  595. background-color: #999;
  596. color: #eee;
  597. white-space: nowrap;
  598. }
  599. .sfWebDebugDebugInfo
  600. {
  601. color: #999;
  602. font-size: 11px;
  603. margin: 5px 0 5px 10px;
  604. padding: 2px 0 2px 5px;
  605. border-left: 1px solid #aaa;
  606. line-height: 1.25em;
  607. }
  608. .sfWebDebugDebugInfo .sfWebDebugLogInfo,
  609. .sfWebDebugDebugInfo a.sfWebDebugFileLink
  610. {
  611. color: #333 !important;
  612. }
  613. .sfWebDebugCache
  614. {
  615. padding: 0;
  616. margin: 0;
  617. font-family: Arial;
  618. position: absolute;
  619. overflow: hidden;
  620. z-index: 995;
  621. font-size: 9px;
  622. padding: 2px;
  623. filter:alpha(opacity=85);
  624. -moz-opacity:0.85;
  625. opacity: 0.85;
  626. }
  627. #sfWebDebugSymfonyVersion
  628. {
  629. margin-left: 0;
  630. padding: 1px 4px;
  631. background-color: #666;
  632. color: #fff;
  633. }
  634. #sfWebDebugviewDetails ul
  635. {
  636. padding-left: 2em;
  637. margin: .5em 0;
  638. list-style: none;
  639. }
  640. #sfWebDebugviewDetails li
  641. {
  642. margin-bottom: .5em;
  643. }
  644. #sfWebDebug .sfWebDebugDataType,
  645. #sfWebDebug .sfWebDebugDataType a
  646. {
  647. color: #666;
  648. font-style: italic;
  649. }
  650. #sfWebDebug .sfWebDebugDataType a:hover
  651. {
  652. text-decoration: underline;
  653. }
  654. #sfWebDebugDatabaseLogs
  655. {
  656. margin-bottom: 10px;
  657. }
  658. #sfWebDebugDatabaseLogs ol
  659. {
  660. margin: 0;
  661. padding: 0;
  662. margin-left: 20px;
  663. list-style: number;
  664. }
  665. #sfWebDebugDatabaseLogs li
  666. {
  667. padding: 6px;
  668. }
  669. #sfWebDebugDatabaseLogs li:nth-child(odd)
  670. {
  671. background-color: #CCC;
  672. }
  673. .sfWebDebugDatabaseQuery
  674. {
  675. margin-bottom: .5em;
  676. margin-top: 0;
  677. }
  678. .sfWebDebugDatabaseLogInfo
  679. {
  680. color: #666;
  681. font-size: 11px;
  682. }
  683. .sfWebDebugDatabaseQuery .sfWebDebugLogInfo
  684. {
  685. color: #909;
  686. font-weight: bold;
  687. }
  688. .sfWebDebugHighlight
  689. {
  690. background: #FFC;
  691. }
  692. EOF;
  693. }
  694. }

Debug toolbar