forked from Public/pics
		
	
		
			
				
	
	
		
			189 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			189 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/*****************************************************************************
 | 
						|
 * ErrorHandler.php
 | 
						|
 * Contains key class ErrorHandler.
 | 
						|
 *
 | 
						|
 * Kabuki CMS (C) 2013-2016, Aaron van Geffen
 | 
						|
 *****************************************************************************/
 | 
						|
 | 
						|
class ErrorHandler
 | 
						|
{
 | 
						|
	private static $error_count = 0;
 | 
						|
	private static $handling_error;
 | 
						|
 | 
						|
	public static function enable()
 | 
						|
	{
 | 
						|
		set_error_handler('ErrorHandler::handleError');
 | 
						|
		ini_set("display_errors", DEBUG ? "On" : "Off");
 | 
						|
	}
 | 
						|
 | 
						|
	public static function disable()
 | 
						|
	{
 | 
						|
		set_error_handler(NULL);
 | 
						|
		ini_set("display_errors", "Off");
 | 
						|
	}
 | 
						|
 | 
						|
	// Handler for standard PHP error messages.
 | 
						|
	public static function handleError($error_level, $error_message, $file, $line, $context = null)
 | 
						|
	{
 | 
						|
		// Don't handle suppressed errors (e.g. through @ operator)
 | 
						|
		if (!(error_reporting() & $error_level))
 | 
						|
			return;
 | 
						|
 | 
						|
		// Prevent recursing if we've messed up in this code path.
 | 
						|
		if (self::$handling_error)
 | 
						|
			return;
 | 
						|
 | 
						|
		self::$error_count++;
 | 
						|
		self::$handling_error = true;
 | 
						|
 | 
						|
		// Basically, htmlspecialchars it, minus '&' for HTML entities.
 | 
						|
		$error_message = strtr($error_message, ['<' => '<', '>' => '>', '"' => '"', "\t" => '  ']);
 | 
						|
		$error_message = strtr($error_message, ['<br>' => "<br>",  '<br />' => "<br>", '<b>' => '<strong>', '</b>' => '</strong>', '<pre>' => '<pre>', '</pre>' => '</pre>']);
 | 
						|
 | 
						|
		// Generate a bunch of useful information to ease debugging later.
 | 
						|
		$debug_info = self::getDebugInfo(debug_backtrace());
 | 
						|
 | 
						|
		// Log the error in the database.
 | 
						|
		self::logError($error_message, $debug_info, $file, $line);
 | 
						|
 | 
						|
		// Are we considering this fatal? Then display and exit.
 | 
						|
		// !!! TODO: should we consider warnings fatal?
 | 
						|
		if (true) // DEBUG || (!DEBUG && $error_level === E_WARNING || $error_level === E_USER_WARNING))
 | 
						|
			self::display($file . ' (' . $line . ')<br>' . $error_message, $debug_info);
 | 
						|
 | 
						|
		// If it wasn't a fatal error, well...
 | 
						|
		self::$handling_error = false;
 | 
						|
	}
 | 
						|
 | 
						|
	public static function getDebugInfo(array $trace)
 | 
						|
	{
 | 
						|
		$debug_info = "Backtrace:\n";
 | 
						|
		$debug_info .= self::formatBacktrace($trace);
 | 
						|
 | 
						|
		// Include info on the contents of superglobals.
 | 
						|
		if (!empty($_SESSION))
 | 
						|
			$debug_info .= "\nSESSION: " . print_r($_SESSION, true);
 | 
						|
		if (!empty($_POST))
 | 
						|
			$debug_info .= "\nPOST: " . print_r($_POST, true);
 | 
						|
		if (!empty($_GET))
 | 
						|
			$debug_info .= "\nGET: " . print_r($_GET, true);
 | 
						|
 | 
						|
		return $debug_info;
 | 
						|
	}
 | 
						|
 | 
						|
	private static function formatBacktrace(array $trace)
 | 
						|
	{
 | 
						|
		$buffer = '';
 | 
						|
		$skipping = true;
 | 
						|
 | 
						|
		foreach ($trace as $i => $call)
 | 
						|
		{
 | 
						|
			if (isset($call['class']) && ($call['class'] === 'ErrorHandler' || $call['class'] === 'Database') ||
 | 
						|
				isset($call['function']) && $call['function'] === 'preg_replace_callback')
 | 
						|
			{
 | 
						|
				if (!$skipping)
 | 
						|
				{
 | 
						|
					$buffer .= "[...]\n";
 | 
						|
					$skipping = true;
 | 
						|
				}
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			else
 | 
						|
				$skipping = false;
 | 
						|
 | 
						|
			$file = isset($call['file']) ? str_replace(BASEDIR, '', $call['file']) : 'Unknown';
 | 
						|
			$object = isset($call['class']) ? $call['class'] . $call['type'] : '';
 | 
						|
 | 
						|
			$args = [];
 | 
						|
			if (isset($call['args']))
 | 
						|
			{
 | 
						|
				foreach ($call['args'] as $j => $arg)
 | 
						|
				{
 | 
						|
					if (is_array($arg))
 | 
						|
						$args[$j] = print_r($arg, true);
 | 
						|
					elseif (is_object($arg))
 | 
						|
						$args[$j] = var_dump($arg);
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			$buffer .= '#' . str_pad($i, 3, ' ')
 | 
						|
				. $object . $call['function'] . '(' . implode(', ', $args) . ')'
 | 
						|
				. ' called at [' . $file . ':' . $call['line'] . "]\n";
 | 
						|
		}
 | 
						|
 | 
						|
		return $buffer;
 | 
						|
	}
 | 
						|
 | 
						|
	// Logs an error into the database.
 | 
						|
	private static function logError($error_message = '', $debug_info = '', $file = '', $line = 0)
 | 
						|
	{
 | 
						|
		if (!ErrorLog::log([
 | 
						|
			'message' => $error_message,
 | 
						|
			'debug_info' => $debug_info,
 | 
						|
			'file' => str_replace(BASEDIR, '', $file),
 | 
						|
			'line' => $line,
 | 
						|
			'id_user' => Registry::has('user') ? Registry::get('user')->getUserId() : 0,
 | 
						|
			'ip_address' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
 | 
						|
			'request_uri' => isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '',
 | 
						|
			]))
 | 
						|
		{
 | 
						|
			header('HTTP/1.1 503 Service Temporarily Unavailable');
 | 
						|
			echo '<h2>An Error Occured</h2><p>Our software could not connect to the database. We apologise for any inconvenience and ask you to check back later.</p>';
 | 
						|
			exit;
 | 
						|
		}
 | 
						|
 | 
						|
		return $error_message;
 | 
						|
	}
 | 
						|
 | 
						|
	public static function display($message, $debug_info, $is_sensitive = true)
 | 
						|
	{
 | 
						|
		$is_admin = Registry::has('user') && Registry::get('user')->isAdmin();
 | 
						|
 | 
						|
		// Just show the message if we're running in a console.
 | 
						|
		if (empty($_SERVER['HTTP_HOST']))
 | 
						|
		{
 | 
						|
			echo $message;
 | 
						|
			exit;
 | 
						|
		}
 | 
						|
		// JSON request?
 | 
						|
		elseif (isset($_GET['json']) || isset($_GET['format']) && $_GET['format'] == 'json')
 | 
						|
		{
 | 
						|
			if (DEBUG || $is_admin)
 | 
						|
				echo json_encode(['error' => $message . "\n\n" . $debug_info]);
 | 
						|
			elseif (!$is_sensitive)
 | 
						|
				echo json_encode(['error' => $message]);
 | 
						|
			else
 | 
						|
				echo json_encode(['error' => 'Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.']);
 | 
						|
			exit;
 | 
						|
		}
 | 
						|
 | 
						|
		// Initialise the main template to present a nice message to the user.
 | 
						|
		$page = new MainTemplate('An error occured!');
 | 
						|
 | 
						|
		// Show the error.
 | 
						|
		$is_admin = Registry::has('user') && Registry::get('user')->isAdmin();
 | 
						|
		if (DEBUG || $is_admin)
 | 
						|
		{
 | 
						|
			$page->adopt(new DummyBox('An error occured!', '<p>' . $message . '</p><pre>' . $debug_info . '</pre>'));
 | 
						|
 | 
						|
			// Let's provide the admin navigation despite it all!
 | 
						|
			if ($is_admin)
 | 
						|
			{
 | 
						|
				$page->appendStylesheet(BASEURL . '/css/admin.css');
 | 
						|
			}
 | 
						|
		}
 | 
						|
		elseif (!$is_sensitive)
 | 
						|
			$page->adopt(new DummyBox('An error occured!', '<p>' . $message . '</p>'));
 | 
						|
		else
 | 
						|
			$page->adopt(new DummyBox('An error occured!', '<p>Our apologies, an error occured while we were processing your request. Please try again later, or contact us if the problem persists.</p>'));
 | 
						|
 | 
						|
		// If we got this far, make sure we're not showing stuff twice.
 | 
						|
		ob_end_clean();
 | 
						|
 | 
						|
		// Render the page.
 | 
						|
		$page->html_main();
 | 
						|
		exit;
 | 
						|
	}
 | 
						|
}
 |