dalehurley/process-manager

A lightweight parallel process runner for PHP. Execute multiple scripts concurrently with configurable parallelism, timeouts, and result tracking.

Fund package maintenance!
dalehurley

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 17

Watchers: 3

Forks: 14

Open Issues: 0

pkg:composer/dalehurley/process-manager

v2.1.0 2025-12-05 11:25 UTC

This package is auto-updated.

Last update: 2025-12-05 11:27:39 UTC


README

Latest Version on Packagist Total Downloads PHP Version License Tests

A lightweight parallel process runner for PHP. Execute multiple scripts or commands concurrently with configurable parallelism, timeouts, and real-time output tracking.

Sequential execution:        Parallel execution (3 concurrent):
┌────┐┌────┐┌────┐┌────┐┌────┐┌────┐    ┌────┐┌────┐
│ T1 ││ T2 ││ T3 ││ T4 ││ T5 ││ T6 │    │ T1 ││ T4 │
│ 5s ││ 5s ││ 5s ││ 5s ││ 5s ││ 5s │    │ 5s ││ 5s │
└────┘└────┘└────┘└────┘└────┘└────┘    ├────┤├────┤
         Total: 30 seconds              │ T2 ││ T5 │
                                        │ 5s ││ 5s │
                                        ├────┤├────┤
                                        │ T3 ││ T6 │
                                        │ 5s ││ 5s │
                                        └────┘└────┘
                                        Total: 10 seconds

Overview

What is it?

PHP Process Manager is a concurrent task runner that spawns and manages multiple OS processes simultaneously. Instead of running tasks one after another (sequential), it executes them in parallel—dramatically reducing total execution time for batch operations.

Think of it as a simple process pool or worker spawner: you queue up scripts, set a concurrency limit, and the manager handles execution, monitoring, timeouts, and result collection.

Who is it for?

Audience Use Case
Backend developers Batch processing, data imports/exports, scheduled jobs
DevOps engineers Deployment scripts, server maintenance, multi-host operations
Data engineers ETL pipelines, file processing, API data collection
QA engineers Parallel test execution, load testing preparation
System administrators Bulk operations, log processing, backup scripts

Why use it?

PHP is single-threaded by default. When you have independent tasks, running them sequentially wastes time:

Scenario Sequential Parallel (5 workers) Speedup
10 API calls × 2s each 20s ~4s
100 file imports × 1s each 100s ~20s
50 email sends × 0.5s each 25s ~5s

This package is ideal when you need to:

  • Run the same script multiple times with different inputs
  • Execute multiple independent scripts as part of a workflow
  • Process batches of work faster by parallelising
  • Add timeout protection to unreliable external calls
  • Limit concurrency to avoid overwhelming external services

When to use it (and when not to)

Good fit:

  • Tasks are independent and don't share state
  • Each task can run as a separate PHP script or CLI command
  • You need timeout protection for unreliable tasks
  • You want to limit concurrency (e.g., max 5 API calls at once)
  • Tasks take seconds to minutes to complete

Consider alternatives for:

  • Tasks requiring shared memory or real-time inter-process communication → use parallel extension
  • Web request handling with high throughput → use a message queue (Redis, RabbitMQ, SQS)
  • Sub-second task spawning at high frequency → process overhead becomes significant
  • Long-running daemon processes → use Supervisor or systemd instead

Real-World Use Cases

1. Batch Data Import

Import thousands of records by processing files in parallel:

$manager = new ProcessManager(executable: 'php', maxConcurrentProcesses: 5);

foreach (glob('/data/imports/*.csv') as $file) {
    $manager->addScript('import-worker.php', arguments: [$file], maxExecutionTime: 300);
}

$results = $manager->run();
echo "Imported " . count(array_filter($results, fn($r) => $r->wasSuccessful)) . " files\n";

2. Multi-API Data Collection

Fetch data from multiple APIs simultaneously:

$endpoints = ['users', 'orders', 'products', 'inventory', 'analytics'];

$manager = new ProcessManager(executable: 'php', maxConcurrentProcesses: 3);

foreach ($endpoints as $endpoint) {
    $manager->addScript('fetch-api.php', arguments: [$endpoint], maxExecutionTime: 60);
}

$results = $manager->run(); // All 5 endpoints fetched in ~2 batches instead of 5 sequential calls

3. Image/Video Processing Pipeline

Process media files in parallel using CLI tools:

$manager = new ProcessManager(executable: 'ffmpeg', maxConcurrentProcesses: 4);

foreach ($videoFiles as $video) {
    $manager->addScript("-i {$video} -vf scale=1280:720 output/{$video}", maxExecutionTime: 600);
}

$manager->run();

4. Database Migration Runner

Run independent migrations concurrently:

$manager = new ProcessManager(executable: 'php', maxConcurrentProcesses: 3);

$manager->addScripts([
    ['script' => 'migrate-users.php', 'maxExecutionTime' => 300],
    ['script' => 'migrate-orders.php', 'maxExecutionTime' => 600],
    ['script' => 'migrate-products.php', 'maxExecutionTime' => 300],
    ['script' => 'migrate-analytics.php', 'maxExecutionTime' => 900],
]);

$results = $manager->run();

5. Parallel Test Execution

Run test suites faster:

$manager = new ProcessManager(executable: 'php', maxConcurrentProcesses: 4);

foreach (glob('tests/*Test.php') as $testFile) {
    $manager->addScript('vendor/bin/phpunit', arguments: [$testFile], maxExecutionTime: 120);
}

$results = $manager->run();
$failed = array_filter($results, fn($r) => !$r->wasSuccessful);

exit(count($failed) > 0 ? 1 : 0);

6. Multi-Server Deployment

Deploy to multiple servers simultaneously:

$servers = ['web1.example.com', 'web2.example.com', 'web3.example.com'];

$manager = new ProcessManager(executable: 'ssh', maxConcurrentProcesses: 10);

foreach ($servers as $server) {
    $manager->addScript("{$server} 'cd /app && git pull && composer install'", maxExecutionTime: 120);
}

$results = $manager->run();

Features

  • 🚀 Concurrent Execution - Run multiple processes in parallel
  • ⏱️ Timeout Management - Automatically kill processes that exceed time limits
  • 📊 Result Tracking - Get detailed results for each process (exit codes, output, timing)
  • 🎨 Flexible Output - Console, HTML, or custom output handlers
  • 🔧 Fluent API - Chain configuration methods for clean setup
  • 🏷️ Fully Typed - PHP 8.2+ with strict typing and readonly classes

Requirements

  • PHP 8.2 or higher
  • proc_open function enabled

Installation

Via Composer

composer require dalehurley/process-manager

Manual Installation

Clone the repository and include the autoloader:

git clone https://github.com/dalehurley/PHP-Process-Manager.git
cd PHP-Process-Manager
composer install

Quick Start

<?php

use DaleHurley\ProcessManager\ProcessManager;
use DaleHurley\ProcessManager\Output\ConsoleOutputHandler;

$manager = new ProcessManager(
    executable: 'php',
    workingDirectory: '/path/to/scripts',
    maxConcurrentProcesses: 3,
    sleepInterval: 1,
    outputHandler: new ConsoleOutputHandler()
);

// Add scripts to the queue
$manager->addScript('task1.php', maxExecutionTime: 60);
$manager->addScript('task2.php', maxExecutionTime: 30);
$manager->addScript('task3.php', maxExecutionTime: 120);

// Execute and get results
$results = $manager->run();

foreach ($results as $result) {
    echo "{$result->script}: " . ($result->wasSuccessful ? 'SUCCESS' : 'FAILED') . "\n";
}

Usage

Basic Configuration

use DaleHurley\ProcessManager\ProcessManager;

// Constructor parameters
$manager = new ProcessManager(
    executable: 'php',              // Command to execute
    workingDirectory: './scripts',  // Directory containing scripts
    maxConcurrentProcesses: 5,      // Max parallel processes
    sleepInterval: 1                // Seconds between status checks
);

Fluent API

$manager = new ProcessManager();

$manager
    ->setExecutable('python')
    ->setWorkingDirectory('/path/to/scripts')
    ->setMaxConcurrentProcesses(10)
    ->setSleepInterval(2)
    ->setOutputHandler(new ConsoleOutputHandler());

Adding Scripts

// Add a single script with default timeout (300 seconds)
$manager->addScript('worker.php');

// Add with custom timeout
$manager->addScript('long-task.php', maxExecutionTime: 600);

// Add with arguments and environment variables
$manager->addScript(
    script: 'process-data.php',
    maxExecutionTime: 120,
    arguments: ['--batch', '100'],
    environment: ['DEBUG' => '1', 'LOG_LEVEL' => 'verbose']
);

// Add multiple scripts at once
$manager->addScripts([
    'task1.php',
    'task2.php',
    ['script' => 'task3.php', 'maxExecutionTime' => 60],
]);

Output Handlers

The package includes several output handlers:

use DaleHurley\ProcessManager\Output\ConsoleOutputHandler;
use DaleHurley\ProcessManager\Output\HtmlOutputHandler;
use DaleHurley\ProcessManager\Output\NullOutputHandler;

// Console output with colors (for CLI)
$manager->setOutputHandler(new ConsoleOutputHandler(useColors: true));

// HTML output (for web)
$manager->setOutputHandler(new HtmlOutputHandler(flush: true));

// No output (silent mode - default)
$manager->setOutputHandler(new NullOutputHandler());

Custom Output Handler

Implement the OutputHandlerInterface for custom output:

use DaleHurley\ProcessManager\Output\OutputHandlerInterface;

class LogOutputHandler implements OutputHandlerInterface
{
    public function __construct(private Logger $logger) {}

    public function scriptAdded(string $script): void
    {
        $this->logger->info("Queued: {$script}");
    }

    public function scriptCompleted(string $script): void
    {
        $this->logger->info("Completed: {$script}");
    }

    public function scriptKilled(string $script): void
    {
        $this->logger->warning("Killed: {$script}");
    }

    public function info(string $message): void
    {
        $this->logger->info($message);
    }

    public function error(string $message): void
    {
        $this->logger->error($message);
    }
}

Working with Results

$results = $manager->run();

foreach ($results as $result) {
    // Access result properties
    echo "Script: {$result->script}\n";
    echo "Exit Code: {$result->exitCode}\n";
    echo "Duration: {$result->elapsedTime}s\n";
    echo "Success: " . ($result->wasSuccessful ? 'Yes' : 'No') . "\n";
    echo "Killed: " . ($result->wasKilled ? 'Yes' : 'No') . "\n";

    if ($result->output) {
        echo "Output: {$result->output}\n";
    }

    if ($result->hasErrors()) {
        echo "Errors: {$result->errorOutput}\n";
    }

    // Convert to array
    $data = $result->toArray();
}

// Analyze results
$successful = array_filter($results, fn($r) => $r->wasSuccessful);
$failed = array_filter($results, fn($r) => !$r->wasSuccessful);
$killed = array_filter($results, fn($r) => $r->wasKilled);

API Reference

ProcessManager

Method Description
setExecutable(string $executable) Set the command to execute
setWorkingDirectory(string $path) Set the working directory
setMaxConcurrentProcesses(int $count) Set max parallel processes
setSleepInterval(int $seconds) Set interval between status checks
setOutputHandler(OutputHandlerInterface $handler) Set the output handler
addScript(string $script, ...) Add a script to the queue
addScripts(array $scripts) Add multiple scripts
getQueueCount() Get number of queued scripts
getRunningCount() Get number of running processes
clearQueue() Clear the script queue
run() Execute all queued scripts

ProcessResult

Property Type Description
script string Script name
exitCode int Process exit code
output string stdout content
errorOutput string stderr content
elapsedTime int Execution time in seconds
wasKilled bool Whether process was killed
wasSuccessful bool Whether process succeeded

Upgrading from v1.x

The 2.0 release is a complete rewrite with breaking changes:

// Old (v1.x)
$manager = new Processmanager();
$manager->executable = "php";
$manager->root = "";
$manager->processes = 3;
$manager->show_output = true;
$manager->addScript("script.php", 300);
$manager->exec();

// New (v2.x)
use DaleHurley\ProcessManager\ProcessManager;
use DaleHurley\ProcessManager\Output\ConsoleOutputHandler;

$manager = new ProcessManager(
    executable: 'php',
    workingDirectory: '',
    maxConcurrentProcesses: 3,
    outputHandler: new ConsoleOutputHandler()
);
$manager->addScript('script.php', maxExecutionTime: 300);
$results = $manager->run();

Key Changes

  • Namespace: DaleHurley\ProcessManager
  • Class renamed: ProcessmanagerProcessManager
  • Method renamed: exec()run()
  • Property renamed: rootworkingDirectory
  • Property renamed: processesmaxConcurrentProcesses
  • Output handling now uses dedicated handler classes
  • Returns detailed ProcessResult objects instead of void

Testing

Run the test suite with PHPUnit:

composer test

Run static analysis with PHPStan (level 8):

composer analyse

Alternatives

This package is intentionally simple and lightweight. Depending on your needs, consider these alternatives:

For More Complex Process Management

Package Description Best For
symfony/process Full-featured process component Single process with advanced I/O handling
spatie/async Asynchronous process handling with Pool Similar use case with event-driven API
amphp/parallel True parallel execution with workers High-performance async applications

For Queue-Based Processing

Package Description Best For
Laravel Queues Queue system with multiple backends Laravel applications, distributed workers
Symfony Messenger Message bus with queue transport Symfony applications, event-driven
php-enqueue Framework-agnostic queue abstraction Multi-backend queue support
Beanstalkd + Pheanstalk Lightweight job queue Simple job queuing

For True Multi-Threading

Package Description Best For
parallel PHP extension for parallel execution Shared memory, true threading (requires ZTS PHP)
pthreads Threading extension (PHP 7 only) Legacy threading needs

When to Choose This Package

Choose PHP Process Manager when you need:

  • ✅ Simple, zero-dependency process spawning
  • ✅ Quick setup without infrastructure (no Redis, no queue server)
  • ✅ Timeout management built-in
  • ✅ Result collection from all processes
  • ✅ CLI script orchestration
  • ✅ Lightweight alternative to full queue systems

Choose alternatives when you need:

  • ❌ Persistent job storage and retry logic → use queues
  • ❌ Distributed processing across servers → use message queues
  • ❌ Shared memory between tasks → use parallel extension
  • ❌ Web-scale throughput → use dedicated worker systems

License

MIT License. See LICENSE for details.

Credits

  • Original concept by Matou Havlena (havlena.net)
  • Modernized by Dale Hurley (dalehurley.com)