PageRenderTime 49ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/www/libs/nette-dev/Debug.php

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