PHPackages                             uncrackable404/concurrent-console-progress - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [CLI &amp; Console](/categories/cli)
4. /
5. uncrackable404/concurrent-console-progress

ActiveLibrary[CLI &amp; Console](/categories/cli)

uncrackable404/concurrent-console-progress
==========================================

Concurrent progress dashboard for PHP console applications that need to render and monitor multiple concurrent tasks in a single live terminal view.

0.3.1(4w ago)211MITPHPPHP ^8.3

Since Apr 10Pushed 4w agoCompare

[ Source](https://github.com/karim-tao/concurrent-console-progress)[ Packagist](https://packagist.org/packages/uncrackable404/concurrent-console-progress)[ RSS](/packages/uncrackable404-concurrent-console-progress/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (5)Versions (7)Used By (0)

Concurrent Console Progress
===========================

[](#concurrent-console-progress)

[![Concurrent Console Progress Preview](art/preview.png)](art/preview.png)

[![Total Downloads](https://camo.githubusercontent.com/ef5c9018f08d7e01c17eacd1404a893f28988b6dbae8ebee27e2f1fa263bd233/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f756e637261636b61626c653430342f636f6e63757272656e742d636f6e736f6c652d70726f6772657373)](https://packagist.org/packages/uncrackable404/concurrent-console-progress)[![Latest Stable Version](https://camo.githubusercontent.com/4008707ffb9697848b75976942240f9ce39dbea2e2173b7d481f7b8ef9dacc7d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f756e637261636b61626c653430342f636f6e63757272656e742d636f6e736f6c652d70726f6772657373)](https://packagist.org/packages/uncrackable404/concurrent-console-progress)[![License](https://camo.githubusercontent.com/415066a5f42faf6ba801663e915de37646cb69330ccdde6c8157cb8f811a0fcd/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f756e637261636b61626c653430342f636f6e63757272656e742d636f6e736f6c652d70726f6772657373)](https://packagist.org/packages/uncrackable404/concurrent-console-progress)

Introduction
------------

[](#introduction)

**Concurrent Console Progress** is a live dashboard for PHP console applications that run many concurrent tasks across one or more queues. Typical use cases: bulk imports, fan-out API calls, background migrations, and any batch job where you want per-queue progress bars, aggregated counters, and atomic shared state updated in real time.

The package is powered by [**spatie/fork**](https://github.com/spatie/fork) for process isolation and inspired by [**laravel/prompts**](https://github.com/laravel/prompts) for terminal rendering.

Requirements
------------

[](#requirements)

- PHP `^8.3`
- `ext-pcntl`, `ext-posix` (Unix-only — macOS, Linux)
- Works in CLI context only (forking is not available in FPM/Apache)

Installation
------------

[](#installation)

```
composer require uncrackable404/concurrent-console-progress
```

Usage
-----

[](#usage)

The package exposes:

- `Uncrackable404\ConcurrentConsoleProgress\ConcurrentProgress` — the main class.
- `Uncrackable404\ConcurrentConsoleProgress\concurrent()` — a convenience helper around `ConcurrentProgress::run()`.
- `Uncrackable404\ConcurrentConsoleProgress\ProgressState` — the interface for the shared state your tasks can read and mutate.

### Minimal Example

[](#minimal-example)

One queue, one counter, no shared state:

```
use Uncrackable404\ConcurrentConsoleProgress\ProgressState;

use function Uncrackable404\ConcurrentConsoleProgress\concurrent;

$tasks = [];
for ($i = 1; $i  'main', 'steps' => 1, 'id' => $i];
}

concurrent(
    queues: ['main' => ['label' => 'Processing', 'total' => count($tasks)]],
    tasks: $tasks,
    concurrent: 10,
    process: function (array $task, ProgressState $state): void {
        usleep(100_000);
        $state->advance('main', 1);
    },
);
```

### Complete Example

[](#complete-example)

Multiple queues, custom columns, footer values, and free-form return values. Task state (progress, per-row counters, global footer values) flows through the `ProgressState` passed as the second argument — the callback's return value is free-form and is forwarded to the caller as-is.

```
use Uncrackable404\ConcurrentConsoleProgress\ConcurrentProgress;
use Uncrackable404\ConcurrentConsoleProgress\ProgressState;

$queues = [
    'users' => ['label' => 'Importing Users', 'total' => 2],
    'orders' => ['label' => 'Importing Orders', 'total' => 1],
];

$tasks = [
    ['queue' => 'users', 'steps' => 1, 'data' => ['id' => 1, 'name' => 'John']],
    ['queue' => 'users', 'steps' => 1, 'data' => ['id' => 2, 'name' => 'Jane']],
    ['queue' => 'orders', 'steps' => 1, 'data' => ['id' => 101, 'total' => 50]],
];

$progress = new ConcurrentProgress();
$results = $progress->run(
    queues: $queues,
    tasks: $tasks,
    concurrent: 5,
    columns: [
        ['key' => 'status', 'label' => 'LAST ITEM'],
    ],
    footer: [
        ['key' => 'memory_peak', 'label' => 'MEMORY PEAK'],
    ],
    process: function (array $task, ProgressState $state): array {
        $id = $task['data']['id'] ?? 'unknown';

        // Per-queue cell (shown in the `status` column).
        $state->set('status', "✅ Processed #{$id}", queue: $task['queue']);

        // Global footer value — update atomically.
        $state->transform(
            'memory_peak',
            fn (mixed $current): int => max((int) ($current ?? 0), memory_get_peak_usage(true)),
        );

        // Advance the progress bar by one step for this queue.
        $state->advance($task['queue'], 1);

        // The return value is free-form and ends up in the $results array.
        return ['id' => $id];
    }
);

// $results = [['id' => 1], ['id' => 2], ['id' => 101]]
```

### Progress State API

[](#progress-state-api)

The `ProgressState` interface exposes atomic read/write operations against state owned by the parent process. A `ProgressState` instance is injected into your task callback when the closure signature declares a second parameter:

```
interface ProgressState
{
    public function get(string $key, ?string $queue = null): mixed;
    public function set(string $key, mixed $value, ?string $queue = null): void;
    public function compareAndSet(string $key, mixed $old, mixed $new, ?string $queue = null): bool;
    public function transform(string $key, Closure $fn, ?string $queue = null): mixed;
    public function advance(string $queue, int $steps = 1): void;
    public function increment(string $key, int|float $delta = 1, ?string $queue = null): int|float;
}
```

- `advance()` increments the queue's `processed` counter (clamped to `total`) — this is what drives the progress bar.
- `increment()` is an atomic numeric counter over `$global[$key]` (when `$queue === null`) or `$rows[$queue]['meta'][$key]`.
- `transform()` applies a closure atomically via a compare-and-set retry loop. The closure receives the current value (or `null` if unset) and returns the new value. Ideal for `min` / `max` aggregations where the result depends on the current value.
- `get()` returns the stored value or `null`. Use the null-coalescing operator for defaults: `$state->get('credits') ?? 0`.
- `set()` / `compareAndSet()` are the primitives backing the higher-level helpers.

When `concurrent >= 1` the state lives in the parent process and child tasks communicate with it via a Unix-socket RPC (no state files, no extra extensions). When `concurrent: 0` the state lives in memory of the single process.

Back-compat: the second parameter is optional. Single-argument closures `fn (array $task) => …` still work — you just don't get access to `ProgressState`.

Laravel Integration
-------------------

[](#laravel-integration)

The package ships with `ConsoleProgressServiceProvider`, auto-discovered by Laravel. It wires the command output into the dashboard, so you can use `concurrent()` inside any `Artisan` command with no extra configuration:

```
use Illuminate\Console\Command;
use Uncrackable404\ConcurrentConsoleProgress\ProgressState;

use function Uncrackable404\ConcurrentConsoleProgress\concurrent;

class ImportUsers extends Command
{
    protected $signature = 'users:import';

    public function handle(): int
    {
        $tasks = User::cursor()->map(fn ($user) => [
            'queue' => 'users',
            'steps' => 1,
            'id' => $user->id,
        ])->all();

        concurrent(
            queues: ['users' => ['label' => 'Users', 'total' => count($tasks)]],
            tasks: $tasks,
            concurrent: 8,
            process: function (array $task, ProgressState $state): void {
                // your work here
                $state->advance('users', 1);
            },
        );

        return self::SUCCESS;
    }
}
```

Synchronous Mode
----------------

[](#synchronous-mode)

Set `concurrent: 0` to run all tasks sequentially in the same process, bypassing `spatie/fork` entirely. The rendering, state, and fail-fast logic are unchanged — only the execution is serial. Useful when debugging with `dd()`, `dump()`, or `xdebug`:

```
use Uncrackable404\ConcurrentConsoleProgress\ProgressState;

use function Uncrackable404\ConcurrentConsoleProgress\concurrent;

concurrent(
    queues: ['main' => ['label' => 'Processing', 'total' => 1]],
    tasks: [['queue' => 'main', 'steps' => 1]],
    concurrent: 0,
    process: function (array $task, ProgressState $state): void {
        dd($task); // works — same process, no fork
        $state->advance('main', 1);
    },
);
```

`concurrent`Behavior`0`Synchronous — no fork, same process`1`Forked — one child process at a time`N`Forked — up to N child processes in parallelHow It Works
------------

[](#how-it-works)

- **Process isolation.** Each task runs in its own child process via `pcntl_fork` (through `spatie/fork`). A crash in one task does not affect the others, and memory leaks are bounded because every child exits after completion.
- **Parent-owned shared state.** `ProgressState` lives only in the parent. Children talk to it over a Unix-domain socket created in the system temp directory; no state file is ever written to disk. Operations (`get`, `set`, `compareAndSet`, `advance`, …) are synchronous RPC calls.
- **Real-time rendering.** After each state mutation the child wakes the parent with `SIGUSR1`; the parent drains the pending RPC, re-renders the dashboard, and returns. Frame throttling prevents flicker under rapid updates.
- **Atomic aggregations.** `transform()` runs a compare-and-set retry loop against the parent, so `min` / `max` / custom reducers work correctly even with many forked tasks updating the same key concurrently.
- **Fail-fast.** If any task throws, the parent tears down all running children and re-throws a snapshot exception with the original file, line, and trace preserved.

License
-------

[](#license)

The MIT License (MIT).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance94

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~6 days

Total

6

Last Release

29d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9b10a089e03141286a917f0af81dddfd0ed350895da930267f891b57187f12fb?d=identicon)[uncrackable404](/maintainers/uncrackable404)

---

Top Contributors

[![karim-tao](https://avatars.githubusercontent.com/u/30530408?v=4)](https://github.com/karim-tao "karim-tao (10 commits)")

---

Tags

concurrencylaravelphpprogress-barsymfony-console

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/uncrackable404-concurrent-console-progress/health.svg)

```
[![Health](https://phpackages.com/badges/uncrackable404-concurrent-console-progress/health.svg)](https://phpackages.com/packages/uncrackable404-concurrent-console-progress)
```

###  Alternatives

[illuminate/console

The Illuminate Console package.

13045.3M6.1k](/packages/illuminate-console)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
