====== Request for Comments: Enhanced Error Handling ====== * Version: 0.5 * Date: 2010-01-10 * Author: Hans-Peter Oeri * Status: Draft (Inactive) * First Published at: http://wiki.php.net/rfc/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 ===== - Development: https://saintcyr.oeri.ch/trac/php-error/ - 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 | | 2010-01-07 | kampfcaspar | Overhaul | | 2010-01-10 | kampfcaspar | +Differences Observed |