|» Metadata||» Status|
Error Handling Guidelines for PHP5 packages submitted to PEAR
This document defines guidelines for error handling within PEAR, for PHP5 packages. It was written to cope with Exceptions, introduced in Zend Engine 2 as the error handling mechanism. The final objective is to integrate the document text into the PEAR Coding Guidelines.
This document is targeted at PHP developers writing packages for submission into the PEAR repository. As any coding guidelines, it is useful to developers using PHP in other environments. The requirements for reading this text are only familiarity with PHP as a programming language, as well as familiarity with the mechanism of Exceptions as an error handling mechanism.
For those PHP developers unfamiliar with Exceptions, the wiki page from which this document was extracted provides a good introduction, as well as pointers to other references.
Definition of error
An error is defined as an unexpected, invalid program state from which it is impossible to recover. For the sake of definition, recovery scope is defined as the method scope. Incomplete recovery is considered a recovery.One pretty straightforward example for an error:
In this example the objective of the method is to connect to the given DSN. Since it can't do anything but ask PEAR DB to do it, whenever DB returns an error, the only option is to bail out and launch the exception.The next example will introduce the concept of recovery:
This second example shows an exception being caught and recovered from. Altough the lower level connectDB method is unable to do anything but throw an error when one database connection fails, the upper level connect method knows the object can go by with any one of the configured databases. Since the error was recovered from, the exception is silenced at this level and not rethrown.The last example illustrates incomplete recovery:
The recovery produces side effects, so it is considered incomplete. However, the program may proceed, so the exception is considered handled, and must not be rethrown. As in the previous example, when silencing the exception, logging or warning should occur.
Error Signaling in PHP5 PEAR packages
Error conditions in PEAR packages written for PHP5 must be signaled using exceptions. Usage of return codes or return PEAR_Error objects is deprecated in favor of exceptions. Naturally, packages providing compatibility with PHP4 do not fall under these coding guidelines, and may thus use the error handling mechanisms defined in the PHP4 PEAR coding guidelines.
An exception should be thrown whenever an error condition is met, according to the definition provided in the previous section. The thrown exception should contain enough information to debug the error and quickly identify the error cause. Note that, during production runs, no exception should reach the end-user, so there is no need for concern about technical complexity in the exception error messages.
The basic PEAR_Exception contains a textual error, describing the program state that led to the throw and, optionally, a wrapped lower level exception, containing more info on the lower level causes of the error.
The kind of information to be included in the Exception is dependent on the error condition. From the point of view of exception throwing, there are three classes of error conditions:
Errors signaled via return codes by lower level libraries, if unrecoverable, should be turned into exceptions. The error description should try to convey all information contained in the original error. One example, is the connect method previously presented:
Lower library exceptions, if they can't be corrected, should either be rethrown or bubbled up. When rethrowing, the original exception must be wrapped inside the one being thrown. When letting the exception bubble up, the exception just isn't handled and will continue up the call stack in search of a handler.One example for rethrowing:
And the same example for bubbling up:
The case between rethrowing or bubbling up is one of software architecture: Exceptions should be bubbled up, except in these two cases:
Exceptions and normal program flow
Exceptions should never be used as normal program flow. If removing all exception handling logic (try-catch statements) from the program, the remaining code should represent the "One True Path" -- the flow that would be executed in the absence of errors.
This requirement is equivalent to requiring that exceptions be thrown only on error conditions, and never in normal program states.One example of a method that wrongly uses the bubble up capability of exceptions to return a result from a deep recursion:
In the example the ResultException is simply using the "eject!" qualities of exception handling to jump out of deeply nested recursion. When actually used to signify an error this is a very powerful feature, but in the example above this is simply lazy development.
Exception class hierarchies
All of PEAR packages exceptions must be descendant from PEAR_Exception. PEAR_Exception provides exception wrapping abilities, absent from the top level PHP Exception class, and needed to comply with the previous section requirements.
Aditionally, each PEAR package must provide a top level exception, named <Package_Name>_Exception. It is considered best practice that the package never throws Exceptions that aren't descendant from its top level exception.
Exception silencing (Section placeholder)
This section will contain the requirements related to Exception silencing. Wether to support exception silencing or not is a cause for debate, and a vote will happen after (if) the main text approval. Supporting exception silencing, depending on PHP support for canceling throws, may require that an Exception throw be done via a PEAR_Exception method (PEAR_Exception::throw) which optionally silences the exception and allows the program to flow normally.
Because PHP, unlike Java, does not require you to explicitly state which Exceptions a method throws in the method signature, it is critical that Exceptions be thoroughly documented in your method headers.
Exceptions should be documented using the @throws phpdoc keyword:
In many cases middle layers of an application will rewrap any lower-level exceptions into more meaningful application exceptions. This also needs to be made clear:
In other cases your method may simply be a conduit through which lower level exceptions can pass freely. As challenging as it may be, your method should also document which exceptions it is not catching.
Exceptions as part of the API
Exceptions play a critical role in the API of your library. Developers using your library depend on accurate descriptions of where and why exceptions might be thrown from your package. Documentation is critical. Also maintaining the types of messages that are thrown is also an important requirement for maintaining backwards-compatibility.
Because Exceptions are critical to the API of your package, you must ensure that you don't break backwards compatibility by making changes to exceptions.
Things that break BC include:
Things that do not break BC:
|» Dependencies||» Links|
|» Timeline||» Changelog|