Graceful Error Handling in PHP

When running a professional level PHP application we want to hide errors from the end-user as much as possible. Part of this is configured by setting different error_level handling and display options in PHP. The other part is implementing robust error handling.

The Function to Show Errors

We've got two routines here, the first dumps the state to a log file. The second is the actual error handling routine, which displays output and dumps the data.


function err_dump($code, $text=null, $file=null, $line=null, $opts=null, $orig=null)
{
	// Detect Error Type
	if (is_numeric($code)) {
		$dump = "Error: $code: $text in $file@$line\n\n";
	} elseif (is_object($code)) {
		$dump = 'Exception: ' . $code->getMessage() . "\n";
		$dump.= $code->getFile() . ' @' . $code->getLine() . "\n";
		$dump.= "\n-----\n" . $code->getTraceAsString() . "\n-----\n";
	}

	ksort($_GET);
	$dump.= "### GET:\n" . print_r($_GET, true) . "\n\n";

	ksort($_POST);
	$dump.= "### POST:\n" . print_r($_POST, true) . "\n\n";

	ksort($_SESSION);
	$dump.= "### SESSION:\n" . print_r($_SESSION, true) . "\n\n";

	ksort($_SERVER);
	$dump.= "### SERVER:\n" . print_r($_SERVER, true) . "\n\n";

	$dump.= "### OUTPUT:\n";
	$dump.= "$orig\n### /OUTPUT\n";

	$file = sprintf('/tmp/%s.dump', $_SERVER['UNIQUE_ID']);

	file_put_contents($file, $dump);

}

function err_trap($code, $text=null, $file=null, $line=null, $opts=null)
{
	// Trap Existing Buffer
	$orig = '';
	while (ob_get_level() > 0) {
		$orig.= ob_get_clean();
	}

	err_dump($code, $text, $file, $line, $opts, $orig)

	// Detect Error Type
	if (is_numeric($code)) {

		switch ($code) {
		case 8:
			return false;
		}
		// Ignore Errors Not Being Shown
		$er = error_reporting();
		// if (($ecode & $er) != $er) return false;

		$body = "

Error: $code: $text in $file@$line

"; } elseif (is_object($code)) { // Exception $body = '

Exception: ' . $code->getMessage() . '

'; $body.= '

' . $code->getFile() . ' @' . $code->getLine() . '

'; if ($x = $code->getPrevious()) { $body.= '

From: ' . $x->getMessage() . '

'; $body.= '

' . $x->getFile() . ' @' . $x->getLine() . '

'; } $body.= '
';
		$body.= $code->getTraceAsString();
		$body.= '
'; } $body.= '

Log: ' . $_SERVER['UNIQUE_ID'] . '

'; if (strlen($orig)) { $body.= '

Output Buffer

' . htmlspecialchars($orig, ENT_QUOTES, 'utf-8', false) . '
'; } header($_SERVER['SERVER_PROTOCOL'] . ' 500 Server Error', true, 500); $_ENV['title'] = 'Server Error'; echo $body; die(500); }

Enable the Error Handlers

Once the Error Handler and Logger are in place, we tell PHP to use those for errors or exceptions.


set_error_handler('err_trap');
set_exception_handler('err_trap');

Handle Really Fatal Errors

Finally, we register a shutdown function near the start of our PHP scripts. Usually in a common bootstrap or front controller pattern. Now, really fatal errors are trapped and some friendly output is shown.


register_shutdown_function(function() {
	$e = error_get_last();
	if (!empty($e)) {
		err_trap($e['type'], $e['message'], $e['file'], $e['line']);
	}
});

See Also