Why can't I catch this 'Maximum Nested Function' Exception in PHP? - php

I am experiencing an Exception due to a recursive function in my code, due to the nature of what I am coding, the exception can just be ignored and FALSE returned instead. So here is som simplified code to illustrate my issue.
function recursive() {
try{ recursive(); }
catch(Exception $e)
{ echo "Error Caught!"; }
}
recursive();
I can't seem to catch the 'Maximum function nesting level of '100' reached, aborting!' exception.
Have I misunderstood how try-catch's work?

Because it is a Fatal Error and not an Exception so you can not use try & catch.
An Error in general means that the execution of the program by all means can not be continued and has to be aborded.
An Exception on the other hand is like a warning, meaning something has gone wrong, but with the right handling of this exception the program execution can continue.
An Example for try & catch could be:
try to connect to the database ... function connect throws an exception because database server is not reachable ... you catch the exception and decide, well then lets read the stuff from a cached file. The intention behind exception is, to let the developer decide wether he wants to catch the exception and continue the program execution or suspend it.

"PHP Fatal error: Maximum function nesting level of '100' reached, aborting!"
It is a "Fatal error", not an exception. There is no way in PHP to convert it to Exception using set_error_handler (which is good for converting lower level errors to exceptions).
In case of "Fatal error" the only thing You can do is to make some cleanup using register_shutdown_function where you can call error_get_last and recognize that this particular fatal error occured. But thats all You can do, there is no way to continue designed program flow.
BTW this partical fatal error can happen only when You have XDebug module enabled in Your php.ini.

Related

Severity of exception codes

I am trying to sort out error and exception handlers. With a fatal error, I send myself a text message, and with other errors I get an email. (I added the arrays at the end in case they may be useful.)
For errors there is https://php.net/manual/en/errorfunc.constants.php I but cannot find anything similar for exceptions.
I believe I can set my own exception values but are there agreed severity levels for general exceptions, PDOExceptions etc. The only place I find severity mentioned is in ErrorException. (I only use PDOExceptions at the moment but am trying to catch other "general" ones.)
Is a severity helper chart available somewhere? I freely admit I am struggling with exceptions.
PS I had a look at https://www.php.net/manual/en/errorexception.getseverity.php but that looks like it is for errors being sent as exceptions. I am quite confused now.
Error code arrays in case they are useful.
$phpErrorCodes = array (
1 => "E_ERROR",
2 => "E_WARNING",
4 => "E_PARSE",
8 => "E_NOTICE",
16 => "E_CORE_ERROR",
32 => "E_CORE_WARNING",
64 => "E_COMPILE_ERROR",
128 => "E_COMPILE_WARNING",
256 => "E_USER_ERROR",
512 => "E_USER_WARNING",
1024 => "E_USER_NOTICE",
2048 => "E_STRICT E_ALL",
4096 => "E_RECOVERABLE_ERROR",
8192 => "E_DEPRECATED",
16384=> "E_USER_DEPRECATED",
32767=> "E_ALL");
$phpErrorCodesFatal = array (
1 => "E_ERROR",
16 => "E_CORE_ERROR",
64 => "E_COMPILE_ERROR",
4096 => "E_RECOVERABLE_ERROR");
$phpErrorCodesFatalNumberOnly = array (
1 ,
16 ,
64 ,
4096);
//4096 recoverable but dangerous so treated as fatal
Exceptions
Let me try to clarify some misconceptions here.
Are there severity codes for exceptions?
No. All exceptions are severe.
An exception will stop the execution of your script. They are used to prevent the execution of the code which follows if the preceding code could not be executed.
How do I know if they are important?
All exceptions are important. When an exception is triggered it tells you as a developer that something unexpected has happened in the code. The code which you have written simply did not anticipate this happening and to prevent undefined behaviour it should stop processing immediately.
Unhandled exceptions would show up as PHP Warning. - mario
That is not true. Any unhandled exception will be converted to PHP Fatal error. If your application doesn't know how to handle an exceptional situation then PHP must immediately stop! It is not a warning you can ignore or silence; it's a fatal error.
As a rule of thumb I should treat exceptions as fatal errors?
Not necessarily. In rare cases a developer might expect a particular piece of code to throw an exception, which is possible to recover from or work around. The code logic anticipates something going wrong and has a way around the problem. If the exception is caught and handled this will not result in a fatal error.
The difference between an exception and a fatal error is that you can catch and recover from an exception, but not from a fatal error.
So if I don't do anything then exceptions turn to errors.
If you have not caught the exception and you do not have your own error handled implemented then PHP will default to the built-in error handler. The logic is that it will stop the script and throw an error with the exception as a message. This error will then, based on your configuration settings, either be logged on the server or displayed to the user.
Errors
PHP has a bad history. One of the problems with early PHP versions was that it was very lenient when it comes to badly written code. It would either try to guess what the correct action should be or merely warn the user about some serious problem. The outcome of this was that a lot of developers learned to simply ignore the warnings, notices and sometimes even errors.
With PHP 7 few things have changed.
PHP 7 changes how most errors are reported by PHP. Instead of reporting errors through the traditional error reporting mechanism used by PHP 5, most errors are now reported by throwing Error exceptions. An error is still not an exception, but it behaves like one. You can catch the error and if you don't you will still see "Fatal error: Uncaught Error: ...".
Modern PHP functionalities will now use exceptions. This means that userland code should also try to follow the same approach. Throw an exception when your code should stop the execution (don't use die/exit for this) and only catch them when when you know how to recover.
With future PHP releases the notices and warnings might be changed to Error exceptions. Don't ignore them now. Treat all of them as severe problems in your code and fix them now.
As per the comments, the general advice with exceptions is that you should catch them all - in that sense, all unhandled exceptions are severe.
However, you could still classify exceptions in your system in the way that you want. Let's say that at the top level of your app you have a call that enters the main app:
try
{
$this->mainApp();
}
catch (SeriousRuntimeException $e)
{
$this->handleSeriousError($e);
}
catch (TrivialRuntimeException $e)
{
$this->handleTrivialError($e);
}
Then in your code you can throw a SeriousRuntimeException or a TrivialRuntimeException as you see fit. In some cases you will have external exceptions to deal with, and you will need to convert them like so:
try
{
$this->databaseOperation();
}
catch (PDOException $e)
{
// Copy the exception properties that are important to you.
// Throw the appropriate severity based on what handler you
// want to run.
throw new SeriousRuntimeException($e->getMessage());
}
In this example I assume that both exception types are fatal, but they are handled differently. It is conceivable that you could have an exception type that is so trivial that the app keeps running (e.g. in a listener loop - it logs the fault and then carries on).

When an exception is thrown and bubble up where a solution is found, how can we send the control back to where the exception was thrown?

For concreteness, I present some code, in which what I want, I know, is not possible. I am looking for another way to get the same.
<?php
error_reporting(E_ALL | E_STRICT);
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
// When $errno is a notice, we would like a way for the execution to continue here. Since it is only a notice, eventually it could go back to where the notice was triggered.
}
set_error_handler("exception_error_handler");
Class TopLevelManagement {
private $genericInfo = "Very important to add in notices";
function highleveljob() {
try{
// In practice, the following statements could occur below in the execution tree, after a few other function calls.
$specific = new SpecificAndEncapsulated();
$specific->specificjob();
}
catch (ErrorException $e) {
$message = $e->getMessage() . $this->genericInfo;
mail($admin_email, "Consider the generic info and recover from this situation", $message);
// Here we would like the execution to continue where the exception was thrown. In the real world, this would be like "thank you error handler for SpecificAndEncapsulated::specificjob, we have taken care of the issue with the help of our larger perspective.".
}
}
}
Class SpecificAndEncapsulated {
function specificjob() {
// Some processing
if($unexpected == true) trigger_error('Here is how little I know, as I should', E_USER_NOTICE);
// Continue as expected after a notice.
}
}
?>
Of course, one solution is to pass $genericInfo as a parameter or as a global variable to SpecificAndEncapsulated::justdomyjob and let the error_handler take care of the issue without bubbling up any exception. However, this solution is not natural. There are other ways to systematically pass the variable $genericInfo to SpecificAndEncapsulated, but the issue will be the same. There should be no need to systematically pass the $genericInfo value, because it is not something that should concern SpecificAndEncapsulated, not even when an exception occurs, even less systematically at every call. A communication back to the issuer of the exception saying "thanks, now continue", after a notice has been managed at an higher level, is natural. Is there a support for this type of E_NOTICE or E_USER_NOTICE management?
Exceptions, by design, are errors after which normal execution cannot continue.
In the real world, it would go like this: a police officer (third party) calls a trucking company dispatcher (the top-level code) and says, "one of your trucks exploded in a ball of fire and the driver is in the hospital" (the job), and the dispatcher says "Noted. I expect the payload to arrive on schedule."
You have to catch exceptions inside the job if you want to continue the job. One viable approach is to pass an error handler function or a delegate object into the job.
PHP 5 has an exception model similar to that of other programming languages. An exception can be thrown, and caught ("catched") within PHP. Code may be surrounded in a try block, to facilitate the catching of potential exceptions. Each try must have at least one corresponding catch block. Multiple catch blocks can be used to catch different classes of exceptions. Normal execution (when no exception is thrown within the try block, or when a catch matching the thrown exception's class is not present) will continue after that last catch block defined in sequence. Exceptions can be thrown (or re-thrown) within a catch block.
When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block. If an exception is not caught, a PHP Fatal Error will be issued with an "Uncaught Exception ..." message, unless a handler has been defined with set_exception_handler().
In PHP 5.5 and later, a finally block may also be specified after the catch blocks. Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.
The thrown object must be an instance of the Exception class or a subclass of Exception. Trying to throw an object that is not will result in a PHP Fatal Error.

How do I capture require errors + error_handlers

I am trying to use the set_error_handler function to capture require errors.
while my custom error_handler is being used, the context seems completely off, although the trace is correct.
<?php
function error_handler($errno, $errstr, $errfile, $errline){
throw new Exception($errstr);
}
set_error_handler('error_handler');
try{
trigger_error("somethign",E_USER_NOTICE);
}catch(Exception $e){
echo "I got caught";
}
try{
require "something/that/does/not/exists.php";
}catch(Exception $e){
echo "I got caught";
}
As can be seen, when I trigger the first error, it triggers the error_handler which in turn is throwing an exception. Which is caught in the first try-catch.
The second time, where I try to include an un-existing file, again the error_handler is used, but the exception is not caught.
What is going on here?
I am using php 5.5.*
Tested in CLI mode.
Just read the documentation:
require is identical to include except upon failure it will also
produce a fatal E_COMPILE_ERROR level error. In other words, it will
halt the script
(http://php.net/manual/en/function.require.php)
That means, the script execution is aborted when this error is encountered. Your custom error handler will be used but throw will be ignore because that assumes further execution which is not allowed at this point.
It's important to remember that there's two general types of errors with PHP
Processing errors are caught when your program runs
Compile errors occur when you have bad syntax or when you try to do something impossible
In your case, require is a compile error. It will not execute your user defined function because it will never get that far. The compiler can't include the bad file and will fail out.

PHP exceptions thrown in error handler not caught by exception handler

I chose this title because I have the exact same problem as stated in here:
PHP exceptions thrown in error handler are not caught by exception handler
The author accepted the answer which said he obviously was doing something wrong.
My error and exception handler were working fine that last two years, but now im facing the exactly same problem.
I did a code update and also a server update (Plesk 11.5 running, the PHP version should be the same and is 5.3.2). I checked my code for the error, but a test made it clear that this cant be the problem:
I wrote the following testfile:
function errorHandler($errno, $errstr, $errfile, $errline, $errcontext) {
throw new Exception("this was an error");
}
function exceptionHandler($e) {
echo 'exceptionHandler';
}
set_error_handler('errorHandler');
set_exception_handler('exceptionHandler');
// test one:
throw new Exception(); // outputs "exceptionHandler"
// test two - uncomment the first test of course!
$test->blabla();
The second test should also output "exceptionHandler", but it doesn't!
The output is "Fatal error: Call to a member function blabla() on a non-object in ......./exeptiontest.php on line 0"
This problem drives me crazy at the moment. Any suggestions here? Any PHP settings that cause this?
Update (After reading your comment).
After an error handler has been executed program flow would went back to the expression after that where the error occurred. But it is unreliable to pass back program flow to a fatally failed script. That's why error handlers won't get called on fatal errors. The documentation says:
The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.
As a workaround (depending on your needs), you may define a shutdown function using register_shutdown_function().
Original Answer (It turned out that this was not the problem here)
You need to read the documentation of set_exception_handler() carefully, especially the code example:
function exception_handler($exception) {
echo "Uncaught exception: " , $exception->getMessage(), "\n";
}
set_exception_handler('exception_handler');
throw new Exception('Uncaught Exception');
echo "Not Executed\n";
Meaning in your case that $test->blabla() will never gets executed.
You might expect that the exception handler function works like a catch block, but that isn't the case. True is, that if an exception occurs and no catch block is defined, the program flow will take over to the exception handler which may gracefully shutdown the script - but not more. If you want to handle exceptions the right way, use try / catch
Just to make it more clear: exception handlers behave different than error handlers. After returning from an exception handler the program will terminate while the program flow wents back to the expression after the error when returning from an error handler.

Ensure PHP shutdown function is called

I'm trying to catch as many errors as possible in PHP and properly handle them in a non-default way. My question is best illustrated in this example:
<?php
function errorHandler($errno, $errstr, $errfile, $errline){
echo "Error handler here <br>\n";
//throw new Exception($errstr);
}
function shutdownFunction() {
echo "Hi I'm in here <br>\n";
}
set_error_handler("errorHandler");
register_shutdown_function("shutdownFunction");
try {
$undefined->ok(); // causes some error
} catch(Exception $e) {
echo "Caught the exception <br>\n";
}
The result of running this code as a PHP program will indicate that errorHandler() is run, a PHP error is printed (if "display_errors" is set to "On"), and then shutdownFunction() is run.
The problem I'm having arises if I uncomment out that exception throw; I want to throw exceptions on PHP errors as often as possible. If I uncomment the throw statement out, then the error handler is called, which throws an exception thus causing a further error which results in shutdownFunction() not to be called.
It is my understanding that I can't make this error into a catchable exception; however, I would at least like to ensure that the shutdown function is called without restricting my ability to catch at least some php errors as exceptions (meaning I don't want to remove that throw statement in errorHandler()).
Is there some check in the errorHandler that I can do to check whether or not throwing it will cause shutdownFunction() to be bypassed?
Throw an exception from the error handler, then use set_exception_handler to handle uncaught exceptions.
So the way I'm solving this is to check if the $errstr parameter starts with "Undefined variable" - and if it does, then I'm not throwing the exception. I suppose if there are any other errors that have a similar issue, i'll do the same thing with those. I can't find a comprehensive list of php errors and what their affects are though, so it'll have to be ad hoc

Categories