PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/leganto/libs/Nette/Debug.php

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