PHP code example of solution-forest / workflow-engine-core
1. Go to this page and download the library: Download solution-forest/workflow-engine-core 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/ */
solution-forest / workflow-engine-core example snippets
use SolutionForest\WorkflowEngine\Core\WorkflowBuilder;
use SolutionForest\WorkflowEngine\Core\WorkflowEngine;
use SolutionForest\WorkflowEngine\Core\WorkflowContext;
use SolutionForest\WorkflowEngine\Core\ActionResult;
use SolutionForest\WorkflowEngine\Actions\BaseAction;
// Define custom actions
class ValidateOrderAction extends BaseAction
{
public function execute(WorkflowContext $context): ActionResult
{
$orderId = $context->getData('order_id');
// Your validation logic here
if ($this->isValidOrder($orderId)) {
return ActionResult::success(['validated' => true]);
}
return ActionResult::failure('Invalid order');
}
}
// Build a workflow definition
$definition = WorkflowBuilder::create('order-processing')
->description('Process customer orders')
->addStep('validate', ValidateOrderAction::class)
->addStep('payment', ProcessPaymentAction::class, timeout: 300, retryAttempts: 3)
->addStep('fulfillment', FulfillOrderAction::class)
->build();
// Create engine with a storage adapter (InMemoryStorage ships with the package)
use SolutionForest\WorkflowEngine\Storage\InMemoryStorage;
$engine = new WorkflowEngine(new InMemoryStorage(), $eventDispatcher);
// Start and run the workflow
$instanceId = $engine->start(
'order-processing',
$definition->toArray(),
['order_id' => 123, 'customer_id' => 456]
);
// Check the result
$instance = $engine->getInstance($instanceId);
echo $instance->getState()->value; // "completed"
use SolutionForest\WorkflowEngine\Core\WorkflowBuilder;
$workflow = WorkflowBuilder::create('order-flow')
->description('Process customer orders')
->addStep('validate', ValidateOrderAction::class)
->when('order.total > 1000', function ($builder) {
$builder->addStep('fraud_check', FraudCheckAction::class);
})
->addStep('payment', ProcessPaymentAction::class, timeout: 300, retryAttempts: 3)
// fakeEmail() records an email but does NOT send one - this package ships
// no mail transport. Use your own action for real delivery.
->fakeEmail('order-confirmation', '[email protected]', 'Order Confirmed')
->build();
// Quick templates for common patterns
$workflow = WorkflowBuilder::quick()->userOnboarding();
$workflow = WorkflowBuilder::quick()->orderProcessing();
$workflow = WorkflowBuilder::quick()->documentApproval();
use SolutionForest\WorkflowEngine\Attributes\Retry;
#[Retry(attempts: 3, backoff: 'exponential', delay: 1000)]
class ReliableApiAction extends BaseAction
{
public function execute(WorkflowContext $context): ActionResult
{
// Metadata only - the engine does not read this attribute yet.
// For real retries use: ->addStep('id', Action::class, retryAttempts: 3)
return ActionResult::success();
}
}
use SolutionForest\WorkflowEngine\Attributes\Timeout;
#[Timeout(seconds: 30)]
class TimedAction extends BaseAction
{
public function execute(WorkflowContext $context): ActionResult
{
// Metadata only - the engine does not read this attribute yet.
// For a real timeout use: ->addStep('id', Action::class, timeout: 30)
return ActionResult::success();
}
}
use SolutionForest\WorkflowEngine\Attributes\Condition;
#[Condition('order.amount > 100')]
class PremiumProcessingAction extends BaseAction
{
public function execute(WorkflowContext $context): ActionResult
{
// Metadata only - the engine does not read this attribute yet.
// For real gating use: ->when('order.amount > 100', fn ($b) => ...)
return ActionResult::success();
}
}
use SolutionForest\WorkflowEngine\Attributes\WorkflowStep;
#[WorkflowStep(id: 'send_email', name: 'Send Welcome Email', description: 'Sends a welcome email to the new user')]
class SendWelcomeEmailAction extends BaseAction
{
public function execute(WorkflowContext $context): ActionResult
{
return ActionResult::success();
}
}
// Steps with retry and timeout configured via builder
$workflow = WorkflowBuilder::create('reliable-flow')
->addStep('fetch_data', FetchDataAction::class, timeout: 30, retryAttempts: 3)
->addStep('process', ProcessAction::class, timeout: 60)
->build();
// Conditional steps evaluated at runtime
$workflow = WorkflowBuilder::create('conditional-flow')
->addStep('validate', ValidateAction::class)
->when('order.total > 1000', function ($builder) {
$builder->addStep('fraud_check', FraudCheckAction::class);
})
->addStep('complete', CompleteAction::class)
->build();
// Comparisons: === !== == != > < >= <=
'order.total > 1000'
'user.plan === "premium"'
'status != cancelled' // bare words are treated as strings
// Truthy checks and negation
'user.active'
'!user.suspended'
// Boolean operators, with && binding tighter than ||
'order.total > 1000 && user.vip === true'
'user.vip || order.total > 5000'
// Parentheses to override precedence
'(user.vip || order.total > 5000) && !order.refunded'
// Start, resume, cancel
$instanceId = $engine->start('my-workflow', $definition->toArray(), ['key' => 'value']);
$instance = $engine->getInstance($instanceId);
$engine->resume($instanceId);
$engine->cancel($instanceId, 'No longer needed');
// Instance IDs are caller-supplied and must be unique: start() throws
// InvalidWorkflowStateException rather than overwriting an existing instance.
// Recovering a failed workflow: fix the cause, then resume to retry the
// step that failed. resume() is rejected for COMPLETED and CANCELLED.
try {
$engine->resume($instanceId);
} catch (StepExecutionException $e) {
// Still failing - the exception carries the real cause, not a
// state-machine error from the failure handler.
}
// Track progress
$progress = $instance->getProgress(); // 0.0 to 100.0
$summary = $instance->getStatusSummary();
// Query instances with filters
$instances = $engine->getInstances([
'state' => 'running',
'definition_name' => 'order-processing',
'created_after' => new \DateTime('-7 days'),
'limit' => 50,
'offset' => 0,
]);
use SolutionForest\WorkflowEngine\Support\SimpleWorkflow;
$simple = new SimpleWorkflow($storageAdapter);
// Run actions sequentially
$instanceId = $simple->sequential('user-onboarding', [
SendWelcomeEmailAction::class,
CreateUserProfileAction::class,
AssignDefaultRoleAction::class,
], ['user_id' => 123]);
// Run a single action as a workflow
$instanceId = $simple->runAction(SendEmailAction::class, [
'to' => '[email protected]',
'subject' => 'Welcome!',
]);
// Execute from a builder
$builder = WorkflowBuilder::create('custom-flow')
->addStep('validate', ValidateAction::class)
->addStep('process', ProcessAction::class);
$instanceId = $simple->executeBuilder($builder, $context);
// Check status
$status = $simple->getStatus($instanceId);
// Returns: id, state, current_step, progress, completed_steps, failed_steps, error_message, ...
$state = $instance->getState();
$state->isActive(); // true for PENDING, RUNNING, WAITING, PAUSED
$state->isFinished(); // true for COMPLETED, FAILED, CANCELLED
$state->isSuccessful(); // true for COMPLETED
$state->isError(); // true for FAILED
$state->label(); // "Running"
$state->description(); // "The workflow is actively executing steps..."
$state->color(); // "blue" (gray, blue, yellow, orange, green, red, purple)
$state->icon(); // "▶️"
$state->canTransitionTo(WorkflowState::COMPLETED); // bool
$state->getValidTransitions(); // [WorkflowState::WAITING, ...]
use SolutionForest\WorkflowEngine\Contracts\StorageAdapter;
class DatabaseStorageAdapter implements StorageAdapter
{
public function save(WorkflowInstance $instance): void { /* ... */ }
public function load(string $id): WorkflowInstance { /* ... */ }
public function findInstances(array $criteria = []): array { /* ... */ }
public function delete(string $id): void { /* ... */ }
public function exists(string $id): bool { /* ... */ }
public function updateState(string $id, array $updates): void { /* ... */ }
}
use SolutionForest\WorkflowEngine\Contracts\EventDispatcher;
use SolutionForest\WorkflowEngine\Events\WorkflowStartedEvent;
use SolutionForest\WorkflowEngine\Events\WorkflowCompletedEvent;
use SolutionForest\WorkflowEngine\Events\WorkflowFailedEvent;
use SolutionForest\WorkflowEngine\Events\WorkflowCancelledEvent;
use SolutionForest\WorkflowEngine\Events\StepCompletedEvent;
use SolutionForest\WorkflowEngine\Events\StepFailedEvent;
use SolutionForest\WorkflowEngine\Events\StepRetriedEvent;
class CustomEventDispatcher implements EventDispatcher
{
public function dispatch(object $event): void
{
match ($event::class) {
WorkflowStartedEvent::class => $this->onWorkflowStarted($event),
WorkflowCompletedEvent::class => $this->onWorkflowCompleted($event),
WorkflowFailedEvent::class => $this->onWorkflowFailed($event),
StepCompletedEvent::class => $this->onStepCompleted($event),
StepFailedEvent::class => $this->onStepFailed($event),
StepRetriedEvent::class => $this->onStepRetried($event),
default => null,
};
}
}
use SolutionForest\WorkflowEngine\Contracts\Logger;
class CustomLogger implements Logger
{
public function info(string $message, array $context = []): void { /* ... */ }
public function warning(string $message, array $context = []): void { /* ... */ }
public function error(string $message, array $context = []): void { /* ... */ }
public function debug(string $message, array $context = []): void { /* ... */ }
}
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.