PageRenderTime 66ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/libs/Nette/Debug.php

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