PageRenderTime 125ms CodeModel.GetById 38ms RepoModel.GetById 1ms app.codeStats 0ms

/Nette/Debug.php

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