solution-forest/workflow-engine-core
A framework-agnostic PHP 8.3+ workflow engine for defining, executing, and tracking workflows with state management, retries, timeouts, and conditions. Extensible and type-safe, with no framework dependencies.
Fixes several cases where the engine silently did the wrong thing and still reported success — the failure mode a workflow engine can least afford, because nothing in the test suite or a user's logs would catch it.
v1.0.0 was released earlier today as "no behavioural change from v0.0.4-alpha" — accurate, but it means the defects below are now in a release marked stable. Because v1.0.0 is a stable tag, these corrections change public class names and runtime behaviour, so semver requires a major bump. Each breaking change is listed with its migration.
ConditionEvaluator had no &&/|| support but swallowed the operator into the right-hand side and string-compared, so 'order.total > 1000 && order.vip === true' returned true for a 500 dollar order. Boolean operators, parentheses and negated groups are now supported; malformed expressions throw instead of collapsing to a boolean.resume() on a failed workflow was broken and masked its own error, throwing Cannot transition workflow from 'failed' to 'failed' from inside the failure handler. FAILED -> RUNNING is now legal, resume retries the failed step, and the handler can never hide the real cause.completed at 66.67% progress.false (null coerces to 0, so missing.key < 1000 was true).start() no longer overwrites an existing instance, discarding its state and history.WAITING instead of sitting in RUNNING indistinguishably from one still executing.DelayAction honours the documented minutes/hours keys — never read, so delay(hours: 2) paused for one second.HttpAction reports missing ext-curl as a step failure, not a fatal error.| Change | Migration |
|---|---|
EmailAction → FakeEmailAction (it never sent mail but reported 'status' => 'sent') |
Implement WorkflowAction with a real mail transport |
WorkflowBuilder::email() → fakeEmail() |
Rename the call |
ConditionAction drops on_true/on_false (read but never consumed) |
Branch with a condition on the transition |
ConditionAction uses the shared condition grammar |
Use ===/==/!= instead of =/is |
FAILED is no longer terminal |
Update code asserting failed workflows are immutable |
Storage\InMemoryStorage now ships with the package. The only adapter previously lived in tests/ under autoload-dev, so the library could not run a workflow out of the box.WorkflowDefinition::getFirstSteps(), Support\Arr::has().SECURITY.md with a disclosure process and scope notes.run-tests.yml and phpstan.yml now run on pull requests, not just pushes. Combined with dependabot auto-merge, updates could previously reach main without the suite ever running against the merge result.body key neither action read), fixed attribute examples that claimed retries/timeouts the engine does not perform, and documented the condition grammar.161 tests / 363 assertions (up from 116/277), with regression coverage for every issue above. Green on PHP 8.3, 8.4 and 8.5; Pint, PHPStan level 6 and composer validate --strict all clean.
Full Changelog: https://github.com/solutionforest/workflow-engine-core/compare/v1.0.0...v2.0.0
Full Changelog: https://github.com/solutionforest/workflow-engine-core/compare/v0.0.3-alpha...v0.0.4-alpha
Full Changelog: https://github.com/solutionforest/workflow-engine-core/compare/v0.0.2-alpha...v0.0.3-alpha
Full Changelog: https://github.com/solutionforest/workflow-engine-core/commits/v0.0.1-alpha
How can I help you explore Laravel packages today?