Search by

jooservices / exceptions

Shared exception contracts, context-aware bases, and secret redaction for the JOOservices ecosystem.

Maintainers

Package info

github.com/jooservices/exceptions

pkg:composer/jooservices/exceptions

Transparency log

Statistics

Installs: 2 990

Dependents: 3

Suggesters: 0

Stars: 0

Open Issues: 1

v4.0.0 2026-08-30 01:28 UTC

README

CI codecov Quality gate status OpenSSF Scorecard PHP Version Release License: MIT

The JOOservices Exceptions Library is a PHP 8.5+ foundational library providing shared exception contracts, context-aware base classes, and secret redaction for the JOOservices package ecosystem.

Warning

v4.0.0 is a complete ground-up rebuild and is not backward compatible with earlier package lines. It starts a fresh Git history and has no legacy shims or deprecation bridge.

Package name: jooservices/exceptions

Install

composer require jooservices/exceptions

Core Features

  • Ecosystem-wide catching: root marker JOOExceptionInterface for one catch clause across all packages.
  • SPL semantics: AbstractJOORuntimeException (operational) and AbstractJOOLogicException (programmer errors).
  • Structured context: immutable, redacted diagnostic context on AbstractContextAwareException, AbstractContextAwareLogicException, or via the HasExceptionContext trait.
  • Stable error metadata: errorCode() ({package}.{domain}.{reason}), logLevel() (PSR-3 vocabulary), toLogArray() (versioned log_schema).
  • Sensitive data redaction: DefaultContextRedactor + CompositeContextRedactor::withExtraKeys().
  • Framework decoupled: zero runtime dependencies.

Basic Usage

Catching ecosystem exceptions

// runnable
use JOOservices\Exceptions\Contracts\JOOExceptionInterface;

$caught = false;

try {
    throw new class('demo') extends \RuntimeException implements JOOExceptionInterface {};
} catch (JOOExceptionInterface $exception) {
    $caught = $exception instanceof \Throwable;
}

Declaring a package exception

use JOOservices\Exceptions\Base\AbstractJOORuntimeException;

abstract class ClientException extends AbstractJOORuntimeException {}

Context-aware exceptions

// runnable
use JOOservices\Exceptions\Base\AbstractContextAwareException;
use JOOservices\Exceptions\Support\ErrorCode;
use JOOservices\Exceptions\Support\ExceptionContext;
use JOOservices\Exceptions\Support\LogLevel;

final class HydrationException extends AbstractContextAwareException
{
    public static function forField(string $path, string $expectedType): self
    {
        return (new self("Hydration failed for field '{$path}'"))
            ->withContext(['path' => $path, 'expectedType' => $expectedType]);
    }

    public function errorCode(): string
    {
        return 'dto.hydration.failed';
    }

    public function logLevel(): string
    {
        return LogLevel::ERROR->value;
    }

    protected function copyWithContext(ExceptionContext $context): static
    {
        return new self($this->getMessage(), $this->getCode(), $this->getPrevious(), $context);
    }
}

$exception = HydrationException::forField('user.email', 'string');

// getContext() is always redacted before it reaches the logger
$context = $exception->getContext();
$logPayload = $exception->toLogArray();

Redaction bootstrap

// runnable
use JOOservices\Exceptions\Base\AbstractContextAwareException;
use JOOservices\Exceptions\Support\CompositeContextRedactor;

AbstractContextAwareException::setRedactor(
    CompositeContextRedactor::withExtraKeys(['national_id', 'ssn']),
);

AbstractContextAwareException::removeRedactor();

Never put secrets in exception messages. Put diagnostics in context and rely on redaction.

Documentation

Development

Everything runs under Docker (php:8.5-cli-bookworm). GitHub Actions uses GitHub-hosted ubuntu-latest with the same Compose image via tools/ci/docker-compose:

make build            # builds jooservices/exceptions:php85 (php:8.5-cli-bookworm + pcov)
make install          # composer install inside the container
make lint             # Pint (per preset) + PHPCS + PHPStan (level max) + PHPMD
make test             # PHPUnit, no coverage
make test-coverage    # PHPUnit with 100% statement coverage gate
make docs-verify      # README/docs code snippets must parse and run
make check            # lint + docs + tests
make ci               # the full local CI gate (lint + docs + coverage)

License

MIT — see LICENSE.