small / workflow
Framework-independent PHP workflow management library.
Requires
- php: >=8.3
- small/collection: ^4.0.2
- symfony/yaml: ^7.4 || ^8.0
Requires (Dev)
- pestphp/pest: ^3.0 || ^4.0
- phpstan/phpstan: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Small Workflow
Framework-independent PHP workflow management package (small/workflow).
The engine owns business validation orchestration and execution order. The application owns business entities, persistence implementation, transactions and locking mechanisms.
Lifecycle
When configured with a lock and a persister, WorkflowEngine::execute() performs:
WorkflowLock::lock($context)— acquire the business resource lock.- Resolve current states and validate business rules via
WorkflowState::getPossibleActions()andWorkflowGuard::check()inside the lock. - Resolve a permitted transition and invoke
WorkflowActionHandler::execute()to mutate business entities. WorkflowPersister::persist($context)— application persists its entities.- Dispatch
completedevent, orfailedwhen the handler/persister fails. - Always
WorkflowLock::unlock($context)infinallyafter successful acquisition.
If locking fails, the engine does not unlock a lock it never acquired. Validation failures do not call handler or persister.
Quick start
name: approval
parallel: false
states:
draft: App\Workflow\DraftState
approved: App\Workflow\ApprovedState
transitions:
submit:
from: draft
to: approved
action: submit
guards:
- App\Workflow\CanSubmit
$definition = (new WorkflowDefinitionLoader())->fromFile('workflow.yaml');
$engine = new WorkflowEngine(
definition: $definition,
handlers: ['submit' => $submitHandler],
services: [CanSubmit::class => $guard],
events: $dispatcher,
lock: $applicationLock,
persister: $businessEntityPersister,
);
$context = new WorkflowContext(['entity' => $entity, 'user' => $user]);
$decision = $engine->can(AffaireAction::Submit, $context);
$result = $engine->execute(AffaireAction::Submit, $context);
can() and evaluate() are read-only advisory operations; execute() rechecks business validations under the lock.
Persistence and consistency
This package does not contain an ORM, a database schema or a repository for workflow state. A WorkflowPersister implementation delegates to business entities/managers owned by the consumer. A WorkflowLock implementation must be appropriate to the runtime (row lock, advisory lock or distributed lock) and scoped to the relevant business resource.
Important: locking is optional for compatibility, but production consumers with concurrent writers should supply a lock. The package cannot create an atomic transaction merely by holding a lock. The application must provide a transaction boundary if it needs rollback of mutations, persistence and event/outbox consistency. A failure after an in-memory entity mutation does not roll back that object automatically. The completed event is sent only once the persister returns successfully, but event dispatch is not transactional by default.
Parallel states
With parallel: true, multiple states may be active. All source states of a transition must be present; the transition replaces those sources with its targets and preserves other active states. The engine does not spawn workers or manage distributed execution.
Quality gates
vendor/bin/phpstan analyse --no-progress
XDEBUG_MODE=coverage vendor/bin/pest --coverage --min=100
See docs/00-index.md for the chapter-by-chapter documentation.