1. Go to this page and download the library: Download benrowe/stateflow 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/ */
benrowe / stateflow example snippets
use BenRowe\StateFlow\StateFlow;
use BenRowe\StateFlow\Configuration;
// Define your state
class Order implements State {
public function __construct(
private string $status,
private ?string $paymentId = null,
) {}
public function with(array $changes): State {
return new self(
status: $changes['status'] ?? $this->status,
paymentId: $changes['paymentId'] ?? $this->paymentId,
);
}
public function toArray(): array {
return ['status' => $this->status, 'paymentId' => $this->paymentId];
}
}
// Configure the workflow
$stateFlow = new StateFlow(
configProvider: fn($state, $delta) => new Configuration(
transitionGates: [new CanProcessGate()], // Must pass to proceed
actions: [
new ChargePaymentAction(), // Execute in order
new ReserveInventoryAction(), // Skip if guard fails
new SendConfirmationAction(),
],
),
eventDispatcher: new Logger(), // See everything that happens
lockProvider: new RedisLock($redis), // Prevent race conditions
);
// Execute transition with automatic locking
$order = new Order('pending');
$worker = $stateFlow->transition($order, new ArrayDelta(['status' => 'processing']));
$context = $worker->execute();
if ($context->isCompleted()) {
echo "Order processed!";
} elseif ($context->isPaused()) {
// Action paused (e.g., waiting for external API)
// Lock is HELD across pause
saveToDatabase($context->serialize());
// Resume hours later...
$resumedWorker = $stateFlow->fromContext($context);
$resumedWorker->execute();
}
// Just this
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();
// Not this
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published', 'author' => 'same', 'created' => 'same', ...]));
$context = $worker->execute();
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
// 1. Run gates first
$gateResult = $worker->runGates();
// 2. Then run actions if gates pass
if (!$gateResult->shouldStopTransition()) {
$context = $worker->runActions();
}
// Or let actions pause themselves for async operations
class ProcessVideoAction implements Action {
public function execute(ActionContext $context): ActionResult {
$job = dispatch(new VideoProcessingJob());
// Pause execution, lock is held
return ActionResult::pause(metadata: ['jobId' => $job->id]);
}
}
// Resume later when ready
$resumedWorker = $stateFlow->fromContext($pausedContext);
$resumedWorker->execute();
$lockProvider = new RedisLockProvider($redis, $config);
$stateFlow = new StateFlow(
configProvider: $configProvider,
lockProvider: $lockProvider,
);
// This transition will be automatically locked
$worker = $stateFlow->transition($state, new ArrayDelta(['status' => 'published']));
$context = $worker->execute();
class CanPublishGate implements Gate {
public function evaluate(GateContext $context): GateResult {
return $context->currentState->hasContent()
? GateResult::ALLOW
: GateResult::DENY;
}
}
class NotifyAction implements Action, Guardable {
public function gate(): Gate {
return new HasSubscribersGate();
}
public function execute(ActionContext $context): ActionResult {
// Only runs if HasSubscribersGate passes
}
}
// 1. Define state with your domain model
class OrderState implements State {
public function __construct(
private string $id,
private string $status,
private float $total,
private ?string $paymentId = null,
) {}
public function with(array $changes): State {
return new self(
id: $this->id,
status: $changes['status'] ?? $this->status,
total: $changes['total'] ?? $this->total,
paymentId: $changes['paymentId'] ?? $this->paymentId,
);
}
public function toArray(): array { /* ... */ }
}
// 2. Configure workflow based on transition type
$configProvider = function(State $state, Delta $delta): Configuration {
return match ($delta->get('status')) {
'processing' => new Configuration(
transitionGates: [new HasInventoryGate($inventory)],
actions: [
new ChargePaymentAction($paymentGateway),
new ReserveInventoryAction($inventory),
new SendEmailAction($mailer),
],
),
'shipped' => new Configuration(
transitionGates: [new HasPaymentGate()],
actions: [new CreateShipmentAction($shipping)],
),
default => new Configuration(),
};
};
// 3. Create state flow with observability and locking
$stateFlow = new StateFlow(
configProvider: $configProvider,
eventDispatcher: new MetricsDispatcher(),
lockProvider: new RedisLockProvider($redis),
lockKeyProvider: new class implements LockKeyProvider {
public function getLockKey(State $state, Delta $delta): string {
return "order:" . $state->toArray()['id'];
}
},
);
// 4. Execute with race protection
try {
$order = new OrderState('ORD-123', 'pending', 99.99);
$worker = $stateFlow->transition($order, new ArrayDelta(['status' => 'processing']));
$context = $worker->execute();
if ($context->isCompleted()) {
return response()->json(['status' => 'success']);
}
} catch (LockAcquisitionException $e) {
// Another request is processing this order
return response()->json(['error' => 'Order is being processed'], 409);
}
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.