ACC SHELL

Path : /srv/www/vhosts/centrumlb/3rdparty/Nette/Diagnostics/
File Upload :
Current File : /srv/www/vhosts/centrumlb/3rdparty/Nette/Diagnostics/Debugger.php

<?php

/**
 * This file is part of the Nette Framework (http://nette.org)
 * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
 * @package Nette\Diagnostics
 */



/**
 * Debugger: displays and logs errors.
 *
 * Behavior is determined by two factors: mode & output
 * - modes: production / development
 * - output: HTML / AJAX / CLI / other (e.g. XML)
 *
 * @author     David Grudl
 * @package Nette\Diagnostics
 */
class NDebugger
{
	/** @var bool in production mode is suppressed any debugging output */
	public static $productionMode;

	/** @var bool in console mode is omitted HTML output */
	public static $consoleMode;

	/** @var int timestamp with microseconds of the start of the request */
	public static $time;

	/** @var bool is AJAX request detected? */
	private static $ajaxDetected;

	/** @var string  requested URI or command line */
	public static $source;

	/** @var string URL pattern mask to open editor */
	public static $editor = 'editor://open/?file=%file&line=%line';

	/** @var string command to open browser (use 'start ""' in Windows) */
	public static $browser;

	/********************* NDebugger::dump() ****************d*g**/

	/** @var int  how many nested levels of array/object properties display {@link NDebugger::dump()} */
	public static $maxDepth = 3;

	/** @var int  how long strings display {@link NDebugger::dump()} */
	public static $maxLen = 150;

	/** @var bool display location? {@link NDebugger::dump()} */
	public static $showLocation = FALSE;

	/** @var array */
	public static $consoleColors = array(
		'bool' => '1;33',
		'null' => '1;33',
		'int' => '1;36',
		'float' => '1;36',
		'string' => '1;32',
		'array' => '1;31',
		'key' => '1;37',
		'object' => '1;31',
		'visibility' => '1;30',
		'resource' => '1;37',
	);

	/********************* errors and exceptions reporting ****************d*g**/

	/** server modes {@link NDebugger::enable()} */
	const DEVELOPMENT = FALSE,
		PRODUCTION = TRUE,
		DETECT = NULL;

	/** @var NDebugBlueScreen */
	public static $blueScreen;

	/** @var bool|int determines whether any error will cause immediate death; if integer that it's matched against error severity */
	public static $strictMode = FALSE; // $immediateDeath

	/** @var bool disables the @ (shut-up) operator so that notices and warnings are no longer hidden */
	public static $scream = FALSE;

	/** @var array of callables specifies the functions that are automatically called after fatal error */
	public static $onFatalError = array();

	/** @var bool {@link NDebugger::enable()} */
	private static $enabled = FALSE;

	/** @var mixed {@link NDebugger::tryError()} FALSE means catching is disabled */
	private static $lastError = FALSE;

	/********************* logging ****************d*g**/

	/** @var NLogger */
	public static $logger;

	/** @var NFireLogger */
	public static $fireLogger;

	/** @var string name of the directory where errors should be logged; FALSE means that logging is disabled */
	public static $logDirectory;

	/** @var string email to sent error notifications */
	public static $email;

	/** @deprecated */
	public static $mailer;

	/** @deprecated */
	public static $emailSnooze;

	/********************* debug bar ****************d*g**/

	/** @var NDebugBar */
	public static $bar;

	/** @var NDefaultBarPanel */
	private static $errorPanel;

	/** @var NDefaultBarPanel */
	private static $dumpPanel;

	/********************* Firebug extension ****************d*g**/

	/** {@link NDebugger::log()} and {@link NDebugger::fireLog()} */
	const DEBUG = 'debug',
		INFO = 'info',
		WARNING = 'warning',
		ERROR = 'error',
		CRITICAL = 'critical';


	/**
	 * Static class - cannot be instantiated.
	 */
	final public function __construct()
	{
		throw new NStaticClassException;
	}


	/**
	 * Static class constructor.
	 * @internal
	 */
	public static function _init()
	{
		self::$time = isset($_SERVER['REQUEST_TIME_FLOAT']) ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime(TRUE);
		self::$consoleMode = PHP_SAPI === 'cli';
		self::$productionMode = self::DETECT;
		if (self::$consoleMode) {
			self::$source = empty($_SERVER['argv']) ? 'cli' : 'cli: ' . implode(' ', $_SERVER['argv']);
		} else {
			self::$ajaxDetected = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
			if (isset($_SERVER['REQUEST_URI'])) {
				self::$source = (isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off') ? 'https://' : 'http://')
					. (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : ''))
					. $_SERVER['REQUEST_URI'];
			}
		}

		self::$logger = new NLogger;
		self::$logDirectory = & self::$logger->directory;
		self::$email = & self::$logger->email;
		self::$mailer = & self::$logger->mailer;
		self::$emailSnooze = & NLogger::$emailSnooze;

		self::$fireLogger = new NFireLogger;

		self::$blueScreen = new NDebugBlueScreen;
		self::$blueScreen->addPanel(create_function('$e', '
			if ($e instanceof NTemplateException) {
				return array(
					\'tab\' => \'Template\',
					\'panel\' => \'<p><b>File:</b> \' . NDebugHelpers::editorLink($e->sourceFile, $e->sourceLine)
					. \'&nbsp; <b>Line:</b> \' . ($e->sourceLine ? $e->sourceLine : \'n/a\') . \'</p>\'
					. ($e->sourceLine ? NDebugBlueScreen::highlightFile($e->sourceFile, $e->sourceLine) : \'\')
				);
			} elseif ($e instanceof NNeonException && preg_match(\'#line (\\d+)#\', $e->getMessage(), $m)) {
				if ($item = NDebugHelpers::findTrace($e->getTrace(), \'NConfigNeonAdapter::load\')) {
					return array(
						\'tab\' => \'NEON\',
						\'panel\' => \'<p><b>File:</b> \' . NDebugHelpers::editorLink($item[\'args\'][0], $m[1]) . \'&nbsp; <b>Line:</b> \' . $m[1] . \'</p>\'
							. NDebugBlueScreen::highlightFile($item[\'args\'][0], $m[1])
					);
				} elseif ($item = NDebugHelpers::findTrace($e->getTrace(), \'NNeon::decode\')) {
					return array(
						\'tab\' => \'NEON\',
						\'panel\' => NDebugBlueScreen::highlightPhp($item[\'args\'][0], $m[1])
					);
				}
			}
		'));

		self::$bar = new NDebugBar;
		self::$bar->addPanel(new NDefaultBarPanel('time'));
		self::$bar->addPanel(new NDefaultBarPanel('memory'));
		self::$bar->addPanel(self::$errorPanel = new NDefaultBarPanel('errors')); // filled by _errorHandler()
		self::$bar->addPanel(self::$dumpPanel = new NDefaultBarPanel('dumps')); // filled by barDump()
	}


	/********************* errors and exceptions reporting ****************d*g**/


	/**
	 * Enables displaying or logging errors and exceptions.
	 * @param  mixed         production, development mode, autodetection or IP address(es) whitelist.
	 * @param  string        error log directory; enables logging in production mode, FALSE means that logging is disabled
	 * @param  string        administrator email; enables email sending in production mode
	 * @return void
	 */
	public static function enable($mode = NULL, $logDirectory = NULL, $email = NULL)
	{
		error_reporting(E_ALL | E_STRICT);

		// production/development mode detection
		if (is_bool($mode)) {
			self::$productionMode = $mode;

		} elseif ($mode !== self::DETECT || self::$productionMode === NULL) { // IP addresses or computer names whitelist detection
			$list = is_string($mode) ? preg_split('#[,\s]+#', $mode) : (array) $mode;
			if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
				$list[] = '127.0.0.1';
				$list[] = '::1';
			}
			self::$productionMode = !in_array(isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : php_uname('n'), $list, TRUE);
		}

		// logging configuration
		if (is_string($logDirectory)) {
			self::$logDirectory = realpath($logDirectory);
			if (self::$logDirectory === FALSE) {
				echo __METHOD__ . "() error: Log directory is not found or is not directory.\n";
				exit(254);
			}
		} elseif ($logDirectory === FALSE) {
			self::$logDirectory = FALSE;

		} elseif (self::$logDirectory === NULL) {
			self::$logDirectory = defined('APP_DIR') ? APP_DIR . '/../log' : getcwd() . '/log';
		}
		if (self::$logDirectory) {
			ini_set('error_log', self::$logDirectory . '/php_error.log');
		}

		// php configuration
		if (function_exists('ini_set')) {
			ini_set('display_errors', !self::$productionMode); // or 'stderr'
			ini_set('html_errors', FALSE);
			ini_set('log_errors', FALSE);

		} elseif (ini_get('display_errors') != !self::$productionMode && ini_get('display_errors') !== (self::$productionMode ? 'stderr' : 'stdout')) { // intentionally ==
			echo __METHOD__ . "() error: Unable to set 'display_errors' because function ini_set() is disabled.\n";
			exit(254);
		}

		if ($email) {
			if (!is_string($email)) {
				echo __METHOD__ . "() error: Email address must be a string.\n";
				exit(254);
			}
			self::$email = $email;
		}

		if (!defined('E_DEPRECATED')) {
			define('E_DEPRECATED', 8192);
		}

		if (!defined('E_USER_WARNING')) {
			define('E_USER_WARNING', 16384);
		}

		if (!self::$enabled) {
			register_shutdown_function(array(__CLASS__, '_shutdownHandler'));
			set_exception_handler(array(__CLASS__, '_exceptionHandler'));
			set_error_handler(array(__CLASS__, '_errorHandler'));
			self::$enabled = TRUE;
		}
	}


	/**
	 * Is Debug enabled?
	 * @return bool
	 */
	public static function isEnabled()
	{
		return self::$enabled;
	}


	/**
	 * Logs message or exception to file (if not disabled) and sends email notification (if enabled).
	 * @param  string|Exception
	 * @param  int  one of constant NDebugger::INFO, WARNING, ERROR (sends email), CRITICAL (sends email)
	 * @return string logged error filename
	 */
	public static function log($message, $priority = self::INFO)
	{
		if (self::$logDirectory === FALSE) {
			return;

		} elseif (!self::$logDirectory) {
			throw new InvalidStateException('Logging directory is not specified in NDebugger::$logDirectory.');
		}

		if ($message instanceof Exception) {
			$exception = $message;
			$message = ($message instanceof FatalErrorException
				? 'Fatal error: ' . $exception->getMessage()
				: get_class($exception) . ": " . $exception->getMessage())
				. " in " . $exception->getFile() . ":" . $exception->getLine();

			$hash = md5(preg_replace('~(Resource id #)\d+~', '$1', $exception . (method_exists($exception, 'getPrevious') ? $exception->getPrevious() : (isset($exception->previous) ? $exception->previous : ''))));
			$exceptionFilename = "exception-" . @date('Y-m-d-H-i-s') . "-$hash.html";
			foreach (new DirectoryIterator(self::$logDirectory) as $entry) {
				if (strpos($entry, $hash)) {
					$exceptionFilename = $entry;
					$saved = TRUE;
					break;
				}
			}
		}

		self::$logger->log(array(
			@date('[Y-m-d H-i-s]'),
			trim($message),
			self::$source ? ' @  ' . self::$source : NULL,
			!empty($exceptionFilename) ? ' @@  ' . $exceptionFilename : NULL
		), $priority);

		if (!empty($exceptionFilename)) {
			$exceptionFilename = self::$logDirectory . '/' . $exceptionFilename;
			if (empty($saved) && $logHandle = @fopen($exceptionFilename, 'w')) {
				ob_start(); // double buffer prevents sending HTTP headers in some PHP
				ob_start(create_function('$buffer', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('logHandle'=>$logHandle)).'-1], EXTR_REFS); fwrite($logHandle, $buffer); '), 4096);
				self::$blueScreen->render($exception);
				ob_end_flush();
				ob_end_clean();
				fclose($logHandle);
			}
			return strtr($exceptionFilename, '\\/', DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR);
		}
	}


	/**
	 * Shutdown handler to catch fatal errors and execute of the planned activities.
	 * @return void
	 * @internal
	 */
	public static function _shutdownHandler()
	{
		if (!self::$enabled) {
			return;
		}

		// fatal error handler
		static $types = array(
			E_ERROR => 1,
			E_CORE_ERROR => 1,
			E_COMPILE_ERROR => 1,
			E_PARSE => 1,
		);
		$error = error_get_last();
		if (isset($types[$error['type']])) {
			self::_exceptionHandler(new FatalErrorException($error['message'], 0, $error['type'], $error['file'], $error['line'], NULL));
		}

		// debug bar (require HTML & development mode)
		if (self::$bar && !self::$productionMode && self::isHtmlMode()) {
			self::$bar->render();
		}
	}


	/**
	 * Handler to catch uncaught exception.
	 * @param  Exception
	 * @return void
	 * @internal
	 */
	public static function _exceptionHandler(Exception $exception)
	{
		if (!headers_sent()) { // for PHP < 5.2.4
			$protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1';
			$code = isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE ? 503 : 500;
			header("$protocol $code", TRUE, $code);
		}

		try {
			if (self::$productionMode) {
				try {
					self::log($exception, self::ERROR);
				} catch (Exception $e) {
					echo 'FATAL ERROR: unable to log error';
				}

				if (self::$consoleMode) {
					echo "ERROR: the server encountered an internal error and was unable to complete your request.\n";

				} elseif (self::isHtmlMode()) {
					require dirname(__FILE__) . '/templates/error.phtml';
				}

			} else {
				if (self::$consoleMode) { // dump to console
					echo "$exception\n";
					if ($file = self::log($exception)) {
						echo "(stored in $file)\n";
						if (self::$browser) {
							exec(self::$browser . ' ' . escapeshellarg($file));
						}
					}

				} elseif (self::isHtmlMode()) { // dump to browser
					self::$blueScreen->render($exception);
					if (self::$bar) {
						self::$bar->render();
					}

				} elseif (!self::fireLog($exception)) { // AJAX or non-HTML mode
					$file = self::log($exception, self::ERROR);
					if (!headers_sent()) {
						header("X-Nette-Error-Log: $file");
					}
				}
			}

			foreach (self::$onFatalError as $handler) {
				call_user_func($handler, $exception);
			}

		} catch (Exception $e) {
			if (self::$productionMode) {
				echo self::isHtmlMode() ? '<meta name=robots content=noindex>FATAL ERROR' : 'FATAL ERROR';
			} else {
				echo "FATAL ERROR: thrown ", get_class($e), ': ', $e->getMessage(),
					"\nwhile processing ", get_class($exception), ': ', $exception->getMessage(), "\n";
			}
		}

		self::$enabled = FALSE; // un-register shutdown function
		exit(254);
	}


	/**
	 * Handler to catch warnings and notices.
	 * @param  int    level of the error raised
	 * @param  string error message
	 * @param  string file that the error was raised in
	 * @param  int    line number the error was raised at
	 * @param  array  an array of variables that existed in the scope the error was triggered in
	 * @return bool   FALSE to call normal error handler, NULL otherwise
	 * @throws FatalErrorException
	 * @internal
	 */
	public static function _errorHandler($severity, $message, $file, $line, $context)
	{
		if (self::$scream) {
			error_reporting(E_ALL | E_STRICT);
		}

		if (self::$lastError !== FALSE && ($severity & error_reporting()) === $severity) { // tryError mode
			self::$lastError = new ErrorException($message, 0, $severity, $file, $line);
			return NULL;
		}

		if ($severity === E_RECOVERABLE_ERROR || $severity === E_USER_ERROR) {
			if (NDebugHelpers::findTrace(PHP_VERSION_ID < 50205 ? debug_backtrace() : debug_backtrace(FALSE), '*::__toString')) {
				$previous = isset($context['e']) && $context['e'] instanceof Exception ? $context['e'] : NULL;
				self::_exceptionHandler(new FatalErrorException($message, 0, $severity, $file, $line, $context, $previous));
			}
			throw new FatalErrorException($message, 0, $severity, $file, $line, $context);

		} elseif (($severity & error_reporting()) !== $severity) {
			return FALSE; // calls normal error handler to fill-in error_get_last()

		} elseif (!self::$productionMode && (is_bool(self::$strictMode) ? self::$strictMode : ((self::$strictMode & $severity) === $severity))) {
			self::_exceptionHandler(new FatalErrorException($message, 0, $severity, $file, $line, $context));
		}

		static $types = array(
			E_WARNING => 'Warning',
			E_COMPILE_WARNING => 'Warning', // currently unable to handle
			E_USER_WARNING => 'Warning',
			E_NOTICE => 'Notice',
			E_USER_NOTICE => 'Notice',
			E_STRICT => 'Strict standards',
			E_DEPRECATED => 'Deprecated',
			E_USER_WARNING => 'Deprecated',
		);

		$message = 'PHP ' . (isset($types[$severity]) ? $types[$severity] : 'Unknown error') . ": $message";
		$count = & self::$errorPanel->data["$file|$line|$message"];

		if ($count++) { // repeated error
			return NULL;

		} elseif (self::$productionMode) {
			self::log("$message in $file:$line", self::ERROR);
			return NULL;

		} else {
			$ok = self::fireLog(new ErrorException($message, 0, $severity, $file, $line));
			return !self::isHtmlMode() || (!self::$bar && !$ok) ? FALSE : NULL;
		}
	}


	/**
	 * Handles exception thrown in __toString().
	 * @param  Exception
	 * @return void
	 */
	public static function toStringException(Exception $exception)
	{
		if (self::$enabled) {
			self::_exceptionHandler($exception);
		} else {
			trigger_error($exception->getMessage(), E_USER_ERROR);
		}
	}


	/**
	 * Starts catching potential errors/warnings.
	 * @return void
	 */
	public static function tryError()
	{
		if (!self::$enabled && self::$lastError === FALSE) {
			set_error_handler(array(__CLASS__, '_errorHandler'));
		}
		self::$lastError = NULL;
	}


	/**
	 * Returns catched error/warning message.
	 * @param  ErrorException  catched error
	 * @return bool
	 */
	public static function catchError(& $error)
	{
		if (!self::$enabled && self::$lastError !== FALSE) {
			restore_error_handler();
		}
		$error = self::$lastError;
		self::$lastError = FALSE;
		return (bool) $error;
	}


	/********************* useful tools ****************d*g**/


	/**
	 * Dumps information about a variable in readable format.
	 * @param  mixed  variable to dump
	 * @param  bool   return output instead of printing it? (bypasses $productionMode)
	 * @return mixed  variable itself or dump
	 */
	public static function dump($var, $return = FALSE)
	{
		if (!$return && self::$productionMode) {
			return $var;
		}

		$output = "<pre class=\"nette-dump\">" . NDebugHelpers::htmlDump($var) . "</pre>\n";

		if (!$return) {
			$trace = PHP_VERSION_ID < 50205 ? debug_backtrace() : debug_backtrace(FALSE);
			$item = ($tmp=NDebugHelpers::findTrace($trace, 'dump')) ? $tmp : NDebugHelpers::findTrace($trace, __CLASS__ . '::dump');
			if (isset($item['file'], $item['line']) && is_file($item['file'])) {
				$lines = file($item['file']);
				preg_match('#dump\((.*)\)#', $lines[$item['line'] - 1], $m);
				$output = substr_replace(
					$output,
					' title="' . htmlspecialchars((isset($m[0]) ? "$m[0] \n" : '') . "in file {$item['file']} on line {$item['line']}") . '"',
					4, 0);

				if (self::$showLocation) {
					$output = substr_replace(
						$output,
						' <small>in ' . NDebugHelpers::editorLink($item['file'], $item['line']) . ":{$item['line']}</small>",
						-8, 0);
				}
			}
		}

		if (self::$consoleMode) {
			if (self::$consoleColors && substr(getenv('TERM'), 0, 5) === 'xterm') {
				$output = preg_replace_callback('#<span class="php-(\w+)">|</span>#', create_function('$m', '
					return "\\033[" . (isset($m[1], NDebugger::$consoleColors[$m[1]]) ? NDebugger::$consoleColors[$m[1]] : \'0\') . "m";
				'), $output);
			}
			$output = htmlspecialchars_decode(strip_tags($output), ENT_QUOTES);
		}

		if ($return) {
			return $output;

		} else {
			echo $output;
			return $var;
		}
	}


	/**
	 * Starts/stops stopwatch.
	 * @param  string  name
	 * @return float   elapsed seconds
	 */
	public static function timer($name = NULL)
	{
		static $time = array();
		$now = microtime(TRUE);
		$delta = isset($time[$name]) ? $now - $time[$name] : 0;
		$time[$name] = $now;
		return $delta;
	}


	/**
	 * Dumps information about a variable in Nette Debug Bar.
	 * @param  mixed  variable to dump
	 * @param  string optional title
	 * @return mixed  variable itself
	 */
	public static function barDump($var, $title = NULL)
	{
		if (!self::$productionMode) {
			$dump = array();
			foreach ((is_array($var) ? $var : array('' => $var)) as $key => $val) {
				$dump[$key] = NDebugHelpers::clickableDump($val);
			}
			self::$dumpPanel->data[] = array('title' => $title, 'dump' => $dump);
		}
		return $var;
	}


	/**
	 * Sends message to FireLogger console.
	 * @param  mixed   message to log
	 * @return bool    was successful?
	 */
	public static function fireLog($message)
	{
		if (!self::$productionMode) {
			return self::$fireLogger->log($message);
		}
	}


	private static function isHtmlMode()
	{
		return !self::$ajaxDetected && !self::$consoleMode
			&& !preg_match('#^Content-Type: (?!text/html)#im', implode("\n", headers_list()));
	}


	/** @deprecated */
	public static function addPanel(IBarPanel $panel, $id = NULL)
	{
		return self::$bar->addPanel($panel, $id);
	}

}

ACC SHELL 2018