rfc:enhanced_error_handling

Request for Comments: Enhanced Error Handling

Introduction

php_error(), zend_throw_exception(), the @-operator... PHP and/or the Zend Engine respectively offer a variety of error issuance and handling mechanisms. There is, however, no encompassing concepts: Core functions only issue php_errors (suppressable), intl 1.0.3 by default suppresses all errors but can activate php_errors, pdo however has a flag to define error behaviour that allows exceptions - but limits php_error to E_WARNING. Furthermore, each extension has a different way of changing its error behaviour etc. etc.

I think that error behaviour has to be in the hands of the user (php coder). Different users have different preferences - and prefer different behaviour in different situations.

Scope

Error conditions are encountered in a variety of places and cases. This RFC only covers error conditions in a running script, e.g. a file was not found.

All other situations - like compilation problems, core errors in engine startup/shutdown - are out of scope.

Differences Observed

  • Almost all error behaviours include a human readable error message and a machine readable error code; the 'original' zend_error however, does not define error codes, but far less granular error types (which are nevertheless named errno in parts of the docs, e.g. set_error_handler).
  • Error codes - if used - do not follow any common semantic rules and are therefore not interpretable without knowing their source; even more, they might be defined externally (SQLSTATE or linked library).
  • While traditionally error numbers have mostly been numeric, also alphanumeric variants exist (SQLSTATE).
  • Error codes and - to a lesser extent - error messages are traditionally held in a special variable or place for later inspection. Information held there may be reset upon request/new error (errno in C, error_get_last in PHP) or with any subsequent successful action/call (PDO, intl).
  • There may be just a single holding space for later inspection of error information (error_get_last), a a compartementalized holding space for independent error information (PDO vs. PDOStatement) or even a hierarchical one, which combines the earlier two (intl).
  • Functions not bailing out on error situations should return a defined error value. There is, however, a variety of defined error values - also dependent on the required valid return value range, which might include values that other functions use as error indicating value.
  • Definition of error actions - as described in the introduction - is handled by php.ini or object calls or not available at all. None of it offers all options.

Goals

The goal would be to create a framework in which

  • the PHP user decides, what kind of error reaction he wishes; that includes
  • having a single error call that abstracts away from zend_errors/exceptions and
  • a minimal inheritance of error behaviours, such that different extensions and/or resource objects might be configured to react differently.
  • offers a C-level API for compiled extensions as well as a PHP-level API for frameworks in that language.
  • can be used in OOP as well as non-OOP situations.

Such goals can only be achieved under the side condition of complete backwards compatibility. Default behaviour of existing php and extensions must not be changed - all existing error behaviour must be mappable.

Definitions

Error Actions

If an error condition is met, different types of reactions are possible:

Name Description
Suppress Note down error and return an error value, e.g. false/NULL
Monitor same as Suppress, but let a php monitor know - independent of error handling taken by user code
Error same as Suppress, but issue a php_error as well
Throw same as Suppress, but throw an exception as well

The error action should be configurable by the user. An extension should usually not differ from the user's wishes.

Noted-down error information, e.g. error code and message etc., would be available by a standardized API.

Error Levels

Existing extensions use their error mechanisms not only to issue grave errors, but also to transport mere “warnings” to the user - much like a message transport. As this is pre-existing, both an error and a warning call should be supplied. The latter - ignoring all configuration - choosing Suppress or Monitor as appropraiate action.

Error Parameters

Different behaviours, ask for different additional parameters: zend_error needs an E_* value to distinguish warnings and errors and throwing an exception needs an exception class.

Default values for those parameters should be configurable like the error action. However, more specific values - like an BadFunctionCallException while testing parameters - have to be definable with the error call itself.

Error Container Hierarchy

PHP lives of its extensibility. Different extensions currently show different default error behaviours. It goes without saying, that error behaviour must keep being configurable by extension.

As some extensions currently do, I propose to add a layer of error configurability on object level (see e.g. PDO -> by PDO connection). They should default to the extension's behaviour, but be configurable differently.

Without any hassle, inheritance could be implemented. The error configuration on a PDO connection could be inherited its PDOStatements.

Special Cases

Enforced Exception class

A lower hierarchy level could enforce the use of a specific exception class, e.g. PersonalIntlExceptionClass() for all of intl.so. While forcing such a common class does ease catching, some information a more specific class could provide is lost.

As of PHP 5.3, the concept of exception chaining has been introduced, whereas a “previous” exception can be attached. In order to keep the previously lost information, the concrete exception class given upon issuing an error should be chained to the enforced class.

Notices

Notices are issued en masse all over PHP - even for plain language constructs like array access. They are regularly ignored on production systems and seldomly indicate errors. I like to compare them to warnings of a compiler, indicating that something could go wrong here (php does issue them at runtime, not compile-time).

Whereas an user could decide to downgrade every error to a notice in this concept, actual notices should be issued the same way as before.

@-Operator

The @ operator does suppress error issuance for the evaluation of the expression it is prepended to. It does so - currently - completely for php_error()s, not however for exceptions.

If possible, the operator should be limited to errors in the execution of scripts but “silence” thrown exceptions as well (like a “per object” Suppress above). It's functionality should be prohibited, if a certain behaviour is enforced.

C API

The following does not represent compilable code.

Error Container

The levels mentioned above would need a common container structure to hold error configuration as well as “last error” information. The same construct can be hold by extensions as well as objects.

  struct error_container {
    char *name;                         // identification, e.g. extension name
    error_container *parent;            // hierarchy: inherit parent's configuration
    error_container *delegate;          // hierarchy: "last" error is in a child from this one
 
    error_action     action;            // error behaviour for this container
    long             level;             // default E_*
    zend_class_entry *exception;        // default exception class
 
    error_incident incident;            // last error's information on this container's level
  }

Error Function

  error_behaviour error_yell( error_container *container, long level, char *exception, long code, char *msg );
  error_behaviour error_yellf( error_container *container, long level, char *exception, long code, char *format, ... );

Instead of writing explicit calls to zend_error or zend_exception_throw, this call abstracts away the error behaviour. It does, however return the actual action initiated.

As errors might be suppressed, after the use of error_yell a defined return value should be returned to PHP. One cannot be sure that the engine will branch away into an error or exception handler.

Utility functions

Apart from the error issuing function, several utility functions are needed, e.g.:

  • Creation/Destruction of error containers
  • Setting/Resetting error behaviour of containers
  • Reading error incidents
  • Clearing error incidents

The corresponding API depends too much on implementation detais to discuss those here.

PHP API

Apart from extensions in C, php code itself could use such unified error configuration as well. Above mentioned error_yell function as well as utility functions to configure error actions and read error information should be available.

A standard ErrorClass - implementing an object level error container plus standardized methods to access it - should be available:

  class ErrorClass {
    function __construct( $parent_container )
    function setErrorAction(...);
    function resetErrorAction();
    function yellError(...);
    function getLastError();
  }

Of course, the internal ErrorClass could be used as base class for extension classes as well.

Error containers would be identified by:

  • NULL: global/highest hierarchy
  • string: extension-level container (e.g. in a hash)
  • ErrorClass: object-level container

In order to allow PHP frameworks to depend on an “extension level” container, such a string-identified container should be creatable on user level.

Backwards Compatibility

Extensions

Extensions can keep backwards compatibility, if their current default behaviour is mapped to the above-mentioned extension level. Even special cases should be representable with the mentioned actions, parameters and enforcement.

Core

The core should probably only be touched if this proposal (or something analogous) is included in it...

Course of Action and Patch

  1. Given enough feedback and operating experience, the enhanced error handling could be bundled in an “error extension”
  2. Other extensions could use the “error extension”

Changelog

Date Author Message
2009-12-27 kampfcaspar Created Initial Version
2009-12-28 kampfcaspar Added draft API
2010-01-07 kampfcaspar Overhaul
2010-01-10 kampfcaspar +Differences Observed
rfc/enhanced_error_handling.txt · Last modified: 2017/09/22 13:28 by 127.0.0.1