projectsaturnstudios/pocketflow-php

PHP implementation of The-Pocket/PocketFlow — minimal graph workflow orchestration.

Maintainers

Package info

github.com/projectsaturnstudios/pocketflow-php

pkg:composer/projectsaturnstudios/pocketflow-php

Transparency log

Statistics

Installs: 101

Dependents: 2

Suggesters: 0

Stars: 1

Open Issues: 0

0.2.0 2026-08-07 18:34 UTC

This package is auto-updated.

Last update: 2026-08-07 18:38:41 UTC


README

tests Latest Version Total Downloads License PHP Version

Minimal graph workflow library for PHP — a port of The-Pocket/PocketFlow.

  • Nodes with prepexecpost
  • Flows that route on action strings (default, continue, …)
  • Shared state bag mutated across hops (Flows clone nodes; put durable data in $shared)
  • Async via ReactPHP Promise (AsyncNode / AsyncFlow)
  • Batch helpers for list and parallel fan-out

Package: projectsaturnstudios/pocketflow-php
Namespace: ProjectSaturnStudios\PocketFlow\

Requirements

  • PHP 8.4+
  • react/promise (required for async APIs; sync Flow / Node do not need an event loop)

Install

composer require projectsaturnstudios/pocketflow-php

Quick start (sync)

<?php

use ProjectSaturnStudios\PocketFlow\Flow;
use ProjectSaturnStudios\PocketFlow\Node;

class GreetNode extends Node
{
    public function prep(mixed &$shared): mixed
    {
        return $shared['name'] ?? 'World';
    }

    public function exec(mixed $prep_res): mixed
    {
        return "Hello, {$prep_res}!";
    }

    public function post(mixed &$shared, mixed $prep_res, mixed $exec_res): mixed
    {
        $shared['greeting'] = $exec_res;

        return 'default'; // next action
    }
}

class PrintNode extends Node
{
    public function post(mixed &$shared, mixed $prep_res, mixed $exec_res): mixed
    {
        echo $shared['greeting'], PHP_EOL;

        return null; // end flow
    }
}

$greet = new GreetNode;
$print = new PrintNode;
$greet->next($print);

$shared = ['name' => 'PocketFlow'];
(new Flow($greet))->run($shared);

Conditional edges

$nodeA->on('ok')->next($nodeB);
$nodeA->next($nodeC, 'retry');

Self-loop

$tick->next($tick, 'continue');
// return 'continue' to loop, or null to stop

Async

use ProjectSaturnStudios\PocketFlow\AsyncFlow;
use ProjectSaturnStudios\PocketFlow\AsyncNode;
use React\Promise\PromiseInterface;
use function React\Promise\resolve;

class FetchNode extends AsyncNode
{
    public function exec_async(mixed $prep_res): PromiseInterface
    {
        return resolve(['ok' => true]);
    }

    public function post_async(mixed $shared, mixed $prep_res, mixed $exec_res): PromiseInterface
    {
        $shared['result'] = $exec_res;

        return resolve('default');
    }
}

$shared = [];
$promise = (new AsyncFlow(new FetchNode))->run_async($shared);
// $promise is a React\Promise\PromiseInterface

Array $shared values are wrapped in a SharedStore for the async run so mutations persist across promise hops, then copied back into your array when the promise settles.

Nested AsyncFlow graphs work: AsyncFlow extends AsyncNode, matching the Python MRO intent.

Batch

  • BatchNode / BatchFlow — sequential items / param sets
  • AsyncBatchNode / AsyncParallelBatchNode — sequential vs Promise\all
  • AsyncBatchFlow / AsyncParallelBatchFlow — batch orchestration

Public API notes

Method Role
Node::run(&$shared) Run a single node (warns if successors exist — use Flow)
Flow::run(&$shared) Orchestrate from the start node
AsyncNode::run_async(&$shared) / AsyncFlow::run_async(&$shared) Promise-based entry
post / post_async return value Action key for the next successor (default if null/non-string)

Retries: Node / AsyncNode honor max_retries and wait (seconds) via _exec.

Tests

composer install
composer test
# or: vendor/bin/pest

CI runs Pest on PHP 8.4.

Comparison with Python PocketFlow

Feature Python This package
Graph + action routing Yes Yes
Shared mutable store dict array / SharedStore
Async asyncio ReactPHP Promise
Batch / parallel batch Yes Yes

License

MIT — same spirit as the original PocketFlow.

Acknowledgments