Run Parallel PHP Code with Spatie Fork: A Practical Guide

This guide explains how to install the Spatie Fork package, meet its requirements, and use its run, before, after, and concurrent methods to execute multiple PHP closures in parallel within CLI environments, including handling return values and database connections.

Open Source Tech Hub
Open Source Tech Hub
Open Source Tech Hub
Run Parallel PHP Code with Spatie Fork: A Practical Guide

Overview

The Spatie Fork package enables easy concurrent execution of PHP code by forking the main PHP process into one or more child processes.

Example

The following snippet runs three simulated slow API calls concurrently:

use Spatie\Fork\Fork;

$results = Fork::new()
    ->run(
        fn () => (new Api)->fetchData(userId: 1),
        fn () => (new Api)->fetchData(userId: 2),
        fn () => (new Api)->fetchData(userId: 3),
    );

$results[0]; // data for user 1
$results[1]; // data for user 2
$results[2]; // data for user 3

Requirements

PHP 8+

pcntl extension installed (default on most Unix/macOS)

CLI environment only (no web request context)

Alpine Linux needs the posix extension for proper process termination

Installation

Install via Composer:

composer require spatie/fork

Basic Usage

Pass any number of closures to the run method; they will be executed concurrently and the method returns an array with all results.

use Spatie\Fork\Fork;

$results = Fork::new()
    ->run(
        function () {
            sleep(1);
            return 'Result from task 1';
        },
        function () {
            sleep(1);
            return 'Result from task 2';
        },
        function () {
            sleep(1);
            return 'Result from task 3';
        },
    );

// This code runs after 1 second
$results[0]; // contains 'Result from task 1'
$results[1]; // contains 'Result from task 2'
$results[2]; // contains 'Result from task 3'

Running Code Before/After Each Closure

You can execute code before or after each callable using the before and after methods. These callbacks run in the child process, either before or after the main closure.

Using before in Child Tasks

When a database connection is needed inside child tasks, reconnect it first:

use App\Models\User;
use Illuminate\Support\Facades\DB;
use Spatie\Fork\Fork;

Fork::new()
    ->before(fn () => DB::connection('mysql')->reconnect())
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

Using before in the Parent Task

Pass a callable to the parent key to run it in the parent process:

Fork::new()
    ->before(
        parent: fn() => echo 'Runs in parent task'
    )
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

Separate Child and Parent Callbacks

Fork::new()
    ->before(
        child: fn() => echo 'Runs in child task',
        parent: fn() => echo 'Runs in parent task',
    )
    ->run(
        fn () => User::find(1)->someLongRunningFunction(),
        fn () => User::find(2)->someLongRunningFunction(),
    );

Returning Data

All output from child tasks is collected into an array that becomes available once every task finishes.

$results = Fork::new()
    ->run(
        fn () => (new Api)->fetchData(userId: 1),
        fn () => (new Api)->fetchData(userId: 2),
        fn () => (new Api)->fetchData(userId: 3),
    );

You can also access results in an after callback, which is invoked as each child completes:

$results = Fork::new()
    ->after(
        child: fn (int $i) => echo $i, // outputs 1, 2, 3 sequentially
        parent: fn (int $i) => echo $i, // outputs 1, 2, 3 sequentially
    )
    ->run(
        fn () => 1,
        fn () => 2,
        fn () => 3,
    );

Child return values are serialized using PHP's serialize function, allowing any serializable data—including objects—to be returned:

$result = Fork::new()
    ->run(
        fn () => new DateTime('2021-01-01'),
        fn () => new DateTime('2021-01-02'),
    );

Configuring Concurrency

By default all callables run in parallel. Use concurrent to limit the maximum number of simultaneous processes.

$results = Fork::new()
    ->concurrent(2)   // at most 2 processes at once
    ->run(
        fn () => 1,
        fn () => 2,
        fn () => 3,
    );

In this scenario the first two functions start immediately; the third begins only after one of the first two finishes.

This package is especially suitable for Laravel Artisan commands or any CLI scripts that need to parallelize time‑consuming tasks such as batch API calls or data processing.

CLIconcurrencyPHPParallel ExecutionSpatie Fork
Open Source Tech Hub
Written by

Open Source Tech Hub

Sharing cutting-edge internet technologies and practical AI resources.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.