PHP code example of sients / compensator

1. Go to this page and download the library: Download sients/compensator 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/ */

    

sients / compensator example snippets


use Compensator\Compensator;
use Compensator\CompensatorContext;

class ChargePayment implements \Compensator\Contracts\CompensatorStep
{
    public function execute(CompensatorContext $context): mixed
    {
        $payment = PaymentGateway::charge($context->get('amount'));
        $context->set('payment_id', $payment->id);

        return $payment;
    }

    public function compensate(CompensatorContext $context): void
    {
        PaymentGateway::refund($context->get('payment_id'));
    }
}

class CreatePartnerPolicy implements \Compensator\Contracts\CompensatorStep
{
    public function execute(CompensatorContext $context): mixed
    {
        $policy = PartnerApi::createPolicy($context->all());
        $context->set('policy_id', $policy->id);

        return $policy;
    }

    public function compensate(CompensatorContext $context): void
    {
        PartnerApi::cancelPolicy($context->get('policy_id'));
    }
}

$result = (new Compensator())
    ->addStep(new ChargePayment())
    ->addStep(new CreatePartnerPolicy())
    ->step(
        execute: fn ($ctx) => Pdf::generate($ctx->get('policy_id')),
        // compensate is optional — omit it for a step with nothing to undo
        name: 'generate_pdf',
    )
    ->run(new CompensatorContext(['amount' => 4999]));

if ($result->needsManualCleanup()) {
    // The chain failed AND rollback failed — a side effect is stranded.
    foreach ($result->compensationFailures as $failure) {
        logger()->critical('stranded side effect', [
            'step'     => $failure->stepName,   // 'ChargePayment'
            'attempts' => $failure->attempts,
            'error'    => $failure->exception->getMessage(),
            'context'  => $result->context->snapshot(redact: ['card_number']),
        ]);
    }
}

->addStep(new ChargePayment())              // 'ChargePayment'
->addStep(new ChargePayment(), 'retry_charge') // or override it

->step(execute: ..., compensate: ..., name: 'charge_payment')

$compensator = app(Compensator::class); // a new, empty chain every time

use Compensator\Enums\CompensationFailureStrategy;

(new Compensator())
    ->withFailureStrategy(CompensationFailureStrategy::StopOnFirstFailure)
    // ...

(new Compensator())
    ->retryCompensation(times: 2, sleepMs: 200)
    // ...

public function compensate(CompensatorContext $context): void
{
    $payment = PaymentGateway::find($context->get('payment_id'));

    if ($payment?->isRefundable()) {
        $payment->refund();
    }
}

$context->snapshot(redact: ['card_number']);
// ['amount' => 4999, 'payment_id' => 'pay_123', 'card_number' => '[redacted]',
//  'gateway_response' => 'object(App\Payments\Response)']

(new Compensator())
    ->compensateFailedStep()
    // ...

public function compensate(CompensatorContext $context): void
{
    if (! $context->has('payment_id')) {
        return;
    }

    PaymentGateway::refund($context->get('payment_id'));
}

// Don't do this.
DB::transaction(function () {
    (new Compensator())
        ->addStep(new ChargePayment())      // external HTTP call
        ->addStep(new CreatePartnerPolicy()) // external HTTP call
        ->run();
});

final class CreateOrder implements CompensatorStep
{
    public function execute(CompensatorContext $context): mixed
    {
        // Atomic within this step, committed before the next one starts.
        $order = DB::transaction(fn () => Order::create([...]));

        $context->set('order_id', $order->id);

        return $order;
    }

    public function compensate(CompensatorContext $context): void
    {
        DB::transaction(fn () => Order::whereKey($context->get('order_id'))->delete());
    }
}

use Illuminate\Support\Facades\Event;
use Compensator\Events\CompensationFailed;

Event::listen(function (CompensationFailed $e) {
    logger()->critical('compensation failed', ['step' => $e->stepName]);
});

(new Compensator())
    ->protectAgainstFatals()
    ->addStep(new ChargePayment())
    // ...