Safety Features

Circuit breakers, graceful execution, and error handling

Laravel Extensions provides multiple safety mechanisms to ensure your application remains stable even when handlers fail.

Circuit Breaker

The Circuit Breaker pattern prevents cascading failures by stopping calls to failing handlers.

How It Works

CLOSED (normal) → failures exceed threshold → OPEN (blocking)
                                                    ↓
                                              timeout expires
                                                    ↓
                                             HALF_OPEN (testing)
                                                    ↓
                    success → CLOSED          failure → OPEN

Configuration

// config/extensions.php
'circuit_breaker' => [
    'enabled' => true,
    'threshold' => 5,        // Failures before opening
    'timeout' => 60,         // Seconds before trying again
    'store' => 'cache',      // 'cache' or 'array'
],

Usage

Circuit breakers are automatic when enabled. You can also use them manually:

use Esegments\LaravelExtensions\CircuitBreaker\CircuitBreaker;

$breaker = new CircuitBreaker('external-api');

$result = $breaker->call(function () {
    return Http::get('https://api.example.com/data');
});

// Check state
$breaker->isOpen();      // true if blocking calls
$breaker->isClosed();    // true if allowing calls
$breaker->isHalfOpen();  // true if testing recovery

Circuit States

use Esegments\LaravelExtensions\CircuitBreaker\CircuitState;

CircuitState::Closed;   // Normal operation
CircuitState::Open;     // Blocking calls
CircuitState::HalfOpen; // Testing recovery

Graceful Execution

The GracefulExecution trait allows handlers to fail without crashing:

use Esegments\LaravelExtensions\Concerns\GracefulExecution;

class MyHandler
{
    use GracefulExecution;

    public function handle($data)
    {
        return $this->gracefully(function () use ($data) {
            // Risky operation
            return $this->processData($data);
        }, default: null);
    }
}

With Logging

$result = $this->gracefully(
    fn() => $this->riskyOperation(),
    default: [],
    log: true,
    level: 'warning'
);

With Callback on Failure

$result = $this->gracefully(
    fn() => $this->riskyOperation(),
    onError: function (\Throwable $e) {
        Report::exception($e);
        return ['error' => $e->getMessage()];
    }
);

Mutable Trait

The Mutable trait allows temporarily muting exceptions:

use Esegments\LaravelExtensions\Concerns\Mutable;

class DataImporter
{
    use Mutable;

    public function import(array $rows)
    {
        $results = [];

        foreach ($rows as $row) {
            // Mute exceptions for individual rows
            $result = $this->muted(fn() => $this->processRow($row));
            if ($result !== null) {
                $results[] = $result;
            }
        }

        return $results;
    }
}

Global Mute

$importer->mute(); // Enable muting

$importer->import($data); // No exceptions thrown

$importer->unmute(); // Disable muting

Silenceable Trait

The Silenceable trait suppresses output:

use Esegments\LaravelExtensions\Concerns\Silenceable;

class VerboseProcessor
{
    use Silenceable;

    public function process()
    {
        return $this->silenced(function () {
            // Any echo/print statements are captured
            echo "Processing...";
            return $this->doWork();
        });
    }
}

Dispatch Result

All dispatches return a DispatchResult object:

$result = Extensions::dispatch('some.event', $data);

// Check status
$result->successful();  // All handlers succeeded
$result->failed();      // Any handler failed
$result->partial();     // Some succeeded, some failed

// Get results
$result->all();         // All results
$result->successes();   // Only successful results
$result->failures();    // Only failures

// Get first result
$result->first();       // First result (any)
$result->firstSuccess(); // First successful
$result->firstFailure(); // First failure

// Count
$result->count();
$result->successCount();
$result->failureCount();

Error Handling Strategies

Fail Fast

Stop on first error (default):

Extensions::dispatch('critical.operation', $data)
    ->failFast();

Collect All

Continue despite errors:

Extensions::dispatch('batch.process', $items)
    ->collectAll()
    ->failures(); // Get all errors

With Fallback

$result = Extensions::dispatch('get.data', $key)
    ->withFallback(fn() => Cache::get($key))
    ->first();

Combining Safety Features

use Esegments\LaravelExtensions\Concerns\GracefulExecution;
use Esegments\LaravelExtensions\Concerns\Mutable;

class RobustHandler
{
    use GracefulExecution, Mutable;

    public function handle($data)
    {
        // Mute for non-critical operations
        $this->muted(fn() => $this->logActivity($data));

        // Graceful for main operation with fallback
        return $this->gracefully(
            fn() => $this->processData($data),
            default: $this->getCachedResult($data)
        );
    }
}