PHP - Capture all error_get_last() in One String - php

Is it possible to capture/gather all the errors on page and concatenate them to a string by using:
$allErrors = "";
$allErrors .= error_get_last(); // Each time an error shows up
I like to log errors in my database and would prefer to log all these PHP errors since I already have SQL-related PHP Fatal Errors logged.

error_get_last(), like the name is suggesting, only gives you the last error. And the fact that most errors will stop your script from running will get you only the last one and none of the previous ones. But you can set an own handler to catch every thrown error and exception. Here is an example
//function for exception handling
function handle_exception (Exception $exception) {
//here you can save the exception to your database
}
//function for error handling
function handle_error ($number, $message, $file, $line, $context = null) {
//pass/throw error to handle_exception
throw new ErrorException ($message, 0, $number, $file, $line);
}
//set error-handler but only for E_USER_ERROR and E_RECOVERABLE_ERROR
set_error_handler ('handle_error', E_USER_ERROR|E_RECOVERABLE_ERROR);
//exception-handler
set_exception_handler ('handle_exception');

Related

How do you catch recoverable errors in PHP 7+?

What happens with set_error_handler() on PHP7 now that all errors are exceptions? makes me think that recoverable errors can be caught but that does not appear to be the case, as demonstrated here:
<?php
$_SERVER['TEST'] = new stdClass;
try {
phpinfo();
} catch (\Throwable $e) {}
If you run that code you'll get this as the output:
$_SERVER['argc'] => 1
$_SERVER['TEST'] =>
Recoverable fatal error: Object of class stdClass could not be converted to string in /Users/username/zzz.php on line 6
So clearly an error is being "issued" but one is not being thrown, unless I'm misunderstanding something?
Not all errors were converted to Throwable errors in PHP 7.
Actually, the docs say:
most errors are now reported by throwing Error exceptions.
(Emphasis mine). Most !== All.
Some errors are still not catchable.
Funnily enough, the error message used you get used to say "Catchable Fatal Error" instead of "Recoverable Fatal Error" before PHP 7.1 was released.
This was reported as a bug, but the solution implemented by the devs was change the error string from Catchable to Recoverable to dispel misconceptions.
In the specific case you are testing, it seems that phpinfo() raises a recoverable error instead of throwing an Error, so it makes sense you can't catch it this way.
Still, not all hope is lost.
What you could do is convert all errors to exceptions by implementing your own error handler. An example of this is described on the ErrorException documentation:
function exception_error_handler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
// This error code is not included in error_reporting
return;
}
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");
The neat thing of this example is that it takes into account your error reporting settings, so only errors that would have been reported under your settings are actually thrown as exceptions. Otherwise, nothing happens.
Testing your code:
<?php
function exception_error_handler($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
// This error code is not included in error_reporting
return;
}
throw new ErrorException($message, 0, $severity, $file, $line);
}
set_error_handler("exception_error_handler");
$_SERVER['TEST'] = new stdClass;
try {
phpinfo(INFO_VARIABLES);
} catch (\Throwable $e) {echo 'CAUGHT!!!!!!';}
prints the following output:
$_SERVER['TERM_SESSION_ID'] => w0t1p0:xxx-cxx-xxxxxxx-xxxxx
$_SERVER['SSH_AUTH_SOCK'] => /private/tmp/com.apple.launchd.xxxxxxxx/Listeners
$_SERVER['LC_TERMINAL_VERSION'] => 3.3.2
....
$_SERVER['argc'] => 1
$_SERVER['TEST'] =>
CAUGHT!!!!!!%

WHEN to call restore_error_handler()?

This has bugged me for some time.
When I call set_error_handler(), errors and warnings and notices are routed to my handler, and when i call restore_error_handler(), my previously defined handler is restored. But what happens when an exception is thrown before restore_error_handler() is called?
I have the following defined within a class:
function do_something() {
set_error_handler([$this, 'convert_errors_to_exceptions']);
//stuff that may trigger error, e.g.
trigger_error('testing an error');
//will this actually ever be called?
restore_error_handler();
}
public function convert_errors_to_exceptions($num, $message, $file, $line) {
//if error reporting turned off
if (!(error_reporting() & $num)) return false;
//convert to exception - then what happens?
throw new Exception("Error: $message in $file on line $line");
return true;
}
Will restore_error_handler() ever be called if an error is triggered? And do I have to call restore_error_handler() immediately prior to throwing the exception if I want my previous handler to actually handle the exception itself?
Cart/horse problems!

Get error before they execute the error message function

I want to receive the error message in php before it gets executed. Basicly what i mean is that if I would have a bad code:
// This code is incorrect, I want to receive the error before it gets handled!
$some_var = new this_class_is_not_made;
Now that class does not exist, so it would be handles by the default error handler in php. But I want to disable the normal error handler and create my own.
Another example:
somefunction( string some_var ); // some_var misses the variable prefix. ( $ )
Example error message:
Fatal error: function 'some_var' is not defined in line: $x!
And this error would be: somefunction( string some_var );
But how would I receive the messages but also disable the normal error system?
EDIT: Making the error system execute a user-defined function
// I would want the error system to execute a function like this:
function(string $errorMessage, int $error_code){
if($error_code < 253){ return "Fatal error"; }
if($error_code < 528 && $error_code > 253){ return "Warning"; }
}
Answer found: By: ShiraNai7
try
{
// Code that may throw an Exception or Error.
}
catch (Throwable $t)
{
// Executed only in PHP 7, will not match in PHP 5
}
catch (Exception $e)
{
// Executed only in PHP 5, will not be reached in PHP 7
}
In PHP 7.0.0 or newer the code will throw Error exception if this_class_is_not_made doesn't exist.
try {
$some_var = new this_class_is_not_made;
} catch (Error $e) {
echo $e->getMessage();
}
Note that that this will also catch any other Error exceptions in case this_class_is_not_made does exist and causes some other error along the way.
In PHP versions prior to 7.0.0 you're out of luck - fatal errors always terminate the main script.
It might be a better idea to use class_exists() instead:
if (class_exists('this_class_is_not_made')) {
$some_var = new this_class_is_not_made;
}
This works in all PHP versions that support classes.

PHP: set_exception_handler not working for error thrown in set_error_handler callback

I have the following code to setup my error/exception handlers:
// Set the exception handler
set_exception_handler(function($e) {
echo 'Exception';
});
// Set the error handler
set_error_handler(function($code, $message, $file, $line) {
throw new ErrorException($message, 0, $code, $file, $line);
});
I have read numerous articles which have said to throw an exception within the set_error_handler callback function. This should mean I only have to handle exceptions. However the set_exception_handler callback function is never called and instead I get the warning:
Warning: Uncaught exception 'ErrorException' with message...
Please note that I am using PHP 5.4.
I tried to run the PHP script below, and it runs fine.
// set the exception handler
set_exception_handler(function($e) {
echo ' Exception';
});
// set the error handler
set_error_handler(function($code, $message, $file, $line) {
echo " Error [$message]";
throw new ErrorException($message, 0, $code, $file, $line);
},-1);
// trigger error
trigger_error('My own error.',E_USER_ERROR);
It returns:
Error [My own error.] Exception
Its exactly the same as yours. I cannot find anything wrong with the piece of code you provided, could the problem be outside it?

Catching errors thrown by token_get_all (Tokenizer)

PHPs token_get_all function (which allows converting a PHP source code into tokens) can throw two errors: One if an unterminated multiline comment is encountered, the other if an unexpected char is found.
I would like to catch those errors and throw them as Exceptions.
Problem is: As those errors are parse errors they cannot be handled with an error handling function you would normally specify using set_error_handler.
What I have currently implemented is the following:
// Reset the error message in error_get_last()
#$errorGetLastResetUndefinedVariable;
$this->tokens = #token_get_all($code);
$error = error_get_last();
if (preg_match(
'~^(Unterminated comment) starting line ([0-9]+)$~',
$error['message'],
$matches
)
) {
throw new ParseErrorException($matches[1], $matches[2]);
}
if (preg_match(
'~^(Unexpected character in input:\s+\'(.)\' \(ASCII=[0-9]+\))~s',
$error['message'],
$matches
)
) {
throw new ParseErrorException($matches[1]);
}
It should be obvious that I'm not really excited to use that solution. Especially the fact that I reset the error message in error_get_last by accessing an undefined variable seems pretty unsatisfactory.
So: Is there a better solution to this problem?
Set a custom errorhandler using set_error_handler.
Call token_get_all.
Then unset the error handler by calling restore_error_handler.
This will allow you to catch warnings. Make sure you remove the # suppressor.
You can for instance register an error handler that is in a class that will just record any warnings for inspection later on.
Untested example code:
class CatchWarnings {
private $warnings = array();
public function handler($errno, $errstr, $errfile, $errline) {
switch ($errno) {
case E_USER_WARNING:
$this->warnings[] = $errstr;
return true; // cancel error handling bubble
}
return false; // error handling as usual
}
public function has_warnings() {
return count($this->warnings) > 0;
}
}
$cw = new CatchWarnings();
set_error_handler(array($cw, "handler"));
token_get_all();
restore_error_handler();
Usually validation and execution are two separate things, but it seems like there is no way to validate/lint a piece of PHP code (not since 5.x anyway).

Categories