This is an old revision of the document!
Request for Comments: Enhanced Error Handling
- Version: 0.1
- Date: 2009-12-27
- Author: Hans-Peter Oeri hp@oeri.ch
- Status: Drafting
- First Published at: http://wiki.php.net/rfc/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
- An intl extension-level implementation is being created by kampfcaspar at https://saintcyr.oeri.ch/trac/php-intl/
- Given enough feedback and operating experience, the enhanced error handling could be bundled in an “error extension”
- 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 |