php-enspired/exceptable

Augmented features for exceptions and exception handling.

v5.0 2024-03-20 01:50 UTC

README

how exceptable!

Exceptables make exceptions exceptional. Exceptables provide some nice utility methods, but the main benefit is having a way to conveniently and quickly organize all the error cases in your application.

Exceptables are easy to create and pass details to, they provide access to error info for both humans and code, and make it easy to extend, add, and maintain error handling code as your application grows.

dependencies

Requires php 8.2 or later.

ICU support requires the intl extension.

installation

Recommended installation method is via Composer: simply composer require php-enspired/exceptable ^5.

a quick taste

<?php

use at\exceptable\ {
  Error,
  Handler\ExceptionHandler,
  Handler\Handler,
  IsError
};

// a simple Error, just for you
enum FooError : int implements Error {
  use IsError;

  case UnknownFoo = 1;
  public const MESSAGES = [
    self::UnknownFoo->name => "i don't know who, you think is foo, but it's not {foo}"
  ];
}

(FooError::UnknownFoo)(["foo" => "foobedobedoo"]);
// on your screen:
// Fatal error: Uncaught at\exceptable\Spl\RuntimeException: i don't know who, you think is foo, but it's not foobedobedoo

$handler = new Handler();
$handler->onException(new class() implements ExceptionHandler {
    public function run(Throwable $t) {
      error_log($t->getMessage());
      return true;
    }
  });
$handler->register();

(FooError::UnknownFoo)(["foo" => "foobedobedoo"]);
// in your error log:
// i don't know who, you think is foo, but it's not foobedobedoo

errors as values

Having errors available to your application as normal values also makes not throwing exceptions a viable solution. The Result pattern, for example, is a functional programming approach to error handling that treats error conditions as normal, expected return values. This can encourage you to consider how to handle error cases more carefully and closer to their source, as well as being a benefit to static analysis and comprehensibility in general. See Larry Garfield's excellent article for more.

<?php

use at\exceptable\ {
  Error,
  IsError
};

enum FooError : int implements Error {
  use IsError;

  case TheyToldMeToDoIt = 1;
  public const MESSAGES = [
    self::TheyToldMeToDoIt->name => "ooh noooooooooooooooooo!"
  ];
}

function foo(bool $fail) : string|FooError {
  return $fail ?
    FooError::TheyToldMeToDoIt :
    "woooooooooooooooooo hoo!";
}

$bool = maybeTrueMaybeFalse();
$result = foo($bool);
if ($result instanceof FooError) {
  echo $result->message();
  // outputs "ooh noooooooooooooooooo!"

  $bool = ! $bool;
  $result = foo($bool);
}

echo $result;
// outputs "woooooooooooooooooo hoo!"

...and if you want to make everybody mad, you can still throw them.

throw $result(["yes" => "i know i'm horrible"]);

see more in the wiki.

Version 5.0

Version 5 requires PHP 8.2 or greater.

  • ICU messaging system overhauled and published to its own package! Check out php-enspired/peekaboo - using exceptable means you get it for free, so take advantage!
  • Introduces the Error interface for enums, making errors into first-class citizens and opening up the ability to handle errors as values. Adds an SplError enum for php's built-in exception types.
  • Reworks and improves functionality for Exceptables and the Handler. Error / Exception / Shutdown Handlers now have explicit interfaces, as do debug log entries.

Read the release notes.

Version 4.0

Version 4.0 requires PHP 7.4 or greater.

  • PHP 7.4 added typehints to some Throwable properties, which required changes to the IsExceptable trait. This means Exceptable can no longer support PHP 7.3 or earlier - though that's fine, right? You've already upgraded your application to 8+ anyway, right?
  • right?

docs

contributing or getting help

I'm on IRC at libera#php-enspired, or open an issue on github. Feedback is welcomed as well.