PHP code example of tiny-blocks / http-error-handler

1. Go to this page and download the library: Download tiny-blocks/http-error-handler library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

tiny-blocks / http-error-handler example snippets




declare(strict_types=1);

use DomainException;
use InvalidArgumentException;
use RuntimeException;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;
use TinyBlocks\Http\ErrorHandler\ExceptionMappingTable;

final readonly class ApplicationExceptionMapping implements ExceptionMapping
{
    public function mappings(): ExceptionMappingTable
    {
        return ExceptionMappingTable::create()
            ->when(exceptionClass: InvalidArgumentException::class)
            ->mapsTo(code: 'INVALID_INPUT', status: 400, message: 'The request payload is invalid.')
            ->whenAny(exceptionClasses: [DomainException::class, RuntimeException::class])
            ->mapsTo(code: 'BUSINESS_FAILURE', status: 422, message: 'The operation could not be completed.')
            ->whenSubclassOf(baseException: RuntimeException::class)
            ->mapsTo(code: 'RUNTIME_FAMILY', status: 500, message: 'A runtime error occurred.');
    }
}



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;

# Build the middleware with the declared mapping.
$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: new ApplicationExceptionMapping())
    ->build();



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;

# The mappings are supplied by the consumer.
$write = new WriteExceptionMapping();
$read = new ReadExceptionMapping();

# Compose both mappings under one middleware.
$middleware = ErrorMiddleware::create()
    ->withMappings($write, $read)
    ->build();



declare(strict_types=1);

use RuntimeException;
use Throwable;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;
use TinyBlocks\Http\ErrorHandler\ExceptionMappingTable;
use TinyBlocks\Http\ErrorHandler\MappedError;

final readonly class GatewayExceptionMapping implements ExceptionMapping
{
    public function mappings(): ExceptionMappingTable
    {
        return ExceptionMappingTable::create()
            ->when(exceptionClass: RuntimeException::class)
            ->resolvesWith(
                resolver: fn(Throwable $exception): MappedError => new MappedError(
                    code: 'GATEWAY_UNAVAILABLE',
                    status: 502,
                    message: $exception->getMessage()
                )
            );
    }
}



declare(strict_types=1);

use Slim\Exception\HttpNotFoundException;
use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;

# The mapping is supplied by the consumer and declares nothing about routing.
$mapping = /** @var ExceptionMapping */ null;

$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: $mapping)
    ->build();

# A request for a path with no route reaches the middleware as HttpNotFoundException, whose code is 404.
# The response is 404 {"code":"NOT_FOUND","message":"Not Found."} instead of a 500.



declare(strict_types=1);

use Psr\Http\Message\ServerRequestInterface;
use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\ErrorPayload;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;

# The mapping is supplied by the consumer.
$mapping = /** @var ExceptionMapping */ null;

# Answer in the reader's language, falling back to what the mapping declared.
$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: $mapping)
    ->withMessage(resolver: static function (ErrorPayload $payload, ServerRequestInterface $request): string {
        $curated = Messages::curatedFor(code: $payload->code);

        return is_null($curated) ? $payload->message : $curated;
    })
    ->build();



declare(strict_types=1);

use Psr\Log\LoggerInterface;
use TinyBlocks\Http\ErrorHandler\ErrorHandlingSettings;
use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;

# The logger and the mapping are supplied by the consumer.
$logger = /** @var LoggerInterface */ null;
$mapping = /** @var ExceptionMapping */ null;

# Enable error logging with full details, but keep stack traces out of the response.
$middleware = ErrorMiddleware::create()
    ->withLogger(logger: $logger)
    ->withMapping(mapping: $mapping)
    ->withSettings(settings: ErrorHandlingSettings::from(
        logErrors: true,
        logErrorDetails: true,
        displayErrorDetails: false
    ))
    ->build();



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;

# The mapping is supplied by the consumer.
$mapping = /** @var ExceptionMapping */ null;

# Disable the fallback so that unmapped exceptions rethrow.
$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: $mapping)
    ->withFallbackOnUnmapped(fallbackOnUnmapped: false)
    ->build();



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\ErrorReporter;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;
use TinyBlocks\Http\ErrorHandler\ReportedError;

# The mapping and the provider client are supplied by the consumer.
$mapping = /** @var ExceptionMapping */ null;
$client = /** @var ErrorTrackingClient */ null;

# One reporter adapts one provider. This one forwards only what no rule described.
final readonly class ErrorTrackingReporter implements ErrorReporter
{
    public function __construct(private ErrorTrackingClient $client)
    {
    }

    public function report(ReportedError $error): void
    {
        if ($error->wasMapped()) {
            return;
        }

        $this->client->capture(
            exception: $error->exception,
            correlationId: $error->correlationId?->toString()
        );
    }
}

# Register as many reporters as there are providers to notify, one withReporter call per provider.
$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: $mapping)
    ->withReporter(reporter: new ErrorTrackingReporter(client: $client))
    ->build();



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\ReportingContext;

final class UpstreamUnavailable extends RuntimeException implements ReportingContext
{
    public function __construct(string $message, private readonly string $service)
    {
        parent::__construct(message: $message);
    }

    public function reportingTags(): array
    {
        return ['service' => $this->service];
    }
}



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\PrioritizedError;
use TinyBlocks\Http\ErrorHandler\ReportingPriority;

final class PaymentTimedOut extends RuntimeException implements PrioritizedError
{
    public function reportingPriority(): ReportingPriority
    {
        return ReportingPriority::CRITICAL;
    }
}



declare(strict_types=1);

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TinyBlocks\Http\ErrorHandler\ReportingTags;

final readonly class RequireMembership implements MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $organizationId = $request->getHeaderLine('Organization-Id');

        ReportingTags::from(request: $request)?->add(key: 'organization_id', value: $organizationId);

        return $handler->handle($request);
    }
}



declare(strict_types=1);

use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\ExceptionMapping;
use TinyBlocks\Http\ErrorHandler\Reporters\SentryReporter;

# The mapping and the deployment values are supplied by the consumer.
$mapping = /** @var ExceptionMapping */ null;
$dsn = /** @var string */ '';
$release = /** @var string */ '';
$environment = /** @var string */ '';

$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: $mapping)
    ->withReporter(
        reporter: SentryReporter::initializedWith(
            dsn: $dsn,
            release: $release,
            environment: $environment
        )
    )
    ->build();

$middleware = ErrorMiddleware::create()
    ->withMapping(mapping: $mapping)
    ->withReporter(reporter: SentryReporter::from(hub: SentrySdk::getCurrentHub()))
    ->build();

SentryReporter::from(hub: SentrySdk::getCurrentHub(), filter: ReportingFilter::EVERY_ERROR);



declare(strict_types=1);

use Psr\Http\Message\ServerRequestInterface;
use Slim\Routing\RouteContext;
use TinyBlocks\Http\ErrorHandler\ErrorMiddleware;
use TinyBlocks\Http\ErrorHandler\RouteMiddleware;

# The middleware and the application are supplied by the consumer.
$middleware = /** @var ErrorMiddleware */ null;
$application = /** @var \Slim\App */ null;

# Registered first, so it runs closest to the route.
$application->add(RouteMiddleware::resolvedBy(
    resolver: static fn(ServerRequestInterface $request): ?string
        => RouteContext::fromRequest($request)->getRoute()?->getPattern()
));

# The router above it, and the error middleware above the router, which is what keeps a 404 reaching it.
$application->addRoutingMiddleware();
$application->add($middleware);