Darkwood Β· PHP Β· Orchestration

πŸ˜Άβ€πŸŒ«οΈ This Person Does Not Exist

From Synchronous PHP
to Asynchronous Orchestration with Flow

A minimal Symfony experiment

HTTP request
β†’
Image bytes
β†’
File saved
@matyo91

The problem

Download 10 images. Respect a rate limit. Save each file.

Image 1
Image 2
Image 3
…
Image 10

Same pipeline for every image. No dependency between them.

Why are we waiting?

01 | problem @matyo91

The synchronous loop

One image at a time. The process blocks on every step.

Image 1 Fetch→ Save→ Sleep
Image 2 waits…
Image 3 waits…

Everything waits.

for (...) { fetch(); save(); sleep(); }
02 | sync-loop @matyo91

What is actually happening?

A hidden pipeline, repeated N times.

Fetch
↓
Save
↓
Report

Each image is an independent work unit.

03 | pipeline @matyo91

The hidden opportunity

Independent units. Same pipeline.

flowchart LR
  I1[Image 1]
  I2[Image 2]
  I3[Image 3]
  P[Pipeline\nFetch β†’ Save β†’ Report]
  I1 --> P
  I2 --> P
  I3 --> P

Why not run them together?

04 | opportunity @matyo91

Introducing Flow

Work units enter an orchestrator.

Ip(1)
Ip(2)
Ip(3)
↓
Flow
↓
Pipeline

An Ip represents one work unit - a payload traveling through the pipeline.

05 | flow @matyo91

The pipeline

Describe steps once. Every Ip follows the same path.

Ip
↓
Fetch Image
↓
Save Image
↓
Report
06 | pipeline-steps @matyo91

The migration

Suspend the work unit - not the whole process.

Before Β· blocking

sleep(1);

Entire process frozen.

After Β· cooperative

$driver->delay($delay);

Only this fiber suspends.

Rate limiting per unit. Progress for everyone else.

07 | migration @matyo91

Fibers Β· PHP 8.1

Waiting becomes productive.

Fiber A delay - suspended waiting
Fiber B downloading image working
Fiber C saving to disk working
Fiber A resumed - fetch starts resume

FiberDriver delegates execution. Flow orchestrates the pipeline.

08 | fibers @matyo91

The new execution model

Enqueue work. Await completion.

for (...) {
    $flow(new Ip(...));
}
$flow->await();
flowchart TB
  E1[enqueue Ip]
  E2[enqueue Ip]
  E3[enqueue Ip]
  S[Flow scheduler]
  P[pipeline execution]
  A[await]
  E1 --> S
  E2 --> S
  E3 --> S
  S --> P
  P --> A

The for loop produces work. Flow schedules it.

09 | execution @matyo91

Clarification

What Flow is - and is not.

βœ• Event Loop
βœ• Fiber implementation
βœ• Runtime
βœ• ReactPHP / Amp replacement
βœ“ Orchestration model
FiberDriver
AmpDriver
ReactDriver
SwooleDriver

Describe the pipeline once. Choose the driver for execution.

10 | what-flow-is @matyo91

Beyond the POC

Same orchestration model. Different domains.

YouTube

Scrape
↓
Transcript
↓
Publish

Video

Scene gen
↓
Encode
↓
Persist

AI Agents

Read
↓
Transform
↓
Act

Independent units. Composable steps. One orchestrator.

11 | applications @matyo91

Takeaway

Not about performance.
About orchestration.

Before

Execute task A
then B
then C

After

  • Describe work units
  • Describe a pipeline
  • Let the orchestrator schedule
Ip
↓
Flow
↓
Fetch
↓
Save
↓
await()
12 | takeaway @matyo91

Resources

Go deeper.

  • flow-thispersondoesnotexist - POC repo Β· commits fae47e8 β†’ c61c929
  • darkwood/flow - orchestration package Β· examples/flow.php
  • FrΓ©dΓ©ric Bouchery - The Evolution of Async PHP
f2r.github.io/fr/asynchrone.html
Sync loop
β†’
Flow pipeline
β†’
Darkwood scale
@matyo91