Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Exception Laravel Package

hoa/exception

Hoa\Exception is a PHP library for richer exception handling. It treats exceptions as objects with formatted, parameterized messages, supports exception groups, chaining, and customizable rendering, making error reporting clearer and more structured in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

hoa/exception is an archived Hoa package that models exceptions and "exception-shaped values" with an API designed before PHP 7's Throwable. Its final release (2.17.08.30, 2017-08-30) moved the library to PHP 7 and dropped PHP 5, PHP 7.0, and HHVM, so treat PHP 7.1 as the effective floor. It is installed with Composer:

composer require hoa/exception

Because that was the last release and it predates PHP 7.4/8.x, pin the version and test it against your runtime before adopting it. In a legacy Hoa codebase it will already be present.

Look first at four classes. Since this release moved the code into a Source/ directory, the files live at vendor/hoa/exception/Source/:

  • Source/Exception.php — the throwable base class.
  • Source/Idle.php — an exception-shaped value meant to be collected, not thrown.
  • Source/Group.php — an exception that aggregates other exceptions.
  • Source/Throwable.php — the package's own interface, implemented by both Exception and Idle.

The first use case is a message template plus arguments. Instead of concatenating strings at throw time, pass the raw format and the values; call getFormattedMessage() when you log or display it.

use Hoa\Exception\Exception;

function openFile(string $path, string $mode)
{
    $handle = @fopen($path, $mode);
    if (false === $handle) {
        throw new Exception(
            'Cannot open %s with mode %s.',
            0,
            null,
            $path,
            $mode
        );
    }

    return $handle;
}

try {
    openFile('/etc/shadow', 'r');
} catch (Exception $e) {
    error_log($e->getFormattedMessage());
}

Extra constructor arguments after $message, $code, and $previous are used as format arguments. The message stays raw until formatted, which is useful for logging and translation pipelines.

Implementation Patterns

Collect first, throw later with Idle

Use Idle in validation or import code that should not abort on the first problem.

use Hoa\Exception\Idle;

$errors = [];
foreach ($rows as $i => $row) {
    if (false === filter_var($row['email'], FILTER_VALIDATE_EMAIL)) {
        $errors[] = new Idle(
            'Row %d has invalid email %s.',
            0,
            null,
            $i,
            $row['email']
        );
    }
}

Then convert the collection into something throwable at the boundary: a Group or a normal Exception.

Aggregate with Group

Group extends Exception and implements ArrayAccess, Countable, and IteratorAggregate, so it can be thrown as one exception and inspected as a list.

use Hoa\Exception\Exception;
use Hoa\Exception\Group;

$group = new Group('Validation failed.');
$group[] = new Exception('Field %s is required.', 0, null, 'email');
$group[] = new Exception('Field %s is too short.', 0, null, 'password');

throw $group;
try {
    $validator->validate($input);
} catch (Group $group) {
    foreach ($group as $error) {
        $messages[] = $error->getFormattedMessage();
    }
}

Catch Group before Exception when you need per-item handling. Otherwise the group is handled as one exception and the individual messages are lost.

Stable identifiers for logs and monitoring

Override getId() in domain exception subclasses to get a machine-readable key independent of the message text.

final class PaymentDeclined extends \Hoa\Exception\Exception
{
    public function getId()
    {
        return 'payment.declined';
    }
}

Log both the identifier and the formatted message:

catch (\Hoa\Exception\Exception $e) {
    $logger->warning($e->getId(), [
        'message' => $e->getFormattedMessage(),
        'raw'     => $e->getRawMessage(),
        'args'    => $e->getArguments(),
    ]);
}

Type-hint the package interface, not native Throwable

If a method accepts either a thrown exception or an idle value, use Hoa\Exception\Throwable:

use Hoa\Exception\Throwable;

function normalizeError(Throwable $e): array
{
    return [
        'id'      => $e->getId(),
        'message' => $e->getFormattedMessage(),
    ];
}

Do not type-hint native \Throwable if you need to accept Idle; it does not satisfy the PHP 7 \Throwable contract.

Integrate with existing logging

Most frameworks and PSR-3 loggers call getMessage() or stringify the exception. That can expose the raw format string. Add a small normalizer at the boundary:

function loggable(\Throwable $e): array
{
    if ($e instanceof \Hoa\Exception\Throwable) {
        return [
            'class'   => get_class($e),
            'id'      => $e->getId(),
            'message' => $e->getFormattedMessage(),
        ];
    }

    return [
        'class'   => get_class($e),
        'message' => $e->getMessage(),
    ];
}

This keeps formatted messages in logs without changing every throw site.

Gotchas and Tips

  • Archived and unmaintained. The last release was 2017-08-30, before PHP 7.4 and 8.x. Installation may work, but runtime compatibility with modern PHP is not guaranteed. Pin the version, run your test suite, and prefer native exceptions for new code. Use this package mainly where a legacy Hoa codebase already depends on it.
  • PHP 7.1 floor. This release dropped PHP 5, PHP 7.0, and HHVM, so Composer will refuse to install it on anything older than PHP 7.1. That is a hard breaking change if you are upgrading from a pre-2017 pin on an old runtime — check the platform constraint before bumping, and note that dropping old runtimes is not the same as adding PHP 8 support.
  • Code now lives in Source/. If you copy files out of vendor/hoa/exception, vendor a fork, or maintain a classmap/exclude rule, update the paths (vendor/hoa/exception/Source/Exception.php, etc.). Composer autoloading is unaffected, but hardcoded paths in scripts and CI config break silently.
  • Pre-Throwable design, despite the PHP 7 port. The final release modernised the runtime requirement, but the type model is unchanged: Idle still does not implement native \Throwable. Do not assume the PHP 7 migration made the package idiomatic for PHP 7 error handling.
  • hoa/consistency is pinned via this release. The update to hoa/consistency shipped here constrains transitive resolution; on a modern app it can drag other Hoa packages to old versions. Isolate this dependency or avoid it in new services.
  • getMessage() may not be formatted. The raw format string can leak into logs, HTTP responses, and framework error pages. Always call getFormattedMessage() for display or logging, or normalize exceptions centrally.
  • Format arguments are lazy. A bad placeholder or missing argument surfaces when getFormattedMessage() is called, often inside a catch block. On PHP 8, sprintf-style mismatches can throw ArgumentCountError or ValueError; that secondary exception can mask the original. Validate argument counts and escape literal percent signs as %%.
  • Never use user input as the format string. Pass user input as an argument. Otherwise %s, %n, or stray % sequences in the input change the message or trigger warnings.
  • Idle is for collection, not throwing. It exists so you can build exception-like values without interrupting control flow. Convert it to a real Exception or Group at the boundary rather than relying on it propagating.
  • Group is still an Exception. A broad catch (Exception $e) catches the group as one item. If you need all underlying errors, catch Group explicitly and iterate.
  • Hoa Throwable is not native \Throwable. Type hints against \Throwable will not accept Idle. Use Hoa\Exception\Throwable where both are expected, and native \Throwable where only real throwables are expected.
  • Serialization can break. Exception arguments may contain objects, resources, or closures. If exceptions cross queue or job boundaries, keep arguments scalar or convert to a serializable DTO before dispatch.
  • Translations need the raw form. Format late. If you format the message at throw time, you lose the ability to translate the template or re-render it with different arguments. Keep getRawMessage() and getArguments() intact.
  • Catch order and previous chaining. getPrevious() can return native exceptions as well as Hoa ones. When walking the chain, check instanceof Hoa\Exception\Throwable before calling getFormattedMessage().
  • Extension point: subclass Hoa\Exception\Exception per domain and override getId(). Avoid putting logic in the constructor; keep message templates static so monitoring can group by identifier.
  • Thin ecosystem. With no active dependents and an archived repository, docs and examples may be stale — this release also rewrote the PHP documentation. Read the source of Source/Exception.php, Source/Idle.php, and Source/Group.php first; the API surface is small.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons