Skip to content
This repository was archived by the owner on Mar 27, 2019. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 96 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,57 +13,71 @@

## Usage

Installing the *Asplode* error handler can be achieved by a single statement:
The *Asplode* error handler can be installed with a single statement:

```php
Eloquent\Asplode\Asplode::install();
```

## What does *Asplode* do?

*Asplode* is very simple library that sets up an [error handler] to throw
[ErrorException] exceptions instead of using the default PHP error handler. This
method of error handling has proven to be extremely effective, and similar
systems are in use across major PHP frameworks such as [Symfony].
*Asplode* is a very simple PHP [error handler] implementation that throws
[ErrorException] exceptions instead of using the default PHP error handling
behaviour. This means that all non-fatal runtime errors are presented to the
developer in the form of an exception. It also means that any unhandled errors
are delivered to a single point, the global exception handler.

## Why use *Asplode*?

Exceptions offer a much more consistent way to handle errors. In modern PHP
development it is generally considered best-practice to use an exception rather
than a legacy-style error.
Developers need the ability to decide how their code behaves when an error
occurs. Exceptions offer the only truly consistent way to report and recover
from errors in PHP.

*Asplode* offers a hassle-free way to improve the error handling in a PHP
project. It also provides a consistent error handling implementation across
any project or library it's used in, allowing for easier integration.
This method of handling errors has proven to be extremely effective. Similar
strategies are used in major PHP frameworks such as [Symfony].

*Asplode* is a standalone implementation that can be used for any project.

## Fatal error handling

*Asplode* provides a simple way to improve the handling of fatal errors. Whilst
fatals can't really be handled in the same way as regular errors, if a global
exception handler is installed, it can be passed an Exception representing the
fatal error just before PHP execution completes. This allows developers to
gracefully inform the user of fatal errors before shutdown occurs.
While it's not feasible to *recover* from fatal PHP errors, it is possible to
*report* fatal errors in the same manner as uncaught exceptions.

With *Asplode*, fatal errors cause a synthesized exception representing the
fatal error to be passed to the global exception handler. This allows developers
to gracefully inform the user of fatal errors just before the PHP interpreter is
shut down.

The *Asplode* fatal error handler is installed by default, but is only activated
if a global exception handler is installed.

```php
// a global exception handler must be in place:
set_exception_handler(
function (Exception $e) {
echo $e->getMessage();
}
);

Eloquent\Asplode\Asplode::installFatalHandler();
Eloquent\Asplode\Asplode::install();
```

To use *Asplode* without the fatal error handler, use `installErrorHandler()`
instead of `Asplode::install()`. To use only the fatal error handler, use
`installFatalHandler()`.

Please note that because the PHP autoloader will not function during a fatal
error, and as such custom exception handlers should explicitly load their
dependencies where possible.

## Asserting that the current error handler is compatible

Code that assumes the use of *Asplode* will not work as expected unless the
right type of error handler is installed. For example, code expecting to catch
an `ErrorException` on failure will have unpredictable results if the installed
Code that assumes the use of *Asplode* may not work as expected unless the right
type of error handler is installed. For example, code expecting to catch an
`ErrorException` on failure will have unpredictable results if the installed
error handler does not throw `ErrorException` instances.

To ensure that a correctly configured error handler is installed, *Asplode*
provides the `Asplode::assertCompatibleHandler()` method:
provides the `Asplode::assertCompatibleHandler()` method.

```php
use Eloquent\Asplode\Asplode;
Expand All @@ -76,64 +90,27 @@ try {
}
```

## Executing legacy code

Sometimes it is unavoidable to work with code that uses bad practices. For
example, a old PHP library might be quite functional and useful, but it may not
anticipate exceptions being thrown when an error occurs.

*Asplode*'s handler stacks both implement an `executeWith()` method that allows
code to be executed with a different handler than the one currently installed.
This method pops all current handlers off the stack temporarily, installs the
specified handler (if one is provided), executes the supplied callback, restores
the handler stack, and returns the result of the callback's execution.

```php
use Eloquent\Asplode\HandlerStack\ErrorHandlerStack;

$stack = new ErrorHandlerStack;

$result = $stack->executeWith(
function () {
// this code will be executed under the default handler
}
);

$result = $stack->executeWith(
function () {
// this code will be executed under the supplied handler
},
'errorHandlerFunctionName'
);

$result = $stack->executeWith(
function () {
// this code will be executed under the supplied handler
},
function ($severity, $message, $path, $lineNumber) {
// handle the error
}
);
```
A *compatible* error handler is any handler that throws `ErrorException`
exceptions. It does not need to be the implementation provided by *Asplode*.

## Managing PHP's handler stacks

PHP's error handlers and exception handlers function roughly as a [stack].
However, the implementation is quite limited, and frankly, bad. *Asplode*
includes classes to aid in management of these stacks, which can be harnessed to
manage error handling in a simple, and flexible manner.
PHP's error and exception handlers approximate the behaviour of a [stack].
However, the interface for manipulating the stack is limited, and quite frankly,
poorly implemented.

The two classes responsible for management of these stacks are
*Asplode* includes two classes to aid in management of these stacks,
[ErrorHandlerStack] and [ExceptionHandlerStack]. Both implement
[HandlerStackInterface]. These classes do not require the use of the *Asplode*
handler, they can be used in a standalone manner to manage the handler stacks.
[HandlerStackInterface] which provides a familiar interface for working with
stacks. These classes do not require the use of the *Asplode* handler; they can
be used in a standalone manner to manage the handler stacks.

## Migrating existing code to work with Asplode

When the *Asplode* error handler is installed, the [error_reporting] setting
will no longer have any effect. Notices, warnings, and errors will all throw an
exception. Deprecation notices will not throw an exception, but will still be
logged, as long as PHP's error logging is correctly configured.
will no longer have any effect. Notices, warnings, and errors will all result in
an exception being thrown. Deprecation notices will not throw an exception, but
will still be logged provided that PHP is configured to do so.

Code that has been written to handle legacy-style PHP errors will most likely
need to be re-written. As an example, this type of logic:
Expand All @@ -146,7 +123,7 @@ if ($fp === false) {
}
```

would need to be replaced with something like:
would need to be rewritten to to handle exceptions:

```php
try {
Expand All @@ -156,12 +133,56 @@ try {
}
```

It's important to note that PHP can be very inconsistent in the way it handles
It's important to note that PHP can be very inconsistent in the way it reports
error conditions. In some cases functions will simply return a boolean false
when an error occurs; or it may have even stranger, less standard behaviour.
when an error occurs, others require the developer to call additional functions
to check for errors, while others still maybe have entirely non-standard
behaviour.

*Asplode* does not free the developer from the responsibility of reading the PHP
documentation, or making sure that they account for all possible outcomes.
documentation, or making sure that they account for all possible error
conditions.

## Executing legacy code

Sometimes working with code that uses bad practices is unavoidable. A legacy PHP
library might be perfectly functional and useful, but it may not anticipate
exceptions being thrown when an error occurs.

*Asplode*'s exception and error handler stacks both implement an `executeWith()`
method that allows code to be executed with a different handler than the one
currently installed. This method pops all current handlers off the stack
temporarily, installs the specified handler (if one is provided) and executes
the supplied callback. The original handler is restored after the callback is
executed.

```php
use Eloquent\Asplode\HandlerStack\ErrorHandlerStack;

$stack = new ErrorHandlerStack;

$result = $stack->executeWith(
function () {
// this code will be executed under the default handler
}
);

$result = $stack->executeWith(
function () {
// this code will be executed under the supplied handler
},
'errorHandlerFunctionName'
);

$result = $stack->executeWith(
function () {
// this code will be executed under the supplied handler
},
function ($severity, $message, $path, $lineNumber) {
// handle the error
}
);
```

<!-- References -->

Expand Down