SkillAgentSearch skills...

Workflow

Declarative workflow engine for PHP with automatic dependency resolution, sync/async job execution, and type-safe response chaining.

Install / Use

/learn @chevere/Workflow
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Workflow

Chevere

Build Code size Apache-2.0 PHPStan Mutation testing badge

Quality Gate Status Maintainability Rating Reliability Rating Security Rating Coverage Technical Debt CodeFactor

Summary

Chevere Workflow is a PHP library for building and executing multi-step procedures with automatic dependency resolution. Define independent jobs that can run synchronously or asynchronously, pass data between them using typed responses, and let the engine handle execution order automatically.

Key features:

  • Declarative job definitions: Define what to do, not how to orchestrate it
  • Automatic dependency graph: Jobs execute in optimal order based on their dependencies
  • Sync and async execution: Mix blocking and non-blocking jobs freely
  • Type-safe responses: Access job outputs with full type safety
  • Conditional execution: Run jobs based on variables or previous responses
  • Built-in retry policies: Handle transient failures automatically
  • Testable: Each job is independently testable and workflow graph can be verified

You define jobs and how they connect and depend on each other, Chevere Workflow figures out the execution order and runs them accordingly.

Integrations

Installing

Workflow is available through Packagist and the repository source is at chevere/workflow.

composer require chevere/workflow

Quick Start

Here's a minimal example to get you started:

use function Chevere\Workflow\{workflow, sync, variable, run};

// 1. Define a workflow with jobs
$workflow = workflow(
    greet: sync(
        fn(string $name): string => "Hello, {$name}!",
        name: variable('username')
    )
);

// 2. Run with variables
$result = run($workflow, username: 'World');

// 3. Get typed responses
echo $result->response('greet')->string();
// Output: Hello, World!

Workflow Provider Convention

Implement WorkflowProviderInterface to expose a workflow definition from a class:

use Chevere\Workflow\Interfaces\WorkflowProviderInterface;
use Chevere\Workflow\Interfaces\WorkflowInterface;

class MyProvider implements WorkflowProviderInterface
{
    public static function workflow(): WorkflowInterface
    {
        return workflow(/* ... */);
    }
}

This is the recommended pattern for packages and applications. It separates workflow configuration from execution logic and enables discovery by tooling such as the Chevere Workflow VSCode extension.

Core Concepts

Workflow is built around four main concepts:

| Concept | Description | | ------------ | --------------------------------------------------- | | Job | A unit of work that produces a response | | Variable | External input provided when running the workflow | | Response | Reference to output from a previous job | | Graph | Automatic execution order based on job dependencies |

How It Works

  1. You define jobs using sync() or async() functions
  2. Jobs declare their inputs: literal values, variable() references, or response() from other jobs
  3. The engine builds a dependency graph automatically
  4. Jobs execute in optimal order (parallel when possible)
  5. Access typed responses after execution

Functions Reference

| Function | Purpose | | ------------ | ----------------------------------------- | | workflow() | Create a workflow from named jobs | | sync() | Create a synchronous (blocking) job | | async() | Create an asynchronous (non-blocking) job | | variable() | Declare a runtime variable | | response() | Reference another job's output | | run() | Execute a workflow with variables |


Jobs

Jobs are the building blocks of a workflow. Each job wraps an executable unit (Action, Closure, Invocable class, or any PHP callable) and declares its input arguments.

Creating Jobs with Closures

Use closures for simple, inline operations:

use function Chevere\Workflow\{workflow, sync, async, variable, response, run};

$workflow = workflow(
    // Simple calculation
    add: sync(
        fn(int $a, int $b): int => $a + $b,
        a: 10,
        b: variable('number')
    ),
    // Format the result
    format: sync(
        fn(int $sum): string => "Sum: {$sum}",
        sum: response('add')
    )
);

$result = run($workflow, number: 5);
echo $result->response('format')->string(); // Sum: 15

Creating Jobs with Action Classes

For complex or reusable logic, use Action classes as these additionally support method definitions for acceptParameters() and acceptReturn() to define parameter and return rules that are automatically applied at runtime.

use Chevere\Action\Action;

class FetchUser extends Action
{
    public function __invoke(int $userId): array
    {
        // Fetch user from database
        return ['id' => $userId, 'name' => 'John', 'email' => 'john@example.com'];
    }
}

class SendEmail extends Action
{
    public function __invoke(string $email, string $subject): bool
    {
        // Send email logic
        return true;
    }
}
$workflow = workflow(
    user: sync(
        FetchUser::class,
        userId: variable('id')
    ),
    notify: sync(
        SendEmail::class,
        email: response('user', 'email'),
        subject: 'Welcome!'
    )
);

$result = run($workflow, id: 123);

Creating Jobs with Invocable Classes

Use invocable classes (classes with __invoke method) for reusable logic without needing Action base class:

class CalculateTotal
{
    public function __invoke(array $items, float $taxRate): float
    {
        $subtotal = array_sum(array_column($items, 'price'));
        return $subtotal * (1 + $taxRate);
    }
}

class FormatCurrency
{
    public function __invoke(float $amount, string $currency = 'USD'): string
    {
        return $currency . ' ' . number_format($amount, 2);
    }
}
$workflow = workflow(
    total: sync(
        CalculateTotal::class,
        items: variable('items'),
        taxRate: 0.08
    ),
    formatted: sync(
        FormatCurrency::class,
        amount: response('total'),
        currency: 'EUR'
    )
);

$result = run($workflow, items: [
    ['name' => 'Item 1', 'price' => 10.00],
    ['name' => 'Item 2', 'price' => 20.00]
]);
echo $result->response('formatted')->string(); // EUR 32.40

Creating Jobs with Callables

Use any PHP callable including array callbacks, function names, or static methods:

class StringHelper
{
    public static function uppercase(string $text): string
    {
        return strtoupper($text);
    }

    public function reverse(string $text): string
    {
        return strrev($text);
    }
}
$helper = new StringHelper();

$workflow = workflow(
    // Using built-in PHP function
    trim: sync(
        'trim',
        string: variable('input')
    ),
    // Using static method
    upper: sync(
        [StringHelper::class, 'uppercase'],
        text: response('trim')
    ),
    // Using instance method
    reversed: sync(
        [$helper, 'reverse'],
        text: response('upper')
    )
);

$result = run($workflow, input: '  hello  ');
echo $result->response('reversed')->string(); // OLLEH

Sync vs Async Jobs

Synchronous jobs (sync) block execution until complete. Use for operations that must run in sequence:

workflow(
    first: sync(ActionA::class),  // Runs first
    second: sync(ActionB::class), // Waits for first
    third: sync(ActionC::class),  // Waits for second
);
// Graph: first →
View on GitHub
GitHub Stars110
CategoryDevelopment
Updated1h ago
Forks2

Languages

PHP

Security Score

100/100

Audited on Mar 30, 2026

No findings