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.
Current release: v4.0.1.
Warning
v4.0.0 is a complete ground-up rebuild and is not backward compatible
with earlier package lines. Rewrite integrations before upgrading; there are
no legacy shims or deprecation bridges. See the changelog.
- Fresh exception contracts and context-aware base classes.
- Zero runtime dependencies and framework-agnostic implementation.
- Redacted diagnostic context remains separate from exception messages.
- PHP
^8.5 - Docker (recommended — local tooling uses
php:8.5-cli-bookworm)
composer require jooservices/exceptions- Ecosystem-wide catching: root marker
JOOExceptionInterfacefor one catch clause across all packages. - SPL semantics:
AbstractJOORuntimeException(operational) andAbstractJOOLogicException(programmer errors). - Structured context: immutable, redacted diagnostic context on
AbstractContextAwareException,AbstractContextAwareLogicException, or via theHasExceptionContexttrait. - Stable error metadata:
errorCode()({package}.{domain}.{reason}),logLevel()(PSR-3 vocabulary),toLogArray()(versionedlog_schema). - Sensitive data redaction:
DefaultContextRedactor+CompositeContextRedactor::withExtraKeys(). - Framework decoupled: zero runtime dependencies.
// runnable
use JOOservices\Exceptions\Contracts\JOOExceptionInterface;
$caught = false;
try {
throw new class('demo') extends \RuntimeException implements JOOExceptionInterface {};
} catch (JOOExceptionInterface $exception) {
$caught = $exception instanceof \Throwable;
}use JOOservices\Exceptions\Base\AbstractJOORuntimeException;
abstract class ClientException extends AbstractJOORuntimeException {}// 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();// 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.
withContext()returns a new exception; existing instances remain immutable.getContext()is always redacted; usegetRawContext()only for tests or internal handling.toLogArray()includes the previous exception class and code, never previous messages.
- Documentation Hub
- API reference
- Architecture
- Quick Start
- Laravel Integration
- Risks and Gaps
- Changelog
- Workflow guide
- Contributing
- Security policy
- Support
- Governance
- Code of Conduct
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)MIT — see LICENSE.