PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/libs/Nette/Debug/Debug.php

https://github.com/Vrtak-CZ/ORM-benchmark
PHP | 974 lines | 603 code | 180 blank | 191 comment | 99 complexity | b4b878426f7eea602b67adf8343878d6 MD5 | raw file
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nette.org/license Nette license
  7. * @link http://nette.org
  8. * @category Nette
  9. * @package Nette
  10. */
  11. namespace Nette;
  12. use Nette,
  13. Nette\Environment;
  14. /**
  15. * Debug static class.
  16. *
  17. * @copyright Copyright (c) 2004, 2010 David Grudl
  18. * @package Nette
  19. */
  20. final class Debug
  21. {
  22. /** @var bool determines whether a server is running in production mode */
  23. public static $productionMode;
  24. /** @var bool determines whether a server is running in console mode */
  25. public static $consoleMode;
  26. /** @var int */
  27. public static $time;
  28. /** @var bool is Firebug & FirePHP detected? */
  29. private static $firebugDetected;
  30. /** @var bool is AJAX request detected? */
  31. private static $ajaxDetected;
  32. /** @var string */
  33. private static $uri;
  34. /********************* Debug::dump() ****************d*g**/
  35. /** @var int how many nested levels of array/object properties display {@link Debug::dump()} */
  36. public static $maxDepth = 3;
  37. /** @var int how long strings display {@link Debug::dump()} */
  38. public static $maxLen = 150;
  39. /** @var int display location? {@link Debug::dump()} */
  40. public static $showLocation = FALSE;
  41. /********************* errors and exceptions reporing ****************d*g**/
  42. /**#@+ server modes {@link Debug::enable()} */
  43. const DEVELOPMENT = FALSE;
  44. const PRODUCTION = TRUE;
  45. const DETECT = NULL;
  46. /**#@-*/
  47. /** @var bool determines whether to consider all errors as fatal */
  48. public static $strictMode = FALSE;
  49. /** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */
  50. public static $scream = FALSE;
  51. /** @var array of callbacks specifies the functions that are automatically called after fatal error */
  52. public static $onFatalError = array();
  53. /** @var callback */
  54. public static $mailer = array(__CLASS__, 'defaultMailer');
  55. /** @var int interval for sending email is 2 days */
  56. public static $emailSnooze = 172800;
  57. /** @var bool {@link Debug::enable()} */
  58. private static $enabled = FALSE;
  59. /** @var string name of the file where script errors should be logged */
  60. private static $logFile;
  61. /** @var resource */
  62. private static $logHandle;
  63. /** @var bool send e-mail notifications of errors? */
  64. private static $sendEmails;
  65. /** @var string e-mail headers & body */
  66. private static $emailHeaders = array(
  67. 'To' => '',
  68. 'From' => 'noreply@%host%',
  69. 'X-Mailer' => 'Nette Framework',
  70. 'Subject' => 'PHP: An error occurred on the server %host%',
  71. 'Body' => '[%date%] %message%',
  72. );
  73. /********************* debug bar ****************d*g**/
  74. /** @var bool */
  75. public static $showBar = TRUE;
  76. /** @var array */
  77. private static $panels = array();
  78. /** @var array payload filled by {@link Debug::barDump()} */
  79. private static $dumps;
  80. /** @var array payload filled by {@link Debug::_errorHandler()} */
  81. private static $errors;
  82. /** @deprecated */
  83. public static $counters = array();
  84. /********************* Firebug extension ****************d*g**/
  85. /**#@+ FirePHP log priority */
  86. const LOG = 'LOG';
  87. const INFO = 'INFO';
  88. const WARN = 'WARN';
  89. const ERROR = 'ERROR';
  90. const TRACE = 'TRACE';
  91. const EXCEPTION = 'EXCEPTION';
  92. const GROUP_START = 'GROUP_START';
  93. const GROUP_END = 'GROUP_END';
  94. /**#@-*/
  95. /**
  96. * Static class - cannot be instantiated.
  97. */
  98. final public function __construct()
  99. {
  100. throw new \LogicException("Cannot instantiate static class " . get_class($this));
  101. }
  102. /**
  103. * Static class constructor.
  104. * @ignore internal
  105. */
  106. public static function _init()
  107. {
  108. self::$time = microtime(TRUE);
  109. self::$consoleMode = PHP_SAPI === 'cli';
  110. self::$productionMode = self::DETECT;
  111. self::$firebugDetected = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'FirePHP/');
  112. self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
  113. if (isset($_SERVER['REQUEST_URI'])) {
  114. self::$uri = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://')
  115. . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''))
  116. . $_SERVER['REQUEST_URI'];
  117. }
  118. $tab = array(__CLASS__, 'renderTab'); $panel = array(__CLASS__, 'renderPanel');
  119. self::addPanel(new DebugPanel('time', $tab, $panel));
  120. self::addPanel(new DebugPanel('memory', $tab, $panel));
  121. self::addPanel(new DebugPanel('errors', $tab, $panel));
  122. self::addPanel(new DebugPanel('dumps', $tab, $panel));
  123. }
  124. /********************* useful tools ****************d*g**/
  125. /**
  126. * Dumps information about a variable in readable format.
  127. * @param mixed variable to dump
  128. * @param bool return output instead of printing it? (bypasses $productionMode)
  129. * @return mixed variable itself or dump
  130. */
  131. public static function dump($var, $return = FALSE)
  132. {
  133. if (!$return && self::$productionMode) {
  134. return $var;
  135. }
  136. $output = "<pre class=\"nette-dump\">" . self::_dump($var, 0) . "</pre>\n";
  137. if (!$return && self::$showLocation) {
  138. $trace = debug_backtrace();
  139. $i = isset($trace[1]['class']) && $trace[1]['class'] === __CLASS__ ? 1 : 0;
  140. if (isset($trace[$i]['file'], $trace[$i]['line'])) {
  141. $output = substr_replace($output, ' <small>' . htmlspecialchars("in file {$trace[$i]['file']} on line {$trace[$i]['line']}", ENT_NOQUOTES) . '</small>', -8, 0);
  142. }
  143. }
  144. if (self::$consoleMode) {
  145. $output = htmlspecialchars_decode(strip_tags($output), ENT_NOQUOTES);
  146. }
  147. if ($return) {
  148. return $output;
  149. } else {
  150. echo $output;
  151. return $var;
  152. }
  153. }
  154. /**
  155. * Dumps information about a variable in Nette Debug Bar.
  156. * @param mixed variable to dump
  157. * @param string optional title
  158. * @return mixed variable itself
  159. */
  160. public static function barDump($var, $title = NULL)
  161. {
  162. if (!self::$productionMode) {
  163. $dump = array();
  164. foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
  165. $dump[$key] = self::dump($val, TRUE);
  166. }
  167. self::$dumps[] = array('title' => $title, 'dump' => $dump);
  168. }
  169. return $var;
  170. }
  171. /**
  172. * Internal dump() implementation.
  173. * @param mixed variable to dump
  174. * @param int current recursion level
  175. * @return string
  176. */
  177. private static function _dump(&$var, $level)
  178. {
  179. static $tableUtf, $tableBin, $reBinary = '#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{10FFFF}]#u';
  180. if ($tableUtf === NULL) {
  181. foreach (range("\x00", "\xFF") as $ch) {
  182. if (ord($ch) < 32 && strpos("\r\n\t", $ch) === FALSE) $tableUtf[$ch] = $tableBin[$ch] = '\\x' . str_pad(dechex(ord($ch)), 2, '0', STR_PAD_LEFT);
  183. elseif (ord($ch) < 127) $tableUtf[$ch] = $tableBin[$ch] = $ch;
  184. else { $tableUtf[$ch] = $ch; $tableBin[$ch] = '\\x' . dechex(ord($ch)); }
  185. }
  186. $tableBin["\\"] = '\\\\';
  187. $tableBin["\r"] = '\\r';
  188. $tableBin["\n"] = '\\n';
  189. $tableBin["\t"] = '\\t';
  190. $tableUtf['\\x'] = $tableBin['\\x'] = '\\\\x';
  191. }
  192. if (is_bool($var)) {
  193. return ($var ? 'TRUE' : 'FALSE') . "\n";
  194. } elseif ($var === NULL) {
  195. return "NULL\n";
  196. } elseif (is_int($var)) {
  197. return "$var\n";
  198. } elseif (is_float($var)) {
  199. $var = (string) $var;
  200. if (strpos($var, '.') === FALSE) $var .= '.0';
  201. return "$var\n";
  202. } elseif (is_string($var)) {
  203. if (self::$maxLen && strlen($var) > self::$maxLen) {
  204. $s = htmlSpecialChars(substr($var, 0, self::$maxLen), ENT_NOQUOTES) . ' ... ';
  205. } else {
  206. $s = htmlSpecialChars($var, ENT_NOQUOTES);
  207. }
  208. $s = strtr($s, preg_match($reBinary, $s) || preg_last_error() ? $tableBin : $tableUtf);
  209. $len = strlen($var);
  210. return "\"$s\"" . ($len > 1 ? " ($len)" : "") . "\n";
  211. } elseif (is_array($var)) {
  212. $s = "<span>array</span>(" . count($var) . ") ";
  213. $space = str_repeat($space1 = ' ', $level);
  214. $brackets = range(0, count($var) - 1) === array_keys($var) ? "[]" : "{}";
  215. static $marker;
  216. if ($marker === NULL) $marker = uniqid("\x00", TRUE);
  217. if (empty($var)) {
  218. } elseif (isset($var[$marker])) {
  219. $brackets = $var[$marker];
  220. $s .= "$brackets[0] *RECURSION* $brackets[1]";
  221. } elseif ($level < self::$maxDepth || !self::$maxDepth) {
  222. $s .= "<code>$brackets[0]\n";
  223. $var[$marker] = $brackets;
  224. foreach ($var as $k => &$v) {
  225. if ($k === $marker) continue;
  226. $k = is_int($k) ? $k : '"' . strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf) . '"';
  227. $s .= "$space$space1$k => " . self::_dump($v, $level + 1);
  228. }
  229. unset($var[$marker]);
  230. $s .= "$space$brackets[1]</code>";
  231. } else {
  232. $s .= "$brackets[0] ... $brackets[1]";
  233. }
  234. return $s . "\n";
  235. } elseif (is_object($var)) {
  236. $arr = (array) $var;
  237. $s = "<span>" . get_class($var) . "</span>(" . count($arr) . ") ";
  238. $space = str_repeat($space1 = ' ', $level);
  239. static $list = array();
  240. if (empty($arr)) {
  241. } elseif (in_array($var, $list, TRUE)) {
  242. $s .= "{ *RECURSION* }";
  243. } elseif ($level < self::$maxDepth || !self::$maxDepth) {
  244. $s .= "<code>{\n";
  245. $list[] = $var;
  246. foreach ($arr as $k => &$v) {
  247. $m = '';
  248. if ($k[0] === "\x00") {
  249. $m = $k[1] === '*' ? ' <span>protected</span>' : ' <span>private</span>';
  250. $k = substr($k, strrpos($k, "\x00") + 1);
  251. }
  252. $k = strtr($k, preg_match($reBinary, $k) || preg_last_error() ? $tableBin : $tableUtf);
  253. $s .= "$space$space1\"$k\"$m => " . self::_dump($v, $level + 1);
  254. }
  255. array_pop($list);
  256. $s .= "$space}</code>";
  257. } else {
  258. $s .= "{ ... }";
  259. }
  260. return $s . "\n";
  261. } elseif (is_resource($var)) {
  262. return "<span>" . get_resource_type($var) . " resource</span>\n";
  263. } else {
  264. return "<span>unknown type</span>\n";
  265. }
  266. }
  267. /**
  268. * Starts/stops stopwatch.
  269. * @param string name
  270. * @return elapsed seconds
  271. */
  272. public static function timer($name = NULL)
  273. {
  274. static $time = array();
  275. $now = microtime(TRUE);
  276. $delta = isset($time[$name]) ? $now - $time[$name] : 0;
  277. $time[$name] = $now;
  278. return $delta;
  279. }
  280. /********************* errors and exceptions reporing ****************d*g**/
  281. /**
  282. * Enables displaying or logging errors and exceptions.
  283. * @param mixed production, development mode, autodetection or IP address(es).
  284. * @param string error log file (FALSE disables logging in production mode)
  285. * @param array|string administrator email or email headers; enables email sending in production mode
  286. * @return void
  287. */
  288. public static function enable($mode = NULL, $logFile = NULL, $email = NULL)
  289. {
  290. error_reporting(E_ALL | E_STRICT);
  291. // production/development mode detection
  292. if (is_bool($mode)) {
  293. self::$productionMode = $mode;
  294. } elseif (is_string($mode)) { // IP adresses
  295. $mode = preg_split('#[,\s]+#', $mode);
  296. }
  297. if (is_array($mode)) { // IP adresses
  298. self::$productionMode = !isset($_SERVER['REMOTE_ADDR']) || !in_array($_SERVER['REMOTE_ADDR'], $mode, TRUE);
  299. }
  300. if (self::$productionMode === self::DETECT) {
  301. if (class_exists('Nette\Environment')) {
  302. self::$productionMode = Environment::isProduction();
  303. } elseif (isset($_SERVER['SERVER_ADDR']) || isset($_SERVER['LOCAL_ADDR'])) { // IP address based detection
  304. $addr = isset($_SERVER['SERVER_ADDR']) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR'];
  305. $oct = explode('.', $addr);
  306. self::$productionMode = $addr !== '::1' && (count($oct) !== 4 || ($oct[0] !== '10' && $oct[0] !== '127' && ($oct[0] !== '172' || $oct[1] < 16 || $oct[1] > 31)
  307. && ($oct[0] !== '169' || $oct[1] !== '254') && ($oct[0] !== '192' || $oct[1] !== '168')));
  308. } else {
  309. self::$productionMode = !self::$consoleMode;
  310. }
  311. }
  312. // logging configuration
  313. if (self::$productionMode && $logFile !== FALSE) {
  314. self::$logFile = 'log/php_error.log';
  315. if (class_exists('Nette\Environment')) {
  316. if (is_string($logFile)) {
  317. self::$logFile = Environment::expand($logFile);
  318. } else try {
  319. self::$logFile = Environment::expand('%logDir%/php_error.log');
  320. } catch (\InvalidStateException $e) {
  321. }
  322. } elseif (is_string($logFile)) {
  323. self::$logFile = $logFile;
  324. }
  325. ini_set('error_log', self::$logFile);
  326. }
  327. // php configuration
  328. if (function_exists('ini_set')) {
  329. ini_set('display_errors', !self::$productionMode); // or 'stderr'
  330. ini_set('html_errors', FALSE);
  331. ini_set('log_errors', FALSE);
  332. } elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally ==
  333. throw new \NotSupportedException('Function ini_set() must be enabled.');
  334. }
  335. self::$sendEmails = self::$logFile && $email;
  336. if (self::$sendEmails) {
  337. if (is_string($email)) {
  338. self::$emailHeaders['To'] = $email;
  339. } elseif (is_array($email)) {
  340. self::$emailHeaders = $email + self::$emailHeaders;
  341. }
  342. }
  343. if (!defined('E_DEPRECATED')) {
  344. define('E_DEPRECATED', 8192);
  345. }
  346. if (!defined('E_USER_DEPRECATED')) {
  347. define('E_USER_DEPRECATED', 16384);
  348. }
  349. register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
  350. set_exception_handler(array(__CLASS__, '_exceptionHandler'));
  351. set_error_handler(array(__CLASS__, '_errorHandler'));
  352. self::$enabled = TRUE;
  353. }
  354. /**
  355. * Is Debug enabled?
  356. * @return bool
  357. */
  358. public static function isEnabled()
  359. {
  360. return self::$enabled;
  361. }
  362. /**
  363. * Log user error.
  364. * @param string
  365. * @return void
  366. */
  367. public static function log($message)
  368. {
  369. error_log(@date('[Y-m-d H-i-s] ') . trim($message) . PHP_EOL, 3, self::$logFile);
  370. }
  371. /**
  372. * Starts catching potential errors/warnings.
  373. * @return void
  374. */
  375. public static function tryError()
  376. {
  377. error_reporting(0);
  378. trigger_error(''); // "reset" error_get_last
  379. }
  380. /**
  381. * Returns catched error/warning message.
  382. * @param string catched message
  383. * @return bool
  384. */
  385. public static function catchError(& $message)
  386. {
  387. error_reporting(E_ALL | E_STRICT);
  388. $error = error_get_last();
  389. if ($error && $error['message'] !== '') {
  390. $message = $error['message'];
  391. return TRUE;
  392. } else {
  393. $message = NULL;
  394. return FALSE;
  395. }
  396. }
  397. /**
  398. * Shutdown handler to execute of the planned activities.
  399. * @return void
  400. * @ignore internal
  401. */
  402. public static function _shutdownHandler()
  403. {
  404. // 1) fatal error handler
  405. static $types = array(
  406. E_ERROR => 1,
  407. E_CORE_ERROR => 1,
  408. E_COMPILE_ERROR => 1,
  409. E_PARSE => 1,
  410. );
  411. $error = error_get_last();
  412. if (isset($types[$error['type']])) {
  413. if (!headers_sent()) { // for PHP < 5.2.4
  414. header('HTTP/1.1 500 Internal Server Error');
  415. }
  416. self::processException(new \FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL), TRUE);
  417. }
  418. // 2) debug bar (require HTML & development mode)
  419. if (self::$showBar && !self::$productionMode && !self::$ajaxDetected && !self::$consoleMode) {
  420. foreach (headers_list() as $header) {
  421. if (strncasecmp($header, 'Content-Type:', 13) === 0) {
  422. if (substr($header, 14, 9) === 'text/html') {
  423. break;
  424. }
  425. return;
  426. }
  427. }
  428. $panels = array();
  429. foreach (self::$panels as $panel) {
  430. $panels[] = array(
  431. 'id' => preg_replace('#[^a-z0-9]+#i', '-', $panel->getId()),
  432. 'tab' => $tab = (string) $panel->getTab(),
  433. 'panel' => $tab ? (string) $panel->getPanel() : NULL,
  434. );
  435. }
  436. require __DIR__ . '/templates/bar.phtml';
  437. }
  438. }
  439. /**
  440. * Debug exception handler.
  441. * @param \Exception
  442. * @return void
  443. * @ignore internal
  444. */
  445. public static function _exceptionHandler(\Exception $exception)
  446. {
  447. if (!headers_sent()) {
  448. header('HTTP/1.1 500 Internal Server Error');
  449. }
  450. self::processException($exception, TRUE);
  451. exit;
  452. }
  453. /**
  454. * Own error handler.
  455. * @param int level of the error raised
  456. * @param string error message
  457. * @param string file that the error was raised in
  458. * @param int line number the error was raised at
  459. * @param array an array of variables that existed in the scope the error was triggered in
  460. * @return bool FALSE to call normal error handler, NULL otherwise
  461. * @throws \FatalErrorException
  462. * @ignore internal
  463. */
  464. public static function _errorHandler($severity, $message, $file, $line, $context)
  465. {
  466. if (self::$scream) {
  467. error_reporting(E_ALL | E_STRICT);
  468. }
  469. if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
  470. throw new \FatalErrorException($message, 0, $severity, $file, $line, $context);
  471. } elseif (($severity & error_reporting()) !== $severity) {
  472. return FALSE; // calls normal error handler to fill-in error_get_last()
  473. } elseif (self::$strictMode) {
  474. self::_exceptionHandler(new \FatalErrorException($message, 0, $severity, $file, $line, $context), TRUE);
  475. }
  476. static $types = array(
  477. E_WARNING => 'Warning',
  478. E_COMPILE_WARNING => 'Warning', // currently unable to handle
  479. E_USER_WARNING => 'Warning',
  480. E_NOTICE => 'Notice',
  481. E_USER_NOTICE => 'Notice',
  482. E_STRICT => 'Strict standards',
  483. E_DEPRECATED => 'Deprecated',
  484. E_USER_DEPRECATED => 'Deprecated',
  485. );
  486. $message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message in $file:$line";
  487. if (self::$logFile) {
  488. if (self::$uri) $message .= ' @ ' . self::$uri;
  489. self::log($message); // log manually, required on some stupid hostings
  490. self::sendEmail($message);
  491. return NULL;
  492. } elseif (!self::$productionMode) {
  493. if (self::$showBar) {
  494. self::$errors[] = $message;
  495. }
  496. if (self::$firebugDetected && !headers_sent()) {
  497. self::fireLog(strip_tags($message), self::ERROR);
  498. }
  499. return self::$consoleMode || (!self::$showBar && !self::$ajaxDetected) ? FALSE : NULL;
  500. }
  501. return FALSE; // call normal error handler
  502. }
  503. /**
  504. * Logs or displays exception.
  505. * @param \Exception
  506. * @param bool is writing to standard output buffer allowed?
  507. * @return void
  508. */
  509. public static function processException(\Exception $exception, $outputAllowed = FALSE)
  510. {
  511. if (!self::$enabled) {
  512. return;
  513. } elseif (self::$logFile) {
  514. try {
  515. $hash = md5($exception );
  516. self::log("PHP Fatal error: Uncaught exception " . get_class($exception) . " with message '" . $exception->getMessage() . "' in " . $exception->getFile() . ":" . $exception->getLine() . (self::$uri ? ' @ ' . self::$uri : ''));
  517. foreach (new \DirectoryIterator(dirname(self::$logFile)) as $entry) {
  518. if (strpos($entry, $hash)) {
  519. $skip = TRUE;
  520. break;
  521. }
  522. }
  523. if (empty($skip) && self::$logHandle = @fopen(dirname(self::$logFile) . "/exception " . @date('Y-m-d H-i-s') . " $hash.html", 'w')) {
  524. ob_start(); // double buffer prevents sending HTTP headers in some PHP
  525. ob_start(array(__CLASS__, '_writeFile'), 1);
  526. self::_paintBlueScreen($exception);
  527. ob_end_flush();
  528. ob_end_clean();
  529. fclose(self::$logHandle);
  530. }
  531. self::sendEmail("$exception\n\n" . self::$uri);
  532. } catch (\Exception $e) {
  533. if (!headers_sent()) {
  534. header('HTTP/1.1 500 Internal Server Error');
  535. }
  536. echo 'Nette\Debug fatal error: ', get_class($e), ': ', ($e->getCode() ? '#' . $e->getCode() . ' ' : '') . $e->getMessage(), "\n";
  537. exit;
  538. }
  539. } elseif (self::$productionMode) {
  540. // be quiet
  541. } elseif (self::$consoleMode) { // dump to console
  542. if ($outputAllowed) {
  543. echo "$exception\n";
  544. }
  545. } elseif (self::$firebugDetected && self::$ajaxDetected && !headers_sent()) { // AJAX mode
  546. self::fireLog($exception, self::EXCEPTION);
  547. } elseif ($outputAllowed) { // dump to browser
  548. if (!headers_sent()) {
  549. @ob_end_clean(); while (ob_get_level() && @ob_end_clean());
  550. header_remove('Content-Encoding');
  551. }
  552. self::_paintBlueScreen($exception);
  553. } elseif (self::$firebugDetected && !headers_sent()) {
  554. self::fireLog($exception, self::EXCEPTION);
  555. }
  556. foreach (self::$onFatalError as $handler) {
  557. call_user_func($handler, $exception);
  558. }
  559. }
  560. /**
  561. * Handles exception throwed in __toString().
  562. * @param \Exception
  563. * @return void
  564. */
  565. public static function toStringException(\Exception $exception)
  566. {
  567. if (self::$enabled) {
  568. self::_exceptionHandler($exception);
  569. } else {
  570. trigger_error($exception->getMessage(), E_USER_ERROR);
  571. }
  572. }
  573. /**
  574. * Paint blue screen.
  575. * @param \Exception
  576. * @return void
  577. * @ignore internal
  578. */
  579. public static function _paintBlueScreen(\Exception $exception)
  580. {
  581. if (class_exists('Nette\Environment', FALSE)) {
  582. $application = Environment::getServiceLocator()->hasService('Nette\\Application\\Application', TRUE) ? Environment::getServiceLocator()->getService('Nette\\Application\\Application') : NULL;
  583. }
  584. require __DIR__ . '/templates/bluescreen.phtml';
  585. }
  586. /**
  587. * Redirects output to file.
  588. * @param string
  589. * @return string
  590. * @ignore internal
  591. */
  592. public static function _writeFile($buffer)
  593. {
  594. fwrite(self::$logHandle, $buffer);
  595. }
  596. /**
  597. * Sends e-mail notification.
  598. * @param string
  599. * @return void
  600. */
  601. private static function sendEmail($message)
  602. {
  603. $monitorFile = self::$logFile . '.monitor';
  604. if (self::$sendEmails && @filemtime($monitorFile) + self::$emailSnooze < time() // @ - file may not exist
  605. && @file_put_contents($monitorFile, 'sent')) { // @ - file may not be writable
  606. call_user_func(self::$mailer, $message);
  607. }
  608. }
  609. /**
  610. * Default mailer.
  611. * @param string
  612. * @return void
  613. */
  614. private static function defaultMailer($message)
  615. {
  616. $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] :
  617. (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : '');
  618. $headers = str_replace(
  619. array('%host%', '%date%', '%message%'),
  620. array($host, @date('Y-m-d H:i:s', self::$time), $message), // @ - timezone may not be set
  621. self::$emailHeaders
  622. );
  623. $subject = $headers['Subject'];
  624. $to = $headers['To'];
  625. $body = str_replace("\n", PHP_EOL, str_replace("\r\n", "\n", $headers['Body']));
  626. unset($headers['Subject'], $headers['To'], $headers['Body']);
  627. $header = '';
  628. foreach ($headers as $key => $value) {
  629. $header .= "$key: $value" . PHP_EOL;
  630. }
  631. mail($to, $subject, $body, $header);
  632. }
  633. /********************* debug bar ****************d*g**/
  634. /**
  635. * Add custom panel.
  636. * @param IDebugPanel
  637. * @return void
  638. */
  639. public static function addPanel(IDebugPanel $panel)
  640. {
  641. self::$panels[] = $panel;
  642. }
  643. /**
  644. * Renders default panel.
  645. * @param string
  646. * @return void
  647. * @ignore internal
  648. */
  649. public static function renderTab($id)
  650. {
  651. switch ($id) {
  652. case 'time':
  653. require __DIR__ . '/templates/bar.time.tab.phtml';
  654. return;
  655. case 'memory':
  656. require __DIR__ . '/templates/bar.memory.tab.phtml';
  657. return;
  658. case 'dumps':
  659. if (!Debug::$dumps) return;
  660. require __DIR__ . '/templates/bar.dumps.tab.phtml';
  661. return;
  662. case 'errors':
  663. if (!Debug::$errors) return;
  664. require __DIR__ . '/templates/bar.errors.tab.phtml';
  665. }
  666. }
  667. /**
  668. * Renders default panel.
  669. * @param string
  670. * @return void
  671. * @ignore internal
  672. */
  673. public static function renderPanel($id)
  674. {
  675. switch ($id) {
  676. case 'dumps':
  677. require __DIR__ . '/templates/bar.dumps.panel.phtml';
  678. return;
  679. case 'errors':
  680. require __DIR__ . '/templates/bar.errors.panel.phtml';
  681. }
  682. }
  683. /** @deprecated */
  684. public static function addColophon(){}
  685. /** @deprecated */
  686. public static function consoleDump(){}
  687. /********************* Firebug extension ****************d*g**/
  688. /**
  689. * Sends message to Firebug console.
  690. * @param mixed message to log
  691. * @param string priority of message (LOG, INFO, WARN, ERROR, GROUP_START, GROUP_END)
  692. * @param string optional label
  693. * @return bool was successful?
  694. */
  695. public static function fireLog($message, $priority = self::LOG, $label = NULL)
  696. {
  697. if ($message instanceof \Exception) {
  698. if ($priority !== self::EXCEPTION && $priority !== self::TRACE) {
  699. $priority = self::TRACE;
  700. }
  701. $message = array(
  702. 'Class' => get_class($message),
  703. 'Message' => $message->getMessage(),
  704. 'File' => $message->getFile(),
  705. 'Line' => $message->getLine(),
  706. 'Trace' => $message->getTrace(),
  707. 'Type' => '',
  708. 'Function' => '',
  709. );
  710. foreach ($message['Trace'] as & $row) {
  711. if (empty($row['file'])) $row['file'] = '?';
  712. if (empty($row['line'])) $row['line'] = '?';
  713. }
  714. } elseif ($priority === self::GROUP_START) {
  715. $label = $message;
  716. $message = NULL;
  717. }
  718. return self::fireSend('FirebugConsole/0.1', self::replaceObjects(array(array('Type' => $priority, 'Label' => $label), $message)));
  719. }
  720. /**
  721. * Performs Firebug output.
  722. * @see http://www.firephp.org
  723. * @param string structure
  724. * @param array payload
  725. * @return bool was successful?
  726. */
  727. private static function fireSend($struct, $payload)
  728. {
  729. if (self::$productionMode) return NULL;
  730. if (headers_sent()) return FALSE; // or throw exception?
  731. header('X-Wf-Protocol-nette: http://meta.wildfirehq.org/Protocol/JsonStream/0.2');
  732. header('X-Wf-nette-Plugin-1: http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.2.0');
  733. static $structures;
  734. $index = isset($structures[$struct]) ? $structures[$struct] : ($structures[$struct] = count($structures) + 1);
  735. header("X-Wf-nette-Structure-$index: http://meta.firephp.org/Wildfire/Structure/FirePHP/$struct");
  736. $payload = json_encode($payload);
  737. static $counter;
  738. foreach (str_split($payload, 4990) as $s) {
  739. $num = ++$counter;
  740. header("X-Wf-nette-$index-1-n$num: |$s|\\");
  741. }
  742. header("X-Wf-nette-$index-1-n$num: |$s|");
  743. return TRUE;
  744. }
  745. /**
  746. * fireLog helper.
  747. * @param mixed
  748. * @return mixed
  749. */
  750. static private function replaceObjects($val)
  751. {
  752. if (is_object($val)) {
  753. return 'object ' . get_class($val) . '';
  754. } elseif (is_string($val)) {
  755. return @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $val)); // intentionally @
  756. } elseif (is_array($val)) {
  757. foreach ($val as $k => $v) {
  758. unset($val[$k]);
  759. $k = @iconv('UTF-16', 'UTF-8//IGNORE', iconv('UTF-8', 'UTF-16//IGNORE', $k)); // intentionally @
  760. $val[$k] = self::replaceObjects($v);
  761. }
  762. }
  763. return $val;
  764. }
  765. }
  766. Debug::_init();