PHPackages                             dakujem/strata74 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. dakujem/strata74

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

dakujem/strata74
================

Backport of dakujem/strata (Contracts for layered exception handling mechanisms) for PHP 7.4

2.0.1(1mo ago)0199UnlicensePHPPHP 7.4.\* || 8.0.\* || 8.1.\* || 8.2.\*

Since Sep 1Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/dakujem/strata74)[ Packagist](https://packagist.org/packages/dakujem/strata74)[ RSS](/packages/dakujem-strata74/feed)WikiDiscussions trunk Synced 1mo ago

READMEChangelog (3)DependenciesVersions (5)Used By (0)

> ✋ Please note
>
> This is a backport of the original package [`dakujem/strata`](https://github.com/dakujem/strata), for **PHP 7.4 only**.
>
> Based on [`v1.0`](https://github.com/dakujem/strata/releases/tag/1.0) release of the PHP 8 version.
>
> After updating to PHP 8, just change the requirement to `dakujem/strata`.
> Chances are, you need not do anything else.
> If your classes directly implement strata interfaces, you will only need to update type hints of implemented methods (`pin`, `replaceContext`, `pass`, `explain`, `tag` and `convey`).
>
> The PHP 8 version has improved key handling (supports integer keys, aligned with PHP array key handling) and full test coverage.

Strata
======

[](#strata)

Contracts and implementations for layered exception handling mechanisms.

TL;DR
-----

[](#tldr)

Native PHP exceptions allow to carry a string message and an integer code.

👉 **That's not enough.** 👈

Embrace context-enabled exceptions.

Context-enabled exceptions
--------------------------

[](#context-enabled-exceptions)

- convey information to clients
- carry meta-data for developers

HTTP client error example:

```
use Dakujem\Strata\Http\UnprocessableContent;

if(!isEmail($input['email'])){
    // HTTP 422
    throw (new UnprocessableContent('Invalid e-mail address.'))
        // Convey messages to clients with localization support and metadata:
        ->convey(
            message: __('Please enter a valid e-mail address.'),
            source: 'input.email',
        )
        // Add details for developers to be reported or logged:
        ->pin([
            'name' => $input['name'],
            'email' => $input['email'],
        ], key: 'input');
}
```

Internal logic fault:

```
use Dakujem\Strata\LogicException;

throw (new LogicException('Invalid value of Something.'))
    // Convey messages and meta-data to clients, humans and apps alike:
    ->convey(
        message: __('We are sorry, we encountered an issue with Something and are unable to fulfil your request.'),
        source: 'something.or.other',
        description: __('We are already fixing the issue, please try again later.'),
        meta: ['param' => 42]
    )
    // Add details for developers to be reported or logged:
    ->explain('Fellow developers, this issue is caused by an invalid value: ' . $someValue)
    ->pin(['value' => $someValue, 'severity' => 'serious'], 'additional context')
    ->pin(['other' => $value], 'other context')
    ->pin('anything')
    ->tag('something')
    ->tag(tag: 'serious', key: 'severity')
;
```

The meta-data can be used by server-side error handlers to report detailed data.

The client-facing data can be used by client apps and displayed to end users or processed programmatically.
This is especially useful for API+client app architecture (JS widgets, PWAs, mobile apps, etc.).

Processing context using error handlers
---------------------------------------

[](#processing-context-using-error-handlers)

Typically, your apps will have a global error handling mechanism in place, be it a try-catch block in your bootstrap, error handling middleware or a native error handler.

Something along these lines:

```
try {
    process_request(Request::fromGlobals());
} catch (Throwable $e) {
    handle_exception($e);
}
```

In Laravel, for example, it's usually the [`App\Exceptions\Handler` class that handles the exceptions](https://laravel.com/docs/10.x/errors#the-exception-handler).
In Slim, [middleware is used for error handling](http://dev.slimframework.com/docs/v4/middleware/error-handling.html).
[Symfony will also catch all exceptions and errors](https://symfony.com/doc/current/controller/error_pages.html), and it is possible to customize the handling logic.

All these rely on developers to come up with specific exceptions that carry specific context, like Guzzle's [`RequestException`](https://github.com/guzzle/guzzle/blob/7.8/src/Exception/RequestException.php) carries HTTP request and response.
And that's fine. By all means, do create specific exceptions for specific purposes.
Strata will help enable context support by implementing a single interface (`SupportsContextStrata`) and using a single trait (`ContextStrata`).

There's also times when one does not need specific exceptions, but still wishes to pass contextual data to global error handlers.
Strata provide exceptions for common HTTP responses.

To add context for reporting or logging or to add information targeted at front-end consumers, strata provide both the interfaces and the implementations.

### Examples

[](#examples)

Example of an exception handler in Laravel (JSON API):

```
namespace App\Exceptions;

use Dakujem\Strata\Contracts\IndicatesAuthenticationFault;
use Dakujem\Strata\Contracts\IndicatesAuthorizationFault;
use Dakujem\Strata\Contracts\IndicatesClientFault;
use Dakujem\Strata\Contracts\IndicatesConflict;
use Dakujem\Strata\Contracts\IndicatesInvalidInput;
use Dakujem\Strata\Support\ErrorContainer;
use Dakujem\Strata\Support\SuggestsHttpStatus;
use Dakujem\Strata\Support\SupportsPublicContext;

class Handler extends ExceptionHandler
{
    protected function prepareJsonResponse($request, Throwable $e)
    {
        $errors = $e instanceof SupportsPublicContext ? $e->publicContext() : null;
        $errors ??= [];

        if ($errors === []) {
            $message = $detail = null;
            // Public error message for clients:
            if ($e instanceof SuggestsErrorMessage) {
                $message = $e->suggestErrorMessage();
            }
            // Laravel/Symfony HTTP exception
            if ($e instanceof HttpExceptionInterface) {
                $message = $e->getMessage();
            }
            // Slim HTTP exception
            if ($e instanceof HttpException) {
                $message = $e->getTitle();
                $detail = $e->getDescription();
            }
            $errors[] = new ErrorContainer(
                message: $message,
                detail: $detail,
            );
        }

        // Status code
        $code = 500;
        if ($e instanceof SuggestsHttpStatus) {
            $code = $e->suggestStatusCode();
        } elseif ($e instanceof IndicatesInvalidInput) {
            $code = 422; // 422 Unprocessable Content
        } elseif ($e instanceof IndicatesConflict) {
            $code = 409; // 409 Conflict
        } elseif ($e instanceof IndicatesAuthorizationFault) {
            $code = 403; // 403 Forbidden
        } elseif ($e instanceof IndicatesAuthenticationFault) {
            $code = 401; // 401 Unauthorized
        } elseif ($e instanceof IndicatesClientFault) {
            $code = 400; // 400 Bad Request
        } elseif ($e instanceof HttpExceptionInterface) {
            // Laravel/Symfony HTTP exceptions
            $code = $e->getStatusCode();
        } elseif ($e instanceof ValidationException) {
            $code = $e->status; // 422 Unprocessable Content (default)
        } elseif ($e instanceof HttpException) {
            // Slim HTTP exception
            $code = $e->getCode();
        }

        return response()
            ->json(
                data: [
                    'errors' => $errors,
                ],
            )
            ->withStatus(
                $code,
            );
    }
}
```

Example of processing the internal context for improved [Sentry](https://sentry.io/) reports:

```
use Dakujem\Strata\Support\SupportsInternalContext;
use Dakujem\Strata\Support\SupportsInternalExplanation;
use Dakujem\Strata\Support\SupportsTagging;
use Sentry\Event;
use Sentry\EventHint;
use Sentry\State\HubInterface;
use Sentry\State\Scope;

function reportException(Throwable $e)
{
    $hub = Container::get(HubInterface::class);
    $hub->configureScope(function (Scope $scope) use ($e): void {

        // Internal context comprises all the pinned data (see `pin` method usage above)
        if ($e instanceof SupportsInternalContext) {
            foreach ($e->context() as $key => $value) {
                if (is_string($value) || is_numeric($value) || $value instanceof Stringable) {
                    $value = [
                        'value' => (string)$value,
                    ];
                }
                if (is_object($value)) {
                    $value = (array)$value;
                }
                if (is_array($value)) {
                    $scope->setContext(
                        is_numeric($key) ? 'context-' . $key : $key,
                        $value,
                    );
                }
            }
        }

        if ($e instanceof SupportsTagging) {
            foreach ($e->tags() as $key => $value) {
                // When tags have numeric keys, use tag:true format, otherwise use key:tag format.
                $scope->setTag(
                    is_numeric($key) ? $value : $key,
                    is_numeric($key) ? 'true' : $value,
                );
            }
        }

        if ($e instanceof SupportsInternalExplanation) {
            $scope->setContext(
                '_dev_',
                [
                    'explanation' => $e->explanation(),
                ],
            );
        }
    });

    $event = Event::createEvent();
    $event->setMessage($e->getMessage());
    $hint = new EventHint();
    $hint->exception = $e;

    $hub->captureEvent($event, $hint);
}
```

Notable contracts for API design
--------------------------------

[](#notable-contracts-for-api-design)

Contracts for automatic error handling, especially useful for HTTP APIs:

- `IndicatesClientFault` 4xx
- `IndicatesServerFault` 5xx

```
if($exception instanceof IndicatesClientFault){
    return convert_client_exception_to_4xx_response($exception);
}
if($exception instanceof IndicatesServerFault){
    report_server_fault($exception);
    return apologize_for_server_issue_with_5xx_status($exception);
}
```

Common HTTP 4xx exceptions
--------------------------

[](#common-http-4xx-exceptions)

This package provides exceptions for common 4xx HTTP status responses:

- 400 `BadRequest`
- 404 `NotFound`
- 403 `Forbidden`
- 401 `Unauthorized`
- 409 `Conflict`
- 422 `UnprocessableContent`

See the [HTTP status reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) for more information.

Build your own
--------------

[](#build-your-own)

The strata contracts and traits allow and encourage developers to come up with their own exceptions for specific use-cases easily.

```
class MySpecificException extends WhateverBaseException implements SupportsContextStrata
{
    use ContextStrata;

    public function __construct($message = null, $code = 0, ?Throwable $previous = null)
    {
        parent::__construct(
            $message ?? 'This is the default message for my specific exception.',
            $code ?? 0,
            $previous,
        );
    }
}
```

If only selected mechanisms are needed, use the table below to pick specific interfaces and traits:

MethodMechanismImplement interfaceUse trait`pin`generic internal metadata "pinning"`SupportsInternalContext``SupportInternalContext``explain`human-readable details for developers`SupportsInternalExmplanation``SupportInternalExmplanation``tag`machine-processable tags`SupportsTagging``SupportTagging``pass`public data for clients`SupportsPublicContext``SupportPublicContext``convey`coherent error details for clients`SupportsPublicConveying``SupportPublicConveying``pin`, `tag`, `explain`internal metadata only`SupportsInternals``SupportInternals`all of themall of them`SupportsContextStrata``ContextStrata`To provide throwables with HTTP capabilities, implement the following trivial interfaces:

- `SuggestsErrorMessage` to suggest error messages to your error handlers
- `SuggestsHttpStatus` to suggest HTTP status codes to your error handlers

###  Health Score

45

—

FairBetter than 92% of packages

Maintenance91

Actively maintained with recent releases

Popularity13

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity59

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~312 days

Total

4

Last Release

44d ago

Major Versions

1.1 → 2.02023-11-05

PHP version history (2 changes)1.0PHP 7.4.\*

2.0.1PHP 7.4.\* || 8.0.\* || 8.1.\* || 8.2.\*

### Community

Maintainers

![](https://www.gravatar.com/avatar/0bd7fa945013e9c0dcd65693575276bf5fcb9b9de13e1123e9f2c4a0a4c0fb6b?d=identicon)[dakujem](/maintainers/dakujem)

---

Top Contributors

[![dakujem](https://avatars.githubusercontent.com/u/443067?v=4)](https://github.com/dakujem "dakujem (12 commits)")

### Embed Badge

![Health badge](/badges/dakujem-strata74/health.svg)

```
[![Health](https://phpackages.com/badges/dakujem-strata74/health.svg)](https://phpackages.com/packages/dakujem-strata74)
```

###  Alternatives

[franzl/studio

Develop your Composer libraries with style

1.1k634.5k15](/packages/franzl-studio)[ezsystems/ez-support-tools

Providing information about the system eZ Platform/Enterprise/Commerce is running on, and eZ install itself

44979.6k18](/packages/ezsystems-ez-support-tools)[sidroberts/phalcon-cron

Cron component for Phalcon.

79149.1k](/packages/sidroberts-phalcon-cron)[data-values/number

Numerical value objects, parsers and formatters

18303.0k14](/packages/data-values-number)[attestto/solana-php-sdk

Solana PHP SDK for interacting with the Solana blockchain

5528.1k](/packages/attestto-solana-php-sdk)[sitegeist/taxonomy

Manage vocabularies and taxonomies as separate node-hierarchy.

1589.7k1](/packages/sitegeist-taxonomy)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
