View Issue Details

IDProjectCategoryView StatusLast Update
403Exception_DefaultHandlerAllpublic2 Jan 2014 10:49
Reportermrosenquist Assigned Totimj  
PrioritynormalSeverityfeatureReproducibilityhave not tried
Status closedResolutionfixed 
Product Version0.8.1 
Target Version1.0.0Fixed in Version1.0.0 
Summary403: Support handling of serious errors (e.g. call to non-existent functions) which skip the normal error handling
DescriptionAdd a way to support handing for errors that php dosen't send to the set_error_handler callback
TagsNo tags attached.
Attached Files
bug-403.patch (2,860 bytes)   
Index: DefaultHandler.php
===================================================================
--- DefaultHandler.php	(revision 54105)
+++ DefaultHandler.php	(working copy)
@@ -202,6 +202,63 @@
 	}
 	
 	/**
+	 * Try and handle PHP errors that are not passed to the error_handler. To use this,
+	 * set error handling as follows:
+	 *
+	 * 	register_shutdown_function(array('Exception_DefaultHandler','handleShutdown'));
+	 *
+	 * This function will pass the last error that could not be handled as php does not sent
+	 * it to the defined "set_error_handler()"
+	 * 
+	 * Error output will not be show if display errors are on as there is no clean (ob buffering can be used)
+	 * way of suppressing the normal php output and there is no point in duplicating the same message
+	 */
+	public static function handleShutdown()
+	{
+		if ($error = error_get_last()){
+			switch($error['type']){
+				case E_ERROR:
+				case E_CORE_ERROR:
+				case E_COMPILE_ERROR:
+				case E_USER_ERROR:
+					$debug_backtrace = debug_backtrace();
+
+					// Clear the first element, which refers to the call to this class
+					unset($debug_backtrace[0]);
+
+					$backtrace = self::getBacktraceOutput($debug_backtrace);
+					$errtype = self::getPHPErrorDescription($error['type']);
+
+					$error_hash[] = strtoupper(substr(md5($error['type'] . $error['message']), 0, 4));
+					$error_hash[] = self::locationHash($error['file'], $error['line'], $debug_backtrace);
+					$error_hash = implode('/', $error_hash);
+
+					$additional_environment  = "PHP Error Type:     $errtype\n";
+					$additional_environment .= "PHP Error Details:  " . $error['message'] . "\n";
+					$additional_environment .= "Script location:    " . $error['file'] . "\n";
+					$additional_environment .= "Line Number:        " . $error['line'] . "\n";
+					self::writeLog($errtype, $error['message'], $error['file'], $error['line'], $backtrace);
+					self::sendEmail($backtrace, $additional_environment, $errtype, $error_hash);
+
+					/*
+					 * Only show errors if the errors have not already been show (diplay_errors is off)
+					 * and errors should not be suppressed
+					 */
+					$disp_err = ini_get('display_errors');
+					settype($disp_err, 'string'); // seems to always return string, but be sure
+					$disp_err = strtolower(trim($disp_err));
+					if ($disp_err !== '1' && $disp_err !== 'on' && !self::suppressErrorDisplay()) {
+						// This are always terminiation errors
+						self::showOutput($errtype.' #'.$error_hash, $error['message'], $error['file'], $error['line'], $backtrace, true);
+					}
+					break;
+				default:
+					break;
+			}
+    	}
+	}
+	
+	/**
 	 * Get a unique(ish) hash for a given exception, which merely serves to
 	 * tie together related errors when looking at a whole load of errors
 	 * The returned hash is in two parts: YYYY/ZZZZ where YYYY is only
bug-403.patch (2,860 bytes)   
test6.php (323 bytes)   
<?php
// This test is to show a fatel error, display errors are turn off so some output will be shown

ini_set('display_errors', false);

require_once 'Exception/DefaultHandler.php';
register_shutdown_function(array('Exception_DefaultHandler','handleShutdown'));

// Force an error to occur
callFunctionThatDoesNotExist();
test6.php (323 bytes)   

Activities

timj

8 Feb 2012 18:09

administrator   ~435

Thanks! Looks like a nice feature. Just a few points:

1) Can you give examples of errors that it will catch that are not caught by the normal error handler?

2) Linked to (1), it would be good to have a test case. Although there are not currently proper unit tests, there are at least some manual tests for key functionality - see http://svn.timj.co.uk/Exception_DefaultHandler/library/Exception_DefaultHandler/trunk/tests/ - and I would like to improve the tests by adding at least some example for all features.

3) Instead of calling ini_get('display_errors') and checking the value, there is a cleaner way, call self::getDisplayErrors() (or is there a reason why this doesn't work in the shutdown function?)

mrosenquist

8 Feb 2012 18:20

reporter   ~436

1)
An example case: (sorry I did write a test case but is not in the patch)

Is where there is a run time error that causes an E_ERROR
best example is trying to use a function that does not exist
<?php
someFakeFunction();
//or
class a {

}
$a = new a();
$a->someFakeMethod();
?>

While it should never happen, I would like to get emails about it if it ever did

2)
Added the test case

3)
I should have added a comment
the self::getDisplayErrors() internally calls self::suppressErrorDisplay() and return false if true

the logic needed is not display error and not suppress error display,

hum ... thinking about it, it would be fine to use self::getDisplayErrors() as we check for self::suppressErrorDisplay() independetly, couldn't think through the login at the time ;-)

mrosenquist

8 Feb 2012 18:21

reporter   ~437

There may need to be a test case for what happens with the following

<?php
@someFakeFunction();

timj

8 Feb 2012 18:33

administrator   ~438

Great thanks, another thing is that it uses error_get_last(), which makes it only work on >=PHP 5.2. Not sure yet if this is a big deal or not.

mrosenquist

8 Feb 2012 18:43

reporter   ~439

If there is a comment in the code then is would only matter if someone set up the shutdown function in a pre PHP 5.2, php will not error unless that code is run, it will not check code that is not visited

could have a warning in the code to check, but that could get circular errors,

mkierat

3 May 2012 14:03

reporter   ~449

Hi Tim,

can you try and review this patch and release a new version soon.

This is the one type of errors we know absolutely nothing about at the moment and it is a real pain to try and work out application problems that were caused by fatal errors happening somewhere else in the system.

With using error_get_last() and 5.2+ => maybe you make this one feature only available in 5.2+ (so the whole handler would not need 5.2 but using the shutdown function would)... Just a thought.

Cheers,
Michal

timj

8 May 2012 00:44

administrator   ~450

If someone wants to update the patch/test case according to the following then this can get in the release :-)

1. clean up as per comment 403:436 point 3
2. make it safe for PHP <5.2 (e.g. use function_exists() to check before calling error_get_last()) with a unit test for this
3. rewrite the existing test to be a proper PHPT unit test, like the existing ones in SVN trunk (e.g. http://svn.timj.co.uk/Exception_DefaultHandler/library/Exception_DefaultHandler/trunk/tests/test1a-errors.phpt)

timj

18 Jul 2012 20:33

manager   ~462

Fixed in SVN r2260

Issue History

Date Modified Username Field Change
8 Feb 2012 15:48 mrosenquist New Issue
8 Feb 2012 15:49 mrosenquist File Added: bug-403.patch
8 Feb 2012 18:09 timj Note Added: 435
8 Feb 2012 18:14 mrosenquist File Added: test6.php
8 Feb 2012 18:20 mrosenquist Note Added: 436
8 Feb 2012 18:21 mrosenquist Note Added: 437
8 Feb 2012 18:33 timj Note Added: 438
8 Feb 2012 18:43 mrosenquist Note Added: 439
3 May 2012 14:03 mkierat Note Added: 449
8 May 2012 00:44 timj Note Added: 450
17 Jul 2012 12:09 timj Status new => confirmed
17 Jul 2012 12:09 timj Summary Add a way to support handing for errors that php dosen't send to the set_error_handler callback => Support handling of serious errors (e.g. call to non-existent functions) which skip the normal error handling
18 Jul 2012 20:33 timj Note Added: 462
18 Jul 2012 20:33 timj Assigned To => timj
18 Jul 2012 20:33 timj Status confirmed => resolved
18 Jul 2012 20:33 timj Resolution open => fixed
18 Jul 2012 20:33 timj Fixed in Version => 0.8.3a
18 Jul 2012 20:33 timj Target Version => 0.8.3a
2 Jan 2014 10:49 timj Fixed in Version 0.8.3a => 1.0.0
2 Jan 2014 10:49 timj Target Version 0.8.3a => 1.0.0
2 Jan 2014 10:49 timj Status resolved => closed