Skip to content

About

Durable execution for PHP: workflows that survive deploys, restarts and crashes, and resume where they stopped. Symfony, Laravel, Sylius and Magento integrations. Monorepo — the published packages are subtree-splits of src/.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Latest commit

 

History

5,019 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Durable (PHP)

License: MIT

Durable runs long-running processes in PHP. You write a process as one PHP method, waits included, and it survives crashes and deploys. Durable records each step in a journal (the history of a run's steps and their results). After a restart, a worker (the process that runs workflows and activities) replays the method from its first line: a step already in the journal returns its recorded result instead of running again, and the method resumes at the first step the journal does not have. Because the method runs again from its first line, it has to take the same decisions on every run: HTTP calls, database queries, random numbers and timestamps belong in activities.

It runs on Symfony, Sylius, Laravel, Magento 2.4 and Mage-OS. On Symfony and Laravel, work goes through Messenger or the application's queue; on Magento, through bin/magento durable:worker. The same workflow code runs on three backends (where the journal lives): in memory (tests), on one SQL database (Doctrine DBAL or Laravel's database layer), or on a Temporal cluster. To switch, you install that backend's bridge and change one configuration value. Magento offers memory and Temporal only.

Example: a workflow that waits three days

An order is charged, the workflow waits three days, then asks for a review. On a SQL database or on Temporal, a deploy or a crash during the wait loses nothing. The journal holds the charge, and the timer still fires after 72 hours.

use Gplanchat\Durable\Activity\ActivityStub;
use Gplanchat\Durable\Attribute\Activities;
use Gplanchat\Durable\Attribute\AsWorkflow;
use Gplanchat\Durable\Attribute\AsWorkflowMethod;
use Gplanchat\Durable\Duration;
use Gplanchat\Durable\WorkflowEnvironment;

#[AsWorkflow(name: 'order-follow-up')]
final class OrderFollowUp
{
    /** @param ActivityStub<OrderActivities> $orders */
    #[AsWorkflowMethod]
    public function run(
        string $orderId,
        #[Activities(OrderActivities::class)]
        ActivityStub $orders,
        WorkflowEnvironment $env,
    ): void {
        $env->await($orders->charge($orderId));            // retried with backoff, without limit by default
        $env->sleep(Duration::hours(72));                  // a timer, not a scheduled job
        $env->await($orders->sendReviewRequest($orderId));
    }
}

OrderActivities is an interface whose methods are activities (calls with a side effect, such as an HTTP request or an e-mail). Getting started builds a complete workflow on Symfony: the activity interface and its handler, the controller that starts it, and the worker that runs it.

Install

Durable is on its beta line, and each package requires its siblings from the same line. Allow beta releases first, then require the package for your application:

composer config minimum-stability beta
composer config prefer-stable true
Your application Command
Symfony, one SQL database composer require gplanchat/durable-bundle gplanchat/durable-bridge-dbal
Symfony, Temporal cluster composer require gplanchat/durable-bundle gplanchat/durable-bridge-temporal
Sylius composer require gplanchat/durable-plugin gplanchat/durable-bridge-dbal
Laravel, one SQL database composer require gplanchat/durable-laravel gplanchat/durable-bridge-illuminate, then php artisan migrate and php artisan vendor:publish --tag=durable-config
Laravel, Temporal cluster composer require gplanchat/durable-laravel gplanchat/durable-bridge-temporal, publish the config, then set backend to temporal and fill the temporal DSN in config/durable.php
Magento 2.4 or Mage-OS, Temporal cluster composer require gplanchat/durable-magento gplanchat/durable-bridge-temporal
No framework, or unit tests only composer require gplanchat/durable

On Symfony and Sylius, add composer config extra.symfony.allow-contrib true before the require, so Flex registers the bundle. The Symfony SQL database line also needs DoctrineBundle (with doctrine/orm) and the Doctrine Messenger transport, which Getting started lists. On Laravel, durable-laravel requires neither bridge: you install the one of the backend you select.

The Packages page has the full table of combinations and what each package adds. PHP 8.2 or later is required. On Temporal, the bridge uses ext-grpc when it is loaded and falls back to ext-curl with HTTP/2 otherwise; the bridge README has the details.

Packages in this repository

This monorepo holds every package. A split publishes each one to its own read-only repository, so composer require pulls only what you name.

Package Path Role
gplanchat/durable src/Durable/ Core library (workflows, activities, event store, in-memory and integration surfaces)
gplanchat/durable-bundle src/DurableBundle/ Symfony bundle (Messenger, configuration, profiler)
gplanchat/durable-bridge-temporal src/Bridge/Temporal/ Temporal gRPC bridge (no official Temporal PHP SDK; see DUR006)
gplanchat/durable-bridge-dbal src/Bridge/Dbal/ Doctrine DBAL journal and stores: durable execution on one SQL database, no cluster (DUR030)
gplanchat/durable-bridge-illuminate src/Bridge/Illuminate/ Illuminate (Laravel) journal and stores: durable execution on one SQL database through Laravel's database layer (DUR030)
gplanchat/durable-laravel src/DurableLaravel/ Laravel integration: binds the four storage ports from one published config file, work rides the application's queue
gplanchat/durable-filament src/DurableFilament/ Filament 3 and 4 panel plugin: read-only dashboard of workflow runs, requires the Laravel integration
gplanchat/durable-magento src/DurableModule/ Magento 2 / Mage-OS module: durable:worker, a read-only admin grid and process history, memory and Temporal backends (DUR046)
gplanchat/durable-plugin src/DurablePlugin/ Sylius 2 admin plugin: workflow dashboard, backend-neutral (DUR037)
gplanchat/durable-phpstan src/DurablePhpstan/ PHPStan extension: resolves stub calls against their typed contract
gplanchat/durable-rector src/DurableRector/ Rector rules migrating a project off the official Temporal PHP SDK
in-tree only src/DurableDemoContracts/ The Nexus contracts (operations one service serves and another calls, like an activity) that the four bench applications share; deliberately not published

Benches

The applications that run the packages in CI, called benches, live in the same tree:

Bench Path Role
Symfony symfony/ Symfony application that runs the bundle, with Temporal integration tests
Sylius sylius/ Sylius 2.2 Standard, where the dashboard renders for real
Laravel laravel/ Laravel 12 application serving the delivery operation of the Nexus demonstration
Magento magento/ Mage-OS 2.2 application with the DurableProbe module and its probes

Durable uses neither the official Temporal PHP SDK nor RoadRunner as its runtime (DUR006).

Documentation

Architecture decisions for this component use the DUR prefix under documentation/adr/ (see DUR000).

Working in the monorepo

From the repository root:

composer install
composer test

For the Symfony bench (workers, Docker, PHPUnit):

cd symfony
composer install
composer test

See symfony/README.md for Messenger consumers, DURABLE_DSN, and optional Temporal integration tests.

Booting the Sylius bench

Sylius Standard 2.2 requires PHP 8.3 and an extension set no PHP on a typical dev box carries in one place, so the bench runs on the image the skeleton ships with, never on the host PHP:

cd sylius
cp compose.override.dist.yml compose.override.yml   # mounts the app, and `../src` it depends on
docker compose up -d                                # php 8.3, MySQL 8.4, nginx, mailhog
docker compose run --rm php composer install
docker compose run --rm php bin/console debug:router

composer.lock is tracked here, unlike in the upstream skeleton: this bench is a test bed in CI, and an unpinned resolve would let a Sylius release break the build with no commit in this repository behind it.

License

This project is released under the MIT License (SPDX identifier: MIT). The full text is in LICENSE; distribution policy is described in WA004.

About

Durable execution for PHP: workflows that survive deploys, restarts and crashes, and resume where they stopped. Symfony, Laravel, Sylius and Magento integrations. Monorepo — the published packages are subtree-splits of src/.

Topics

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages