PHPackages                             onlab/balno-workflow - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. onlab/balno-workflow

ActiveLibrary

onlab/balno-workflow
====================

A PHP 5.5+ Workflow with parallel execution

1.1.1(10y ago)3412.3k↓50%6[1 issues](https://github.com/onlab/BalnoWorkflow/issues)PHPPHP &gt;=5.5

Since Jun 30Pushed 10y ago5 watchersCompare

[ Source](https://github.com/onlab/BalnoWorkflow)[ Packagist](https://packagist.org/packages/onlab/balno-workflow)[ RSS](/packages/onlab-balno-workflow/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (2)Versions (5)Used By (0)

BalnoWorkflow
=============

[](#balnoworkflow)

[![Build Status](https://camo.githubusercontent.com/153fbbfe62bae376e312a5774ddc5d01aa364704a0036787af3607ab87bcd031/68747470733a2f2f7472617669732d63692e6f72672f6f6e6c61622f42616c6e6f576f726b666c6f772e7376673f6272616e63683d6d6173746572)](https://travis-ci.org/onlab/BalnoWorkflow)

BalnoWorkflow is a workflow engine built for **PHP 5.5+** based on some other Workflows and State Machines that does not have all features in the same lib.

This workflow gives to you the following feature:

- nice syntax to setup the workflow based on SCXML () structure.
- easy setup for parallel workflows (fork/merge).
- paused workflow can be resumed by a event trigger.
- no event-based transitions run automatically when guard condition (if was set) is satisfied.
- guards and actions configuration as service using Pimple as DI container.
- events available to lock control, log or anything else you want to implement with event listeners.

Workflow Events to Subscribe
----------------------------

[](#workflow-events-to-subscribe)

You can check the events available in the *interface* `WorkflowEvents`:

- **begin\_execution**: triggered when the workflow execution will start.
- **end\_execution**: triggered when the workflow execution is done (paused or finished).
- **start\_transition**: before onExit actions when changing the workflow state.
- **state\_changed**: between onExit and onEntry actions just after setting the current state on context.
- **end\_transition**: after onEntry actions when changed the workflow current state.

Basic Definition Sample
-----------------------

[](#basic-definition-sample)

Always the first state defined will be set as initial state in the case below, the 'state\_1' state is set as initial state.

```
use BalnoWorkflow\DefinitionsContainer;

$definitionsContainer = new DefinitionsContainer();
$definitionsContainer->addDefinition('sample_workflow', [
    'state_1' => [
        targets => [
            'state_2' => null,
        ],
        onExit => [
            [ action => 'pimple_service:method1' ],
        ],
    ],
    'state_2' => [
        targets => [
            'state_3' => [ event => 'some_event' ],
            'state_5' => [ guard => 'balno.workflow.guard.timer:hasTimedOut("30m")' ],
        ],
        onEntry => [
            [ action => 'pimple_service1:method' ],
            [ action => 'pimple_service2:method' ],
        ],
    ],
    'state_3' => [
        targets => [
            'state_4' => null,
        ],
        parallel => [
            'forked_workflow1',
            'forked_workflow2',
        ],
    ],
    'state_4' => [
        onEntry => [
            [ action => 'pimple_service:method2("param")' ],
        ],
    ],
    'state_5' => null
]);
$definitionsContainer->addDefinition('forked_workflow1', [ ... ]);
$definitionsContainer->addDefinition('forked_workflow2', [ ... ]);
```

Given a new `Context` to execute...

```
use BalnoWorkflow\Workflow;
use BalnoWorkflow\Context;

$context = new Context();

$workflow = new Workflow(...);
$workflow->execute($context);
```

... this workflow will execute the below history:

> **trigger** `begin_execution` listeners
>
> > **IMPORTANT:** initial state will not trigger onEntry actions
>
> ... check for default (no event set) transitions (found transition without guard to state\_2)
>
> **trigger** `begin_transition` listeners
>
> **execute** state\_1 `onExit` actions
>
> **trigger** `state_changed` listeners
>
> **execute** state\_2 `onEntry` actions
>
> **trigger** `end_transition` listeners
>
> ... check for default transitions (found one with guard)
>
> **execute** guard `balno.workflow.guard.timer:hasTimedOut("30m")` that returns `false`
>
> **trigger** `end_execution` listeners

Now the state is in a paused state. To move forward the workflow must be executed after 30m (to satisfy the configured guard timeout) or be executed with an event `some_event`.

To trigger the event you must run the code below (imagine that you is resuming the workflow with a persisted context):

```
$context = $myContextService->getSubjectWorkflowContext($subject);
$workflow->execute($context, 'some_event');

```

.. or if you just want to resume the workflow to reach the timeout condition:

```
$context = $myContextService->getSubjectWorkflowContext($subject);
$workflow->execute($context);

```

Actions
-------

[](#actions)

Every action or guard are executed by the workflow passing the `Context` object as first parameter followed by the parameters configured on the action or guard.

Given a guard `guard.example:someGuardCondition("test1", 2)` the method must be something like this:

```
class GuardExample
{
    public function someGuardCondition(ContextInterface $context, $parameter1, $parameter2)
    {
        ...
    }
}

```

Parallel Execution
------------------

[](#parallel-execution)

Parallel execution is a special state that can't forward to an other state (even by an event) until all parallel workflows are finished.

From the \[Basic Definition Sample\]:

```
state_3
   |
   |------------v--------------------v
   |            |                    |
   |      forked_workflow1     forked_workflow2
   |            |                    |
   |------------^--------------------^
   |
state_4

```

Since PHP unfortunately does not work with threads out-of-the-box the BalnoWorkflow will execute first the `forked_workflow1` then `forked_workflow2`. So I recommend to place the fast process first then the slowest.

But imagine you need to send an e-mail to your client to confirm his e-mail (you're using a slow SMTP server) on the `forked_workflow2` and the `forked_workflow1` will prepare an order to the product factory. Sending an e-mail to the client will be better than creating an order to the factory. So, in this case, to satisfy the customer, will be better to execute the slowest workflow first.

If you really need to execute in parallel you may setup a initial state without a default transition to pause one or both workflows then run each workflow in kind of worker.

**IMPORTANT**: the workflow will automatically try to resume the parent context when you resume a child workflow and it finishes. So, if you're really trying to parallelize this sub workflows ensure that your locking system is compatible with this scenario.

###  Health Score

35

—

LowBetter than 80% of packages

Maintenance19

Infrequent updates — may be unmaintained

Popularity35

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity61

Established project with proven stability

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~43 days

Total

3

Last Release

3888d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/29ca7e71b2877d7acea488f9ecbcd6f68ffc9ef5584fdb1b8217ed9ff9e6892e?d=identicon)[mfernandez](/maintainers/mfernandez)

---

Top Contributors

[![onlab](https://avatars.githubusercontent.com/u/992037?v=4)](https://github.com/onlab "onlab (1 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/onlab-balno-workflow/health.svg)

```
[![Health](https://phpackages.com/badges/onlab-balno-workflow/health.svg)](https://phpackages.com/packages/onlab-balno-workflow)
```

###  Alternatives

[symfony/framework-bundle

Provides a tight integration between Symfony components and the Symfony full-stack framework

3.6k235.4M9.7k](/packages/symfony-framework-bundle)[symfony/security-bundle

Provides a tight integration of the Security component into the Symfony full-stack framework

2.5k172.9M1.8k](/packages/symfony-security-bundle)[drupal/core

Drupal is an open source content management platform powering millions of websites and applications.

19462.3M1.3k](/packages/drupal-core)[damienharper/auditor

The missing audit log library.

1922.8M8](/packages/damienharper-auditor)[symfony/ai-platform

PHP library for interacting with AI platform provider.

51927.7k136](/packages/symfony-ai-platform)[acquia/orca

A tool for testing a company's software packages together in the context of a realistic, functioning, best practices Drupal build

32902.4k](/packages/acquia-orca)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
