-
Notifications
You must be signed in to change notification settings - Fork 0
Usage: Exceptables
These examples will all use the following Error enum:
use at\exceptable\ {
Error,
IsError
};
enum FooError : int implements Error {
use IsError;
case UnknownFoo = 1;
case SpookyFoo = 2;
public const MESSAGES = [
self::UnknownFoo->name => "i don't know who, you think is foo, but it's not {foo}",
self::SpookyFoo->name => "ph'nglui mglw'nafh {foo} r'lyeh wgah'nagl fhtagn"
];
}
The IsExceptable
trait provides a complete base implementation for the Exceptable
interface. A working implementation is easy: extend
a Throwable
class, implement
the Exceptable
interface, and use
the IsExceptable
trait
Even easier, however, is to extend
from one of the Spl Exceptables.
So here's a brief example Exceptable in three easy steps:
<?php
// step one: choose an Spl Exceptable base class
use at\exceptable\Spl\RuntimeException;
// step two: extend your new Exceptable class from it
class FooException extends RuntimeException {}
// step three:
// there's no step three
Exceptables have very straightforward constructors. The first, and often only, argument you'll need to provide is the Error case:
<?php
throw new FooException(FooError::UnknownFoo);
// Fatal error: Uncaught FooException: FooError.UnknownFoo
Note, our Exceptable set an exception message for us. But, this message has no details - why didn't it use the full message that FooError defined? Let's add some $context
.
<?php
throw new FooException(FooError::UnknownFoo, ["foo" => "foobedobedoo"]);
// Fatal error: Uncaught FooException: FooError.UnknownFoo: i don't know who, you think is foo, but it's not foobedobedoo
Just like normal exceptions, you can pass a $previous
exception (exceptable or not) as the third argument.
Uncaught exceptions are great and all, but what if we want to catch them? How do we know what to do with them? Because you have defined error cases, your program can read Exceptables almost as well as you can - without looking at the message at all.
<?php
try {
aFunctionThatThrowsFooException();
} catch (FooException $x) {
if ($x->is(FooException::UnknownFoo)) {
// we know how to handle this; everyone can be happy
introduceFoo($x->context()["foo"]);
} else {
// nnnnope too spooky
throw $x;
}
}
In the above examples, you might have noticed some of those useful utilities.
We can use the is()
method to identify Exceptables by their Error case.
Similarly, there is has()
to see if the Exceptable, or any "previous" exceptions, match a given Error case.
Since we can pass a $context
array to the Exceptable, it makes sense that we'd have a context()
method to get it back.
When you have a chain of previous exception(s), it's common that the initial exception is of more interest than other, intermediate exceptions; so we have root()
to get it directly (if there is no previous exception, no problem).
There is a test suite for the base IsExceptable
trait, which is also be useful as a starting point for testing your own Exceptables. Run it with composer test:unit
.