rfc:enhanced_error_handling

This is an old revision of the document!


Request for Comments: Enhanced Error Handling

Introduction

php_error(), zend_throw_extension(), 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 proprietary flag to define error behaviour that allows exceptions - but limits php_error to E_WARNING.

I state that error behaviour has to be in the hands of the user (php coder). He has to handle the situation and live with the consequences of failed calls. Some may prefer php_error()s, others prefer exceptions; some are ok with E_WARNINGs that indicate severe situations, others prefer E_ERROR for those cases; and some might want to either suppress all messages or default to a big exception block for a whole script in production systems (redirecting to a “sorry, something went wrong” page).

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.

Definitions

Error Behaviours

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
Error same as Suppress, but issue a php_error as well
Throw same as Suppress, but throw an exception as well

The error behaviour should be configurable by the user; preferably system-wide and overridable. An extension should not, however, decide to differ from the user's wishes.

In any case, there should be a method to get at the noted-down error information, e.g. error code and message.

Error Parameters

php_error needs an E_* value to distinguish warnings and errors and the Throw behaviour needs an exception class.

Defaults for those parameters should be configurable like the error behaviour. The extension should however be able, while issuing an error, to set appropriate parameters if necessary.

For extreme cases - like “one big exception block per script” - the default parameters should be enforcable by the user's wishes.

Error Container Hierarchy

PHP lives of its extensibility. An extension can offer a whole bunch of different classes that are related; take intl as an example. It could be interesting for the user to differentiate his error reactions by object and/or extension. He could wish to let the whole of intl throw a PersonalIntlExceptionClass upon errors or decide to switch to exceptions for a long block of translation calls (not testing each and every result on its own) while he usually prefers E_ERRORS being issued.

The proposal therefore stands for a three-level hierarchy:

Level Description
Global Define behaviour and default parameters
Could be used to force behaviour as well as parameters, eg. in production
Field/Extension Inherits from Global level
Overrides behaviour and parameters for a “field”, e.g. all i18n in intl.so
Object Inherits from Field level
Overrides behaviour and parameters for a single object

If any level enforces behaviour and/or parameters, the lower levels will not deviate.

Special Cases

Enforced Exception class

Each 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” extension 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.

Implementation

Error Container

Each level mentioned above would need storage for error parameters as well as error results:

  struct _error_container {
    // identification
    char *name;
 
    // used to propagate errors up/down
    error_container *parent;
    error_container *delegate;
 
    // parameters as set by ini or api
    enum error_behaviour behaviour;
    zend_bool            force_behaviour;
    long                 level;
    char *               exception;
    zend_bool            force_parameters;
 
    // last error's data
    char *container;
    long  code;
    char *msg;
  }
  • name
    There would be a global container (probably NULL), per extension (extension name) and per object (“object container”)
  • parent
    Used to create the hierarchy of containers, e.g. to force a behaviour to lower containers
  • delegate
    Rather than copying error data two or even three times, set a pointer to the real data (container's destructors will have to correct pointers)
  • behaviour
    Some enum for the behaviours mentioned above
  • force-*
    Do force either behaviour or parameters for this container and children
  • level
    Either default or forced error level (E_*) for zend_error()
  • exception
    Either default or forced exception name (name instead of ce ptr allows to autoload)
  • container
    In which container the error did occur (for user presentation)
  • code / msg
    The usual error parameters

Error Function

  void error_throw( error_container *container, long level, char *exception, long code TSRMLS_DC, char *format, ...);

Each use of the error function should nevertheless be followed by a defined return value.

User API

  int    error_get_code( [string|object] );
  string error_get_message( [string|object] );
  string error_get_origin( [string|object] );

  void   error_set_behaviour( [string|object], $behaviour );
  void   error_set_parameters( [string|object], $errorlevel [, $exception] );
  void   error_reset_behaviour( [string|object] );
  void   error_reset_parameters( [string|object] );

A container should be identifiable as NULL (global), a string (e.g. extension containers) or by giving an object that supports this scheme. As written above, any error should “trickle up” the chain, such that an error_get_code() gets the (globally) last error's code, even if it happend in a child container.

Integration

Short of extending core structures of the Zend Engine and for better backwards compatibility, the error containers would have to be held in a hash. Extensions and objects would be able to “register” their container - as well as unregister them upon destruction. Extension names and object handles could be used as keys. As errors should be a relatively rare occurence, not much overhead is to be expected.

Backwards Compatibility

Extensions

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

Core

I would personally not touch the core, but it's error behaviour should be representable as well.

Course of Action and Patch

  1. An intl extension-level implementation is being created by kampfcaspar at https://saintcyr.oeri.ch/trac/php-intl/
  2. Given enough feedback and operating experience, the enhanced error handling could be bundled in an “error extension”
  3. 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
rfc/enhanced_error_handling.1262003412.txt.gz · Last modified: 2017/09/22 13:28 (external edit)