sapphirecat/lisp-signals

Lisp-style signals, conditions, and restarts

v0.9.0 2016-08-07 21:08 UTC

This package is auto-updated.

Last update: 2024-12-21 10:10:58 UTC


README

A signal/condition/restart system, inspired by Common Lisp and Peter Seibel.

What even?

In most languages, PHP included, an exception is thrown at a low level and caught higher on the call stack. By the time it is caught, execution has left the lower levels, and there's no way to return to them.

In Common Lisp, the equivalent notions are that a signal is sent, and handled by a signal handler function. But, there's a third part, restarts, which can be registered at any level in between. The signal handler can choose any restart to invoke, and execution resumes inside that function, even if it's below the level of where the signal handler was registered.

The simplest example is implemented in the appDoesNothing.php example, using stuff defined in common.inc.php. All of the examples follow the log-parsing example in Chapter 19 of Practical Common Lisp.

I'm a lisper, how does it happen in PHP?

Restarts are a special exception class that gets thrown, and declaring the restart is actually writing a try/catch block and telling the Signal class the restart exists. Low-level code calls downward into the signal-handling stuff to send errors or other conditions, so the stack stays intact.

I took another liberty with the Common Lisp design: signal names are actually the class name of a signal object that is sent. So an error is really a Sapphirecat\Signal\Error instance. This allows for signals to package however much data they want, and provide behavior for it, all in a single argument to the signal system (and handler functions.)

In this library, a signal handler is bound by Signal::receive(), signals are sent with Signal::send(), and active restarts are declared by Signal::restart(). The condition is a SignalInterface and a restart is actually invoked by throwing a BaseRestart.

The error and warning protocols are implemented in Signal::error() and Signal::warning() (with a Silence restart), respectively. Finally, the built-in Error condition can be subclassed, and sent with Signal::sendError().

(I hope this terminology is accurate. I haven't gone deep into Lisp in a while.)

Installation

Get it from composer:

composer require sapphirecat/lisp-signals "~0.9.0"

Alternatively, include the autoload.php in this repository's top level, and enjoy.

License

2-clause BSD. If it breaks, you get to keep the pieces.