Go to file
Adrian dd5a76d39c Clarifications on error handling 2016-11-15 19:20:51 +01:00
config Initial commit 2016-11-15 18:36:06 +01:00
public Initial commit 2016-11-15 18:36:06 +01:00
src Initial commit 2016-11-15 18:36:06 +01:00
README.md Clarifications on error handling 2016-11-15 19:20:51 +01:00
composer.json Initial commit 2016-11-15 18:36:06 +01:00
composer.lock Initial commit 2016-11-15 18:36:06 +01:00

README.md

PHP Error Handler Demonstration

This small project demonstrates the internals of PHP's error handling.

It is written for Zend Expressive to demonstrate how the error handler in Expressive 1.0.3 swallows all errors.

Error Handling in PHP

At most two error handlers in PHP are active at the same time. User defined error handlers using set_error_handler are implemented as a stack, but only the top handler is executed.

  • If the error handler returns false, PHP's internal error handler is executed as well.
  • If the error handler was registered with an error type mask, it is not executed and PHP's internal error handler is executed immediately.

The implementation of this behaviour is in Zend/zend.c and the following lines.

PHP's internal error handler and the @ operator

Only PHP's internal error handler sets the error reported by error_get_last. This means: If a user defined error handler is registered, the last error can only be retrieved if the handler returns false.

Error handlers are executed, if registered with the appropriate bitmask, but regardless whether the statement was prepended with the @ operator. However, error_reporting returns zero, if the @ was prepended.

Demonstration

Install the composer packages:

composer install --no-dev

Run the demonstration with PHP's integrated web server:

php -S localhost:8080 -t public

Observations

An error handler which converts errors to ErrorExceptions is registered at the start of the script. Additionally, it prints the registration message to stderr.

However, because of the described circumstances above, it is not executed as intended.

Use Globals in public/index.php to modify the behaviour.

The default is:

Test\Globals::$registerHandlerInMiddleware = true;
Test\Globals::$restoreBeforeRegistering = false;
Test\Globals::$triggerError = true;

A second error handler is registered in ErrorMiddleware. Therefore, errors are converted to ErrorExceptions and can be caught by the middleware.

No second error handler

Test\Globals::$registerHandlerInMiddleware = false;
Test\Globals::$restoreBeforeRegistering = false;

No second error handler is registered and it is assumed that the global error handler (number one) is handles errors.

This is not the case, because Expressive registers an error handler as described in the introduction. Since it returns false, PHP's internal error handler prints the error on the console and execution resumes as normal.

Removing Expressive's error handler

Test\Globals::$registerHandlerInMiddleware = false;
Test\Globals::$restoreBeforeRegistering = true;

If Expressive's error handler is removed by popping it from the handler stack, the global error handler can handle the error.

Consequences

An error handler registered before running the application is not usable.

Middleware should not be required to install a new handler, because they can not know what the previous handler was.

I believe, it is a bug in Expressive which should be fixed.