PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/core/Debug.class.php

https://bitbucket.org/multimedium/bob361
PHP | 525 lines | 213 code | 57 blank | 255 comment | 17 complexity | 3957e610580cc688454cae2e41b9a229 MD5 | raw file
Possible License(s): LGPL-2.0
  1. <?php
  2. class M_Debug extends M_Object {
  3. /**
  4. * Stores if debug-mode is on/off
  5. *
  6. * @var bool
  7. */
  8. private static $_debugMode = false;
  9. /**
  10. * Stores if queries need to be logged or not
  11. *
  12. * @var bool
  13. */
  14. private static $_logQueries = false;
  15. /**
  16. * Stores if a growl notification needs to be sent when catching an
  17. * {@link M_Excpetion}
  18. *
  19. * @var bool
  20. */
  21. private static $_growlNotifications = false;
  22. /**
  23. * Holds the recipient to which mails will be sent if
  24. * {@link M_Debug::getDebugMode()} is enabled
  25. *
  26. * @var string
  27. */
  28. private static $_debugEmailRecipient;
  29. /**
  30. * Stores if cache-busting on/off
  31. *
  32. * @var bool
  33. */
  34. private static $_cacheBusting = false;
  35. /**
  36. * Stores the date which is used for cachebusting
  37. *
  38. * @var M_Date
  39. */
  40. private static $_cacheBustingDate;
  41. /**
  42. * the ip addresses that wil effectively show
  43. * the debug message
  44. *
  45. * @var array
  46. */
  47. private static $_safeIps = array('81.83.0.140', '::1', '127.0.0.1');
  48. /**
  49. * Enable/disable debug-mode
  50. *
  51. * @param bool $arg
  52. */
  53. public static function setDebugMode($arg) {
  54. self::$_debugMode = (bool)$arg;
  55. }
  56. /**
  57. * Check if debug mode is enabled/disabled
  58. *
  59. * @return bool
  60. */
  61. public static function getDebugMode() {
  62. return self::$_debugMode;
  63. }
  64. /**
  65. * Tell the db-driver wether or not to log queries
  66. *
  67. * @param bool $arg
  68. */
  69. public static function setLogQueries($arg) {
  70. self::$_logQueries = (bool)$arg;
  71. }
  72. /**
  73. * Enable/disable cache busting
  74. *
  75. * Cache busting will append a timestamp to all javascript and css
  76. * references.
  77. *
  78. * E.g. <link rel="stylesheet" href="http://domain/application/resources/css/all.css" type="text/css" media="screen" />
  79. * will be transformed into:
  80. * E.g. <link rel="stylesheet" href="http://domain/application/resources/css/all.css?20111114" type="text/css" media="screen" />
  81. *
  82. *
  83. * @see M_ViewHtmlResource::fetch()
  84. * @param bool $mode Set to true to enable cachebusting
  85. * @param M_Date $date Pass a date to use as variable in the link
  86. */
  87. public static function setCacheBusting($mode = true, M_Date $date = null) {
  88. self::$_cacheBusting = $mode;
  89. if(!is_null($date)) {
  90. self::$_cacheBustingDate = $date;
  91. }
  92. }
  93. /**
  94. * Add safe ip
  95. *
  96. * by default the ip from multimedium and your localhost
  97. * are enabled. By adding extra IP addresses they can see
  98. * the debug messages as well.
  99. *
  100. * @param type $ip
  101. */
  102. public static function addSafeIp($ip) {
  103. self::$_safeIps[] = (string) $ip;
  104. }
  105. /**
  106. * Check if cache busting is enabled
  107. *
  108. * @return bool
  109. */
  110. public static function getCacheBusting() {
  111. return self::$_cacheBusting;
  112. }
  113. /**
  114. * Get cache busting date
  115. *
  116. * @return M_Date
  117. */
  118. public static function getCacheBustingDate() {
  119. return self::$_cacheBustingDate;
  120. }
  121. /**
  122. * Is the db-driver logging queries?
  123. *
  124. * @return bool
  125. */
  126. public static function getLogQueries() {
  127. return self::$_logQueries;
  128. }
  129. /**
  130. * Enable/disable growl notifications
  131. *
  132. * @param bool $arg
  133. */
  134. public static function setGrowlNotifications($arg) {
  135. self::$_growlNotifications = (bool)$arg;
  136. }
  137. /**
  138. * Check if growl notifications are enabled/disabled
  139. *
  140. * @return bool
  141. */
  142. public static function getGrowlNotifications() {
  143. return self::$_growlNotifications;
  144. }
  145. /**
  146. * Enable maintenance mode
  147. *
  148. * In maintenance mode all traffic will be redirected to maintenance.php,
  149. * and a header 302 will be sent (temporary redirect).
  150. *
  151. * You will call this method before dispatch, all traffic will then be
  152. * redirected.
  153. *
  154. * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
  155. * @param string $excludeIp
  156. * Check the ip and do not redirect if the users' ip matches the $excludeIp
  157. * @return void
  158. */
  159. public static function maintenanceMode($excludeIp = null) {
  160. //do not redirect if users' ip matches the exclude-ip
  161. if(!is_null($excludeIp) && $excludeIp == M_Client::getIp()) {
  162. //display a message to remind the developer maintenance mode is enabled
  163. $msg = '<div style="padding:10px;background-color:#dd0000;margin-bottom:20px;color:#fff;text-align:center;">';
  164. $msg .= 'Maintenance mode enabled';
  165. $msg .= '</div>';
  166. echo $msg;
  167. return false;
  168. }
  169. $date = new M_Date();
  170. $date->addTime(1, M_Date::HOUR);
  171. M_Header::sendHttpStatus(503);
  172. M_Header::send('Retry-After: '.$date->getRfc2822());
  173. echo file_get_contents(M_Request::getLink('maintenance'));
  174. die();
  175. }
  176. /**
  177. * Set the recipient to which all mail will be redirected if
  178. * {@link M_Debug::getDebugMode()} is enabled
  179. *
  180. * @param string
  181. */
  182. public static function setDebugEmailRecipient($arg) {
  183. self::$_debugEmailRecipient = (string)$arg;
  184. }
  185. /**
  186. * Set the recipient to which all mail will be redirected if
  187. * {@link M_Debug::getDebugMode()} is enabled
  188. *
  189. * Note: if empty (or invalid) the mail won't be redirected automaticcally
  190. *
  191. * @return string
  192. */
  193. public static function getDebugEmailRecipient() {
  194. return self::$_debugEmailRecipient;
  195. }
  196. /**
  197. * Output array to screen
  198. *
  199. * @param array $array
  200. * @return void
  201. */
  202. public static function printArray($array) {
  203. if (self::isSafeIp()) {
  204. echo "<pre>";
  205. print_r((array)$array);
  206. echo"</pre>";
  207. }
  208. }
  209. /**
  210. * Alias for {@link M_Debug::printArray()}
  211. *
  212. * @access public
  213. * @param array $array
  214. * @return void
  215. */
  216. public static function printr($array) {
  217. self::printArray($array);
  218. }
  219. /**
  220. * Output var_dump(); to screen
  221. *
  222. * @param mixed $mixed
  223. * @param str $message
  224. */
  225. public static function printVarDump($mixed, $message = null) {
  226. if (self::isSafeIp()) {
  227. echo "<pre>";
  228. if ($message) echo $message.'<hr>';
  229. var_dump($mixed);
  230. echo"</pre>";
  231. }
  232. }
  233. /**
  234. * Alias for {@link M_Debug::printVarDump()}
  235. *
  236. * @access public
  237. * @param mixed $mixed
  238. * @param string $message
  239. * @return void
  240. */
  241. public static function dump($mixed, $message = null) {
  242. self::printVarDump($mixed, $message);
  243. }
  244. /**
  245. * is safe ip
  246. *
  247. * will return true if the current user ip is Multimedium,
  248. * localhost or added trough {link M_Debug::addSafeIp()}
  249. *
  250. * @param string $ip
  251. * optional, the ip to check, if none given,
  252. * the current Client ip will be used
  253. * @return bool
  254. */
  255. public static function isSafeIp($ip = null) {
  256. $ip = $ip ? (string) $ip : M_Client::getIp();
  257. return in_array($ip, self::$_safeIps) || strpos($ip, '192.168.') !== false;;
  258. }
  259. /* -- Query logging -- */
  260. /**
  261. * Get a log of all queries which have been run during runtime
  262. *
  263. * Each query is stored in an array containing the sql statement,
  264. * execution time and backtrace
  265. *
  266. * @author Ben Brughmans
  267. * @return M_ArrayIterator
  268. */
  269. public static function getQueryLog() {
  270. return M_Registry::getInstance()->get(
  271. 'queries',
  272. new M_ArrayIterator()
  273. );
  274. }
  275. /**
  276. * Update the query log with a new set of queries
  277. *
  278. * @param M_ArrayIterator $log
  279. */
  280. protected static function _setQueryLog(M_ArrayIterator $log) {
  281. M_Registry::getInstance()->__set('queries', $log);
  282. }
  283. /**
  284. * Add a query to the registry-log
  285. *
  286. * By storing queries (and optionally their execution time and backtrace)
  287. * in the registry we can create a log of all queries which have been
  288. * executed during runtime.
  289. *
  290. * This is done automatically during debug-mode.
  291. *
  292. * If debug-mode is disabled you could also do this yourself.
  293. *
  294. * @see M_Debug::getQueryLog()
  295. * @param string $sql
  296. * @param float $executionTime
  297. * @param array $backtrace
  298. * @author Ben Brughmans
  299. */
  300. public static function addToQueryLog($sql, $executionTime = null, $backtrace = null) {
  301. $log = self::getQueryLog();
  302. //append new values to query log
  303. $log->append(array(
  304. 'sql' => (string)$sql ,
  305. 'executiontime' => floatval($executionTime),
  306. 'backtrace' => $backtrace
  307. ));
  308. //update query log with new query
  309. self::_setQueryLog($log);
  310. }
  311. /**
  312. * Print a list of queries which have been executed during runtime
  313. *
  314. * @return void
  315. * @see M_Debug::getQueryLog()
  316. * @author Ben Brughmans
  317. * @todo: toggle backtrace (backtrace row is hidden now)
  318. */
  319. public static function printQueryLog() {
  320. // only for safge ip's
  321. if (! self::isSafeIp()) {
  322. return false;
  323. }
  324. // Fetch the query log
  325. $queryLog = self::getQueryLog();
  326. // Working variables
  327. $totalExecutionTime = 0;
  328. $str = '';
  329. // Working variables to detect double queries
  330. $queryStrings = array();
  331. $doubleQueries = array();
  332. // Loop through the query log
  333. foreach($queryLog AS $i => $log) {
  334. // If this query has been run before
  335. if(in_array($log['sql'], $queryStrings)) {
  336. // Is this the first time this query has been duplicated?
  337. if(! array_key_exists($log['sql'], $doubleQueries)) {
  338. // Log the double query
  339. $doubleQueries[$log['sql']] = array(
  340. 'index' => $i,
  341. 'count' => 2
  342. );
  343. }
  344. // Otherwise, simply increase the count
  345. else {
  346. $doubleQueries[$log['sql']]['count']++;
  347. }
  348. // Otherwise, store it
  349. } else {
  350. // Log the query as a string, to detect doubles
  351. $queryStrings[] = $log['sql'];
  352. }
  353. // Fetch the execution time
  354. $executiontime = M_Helper::getArrayElement('executiontime', $log);
  355. //store the total amount of executiontime
  356. $totalExecutionTime += $executiontime;
  357. $str .= '<tr onclick="document.getElementById(\'debug-row-'. $i .'\').style.display = (document.getElementById(\'debug-row-'. $i .'\').style.display == \'none\' ? \'block\' : \'none\')">';
  358. $str .= '<td>'.$log['sql'].'</td>';
  359. $str .= '<td>'.$executiontime.'</td>';
  360. $str .= '</tr>';
  361. //get backtrace, and print if available
  362. $backtrace = M_Helper::getArrayElement('backtrace', $log);
  363. //if a backtrace is set, print it
  364. if (is_array($backtrace)):
  365. $str .= '<tr style="display:none;" id="debug-row-'. $i .'">';
  366. $str .= '<td colspan="2">';
  367. $str .= '<table>';
  368. foreach($backtrace AS $backtraceItem) {
  369. $str .= '<tr>';
  370. $str .= '<td>'.M_Helper::getArrayElement('file', $backtraceItem).'</td>';
  371. $str .= '<td>'.M_Helper::getArrayElement('class', $backtraceItem).'::'.M_Helper::getArrayElement('function', $backtraceItem).'</td>';
  372. $str .= '<td>'.M_Helper::getArrayElement('line', $backtraceItem).'</td>';
  373. $str .= '</tr>';
  374. }
  375. $str .= '</table>';
  376. $str .= '</tr>';
  377. endif;
  378. }
  379. //output the log in a table, with the totals on top
  380. $table = '<table border="1" cellpadding="2" style="background:#fff;color:#000;border:1px black solid;clear:both;">';
  381. $table .= '<tr>';
  382. $table .= '<td style="background-color:#ccc;font-weight:bold;">' . count($queryLog) . ' queries executed (' . (count($doubleQueries) > 0 ? 'double queries detected; check below' : 'no double queries') . ')</td>';
  383. $table .= '<td style="background-color:#ccc;font-weight:bold;">'.$totalExecutionTime.'</td>';
  384. $table .= '</tr>';
  385. $table .= $str;
  386. $table .= '</table>';
  387. // Echo the table
  388. echo $table;
  389. // If there are any double queries:
  390. if(count($doubleQueries) > 0) {
  391. echo '<br/>';
  392. // Reset the working variables
  393. $totalExecutionTime = 0;
  394. $redundantCount = 0;
  395. $str = '';
  396. // Loop through the double queries
  397. $i = 0;
  398. foreach($doubleQueries as $sql => $elem) {
  399. $i++;
  400. // Fetch the log
  401. $log = $queryLog[$elem['index']];
  402. // Fetch the execution time and update the redundant count
  403. $executiontime = M_Helper::getArrayElement('executiontime', $log);
  404. $redundantCount += $elem['count'];
  405. //store the total amount of executiontime
  406. $totalExecutionTime += $executiontime;
  407. $str .= '<tr onclick="document.getElementById(\'debug-row-doubles-'. $i .'\').style.display = (document.getElementById(\'debug-row-doubles-'. $i .'\').style.display == \'none\' ? \'block\' : \'none\')">';
  408. $str .= '<td>'.$log['sql'].'</td>';
  409. $str .= '<td>'.$executiontime.'</td>';
  410. $str .= '<td>'.$elem['count'].'</td>';
  411. $str .= '</tr>';
  412. //get backtrace, and print if available
  413. $backtrace = M_Helper::getArrayElement('backtrace', $log);
  414. //if a backtrace is set, print it
  415. if (is_array($backtrace)):
  416. $str .= '<tr style="display:none;" id="debug-row-doubles-'. $i .'">';
  417. $str .= '<td colspan="2">';
  418. $str .= '<table>';
  419. foreach($backtrace AS $backtraceItem) {
  420. $str .= '<tr>';
  421. $str .= '<td>'.M_Helper::getArrayElement('file', $backtraceItem).'</td>';
  422. $str .= '<td>'.M_Helper::getArrayElement('class', $backtraceItem).'::'.M_Helper::getArrayElement('function', $backtraceItem).'</td>';
  423. $str .= '<td>'.M_Helper::getArrayElement('line', $backtraceItem).'</td>';
  424. $str .= '</tr>';
  425. }
  426. $str .= '</table>';
  427. $str .= '</tr>';
  428. endif;
  429. }
  430. // Output the double queries in a table, with the total on top:
  431. $table = '<table border="1" cellpadding="2" style="background:#fff;color:#000;border:1px black solid;clear:both;width:100%;">';
  432. $table .= '<tr>';
  433. $table .= '<td style="background-color:#ccc;font-weight:bold;">' . count($doubleQueries) . ' duplicate queries ('. ($redundantCount - count($doubleQueries)) .' redundant)</td>';
  434. $table .= '<td style="background-color:#ccc;font-weight:bold;">'.$totalExecutionTime.'</td>';
  435. $table .= '<td style="background-color:#ccc;font-weight:bold;"># executed</td>';
  436. $table .= '</tr>';
  437. $table .= $str;
  438. $table .= '</table>';
  439. // Finally, echo the doubles table
  440. echo $table;
  441. }
  442. }
  443. /**
  444. * Print Query log to console
  445. *
  446. * This will write a list off all performed SQL queries to the console. An
  447. * optional name parameter can be provided for the name of the log file,
  448. * which will default to 'qryLog'.
  449. *
  450. * Note that timing information and check for double queries will NOT be
  451. * available here. Also the setLogQueries needs to be set to TRUE for this
  452. * to work.
  453. *
  454. * This can be sepecially useful for debugging ajax request, live websites, or
  455. * controllers that do a Redirect.
  456. *
  457. * @param string $console
  458. */
  459. public static function printQueryLogToConsole($console = 'qryLog') {
  460. $log = self::getQueryLog();
  461. $console = M_Console::getInstance($console);
  462. foreach ($log as $qry) {
  463. if (array_key_exists('sql', $qry)) {
  464. $console->write($qry['sql']);
  465. }
  466. }
  467. }
  468. }