spatie/fork
Run PHP code concurrently using lightweight process forking. Define multiple closures and execute them in parallel, collecting results in order. Requires PHP 8 with pcntl (CLI only) and posix extensions on Unix-like systems.
Installation:
composer require spatie/fork
No additional configuration is required—just require the package in your project.
First Use Case: Run a simple concurrent task:
use Spatie\Fork\Fork;
$fork = Fork::create(function () {
// Long-running or blocking task (e.g., API calls, file processing)
sleep(2);
echo "Forked task completed\n";
});
$fork->run(); // Runs in a separate process
echo "Main process continues\n";
Where to Look First:
Spatie\Fork\Fork class for core functionality.Spatie\Fork\Exceptions for error handling.Fire-and-Forget Tasks: Use for non-critical, long-running operations (e.g., sending emails, generating reports).
Fork::create(function () {
Mail::to('user@example.com')->send(new WelcomeEmail());
})->run();
Parallel Processing: Execute multiple tasks concurrently:
$forks = collect(range(1, 5))->map(fn ($i) =>
Fork::create(fn () => processItem($i))->run()
);
Resource-Intensive Workloads: Offload CPU-heavy tasks (e.g., image resizing, data transformations):
Fork::create(function () {
$image = Image::make('large.jpg')->resize(800, 600)->save('small.jpg');
})->run();
Integration with Queues: Use alongside Laravel queues for hybrid concurrency:
Fork::create(function () {
dispatch(new ProcessDataJob());
})->run();
Error Handling:
Wrap Fork execution in try-catch to handle failures gracefully:
try {
$fork->run();
} catch (\Spatie\Fork\Exception $e) {
Log::error("Fork failed: " . $e->getMessage());
}
Synchronization:
Use Fork::wait() to block until all forks complete (if needed):
$forks = collect([...])->map(...);
foreach ($forks as $fork) $fork->run();
Fork::wait(); // Blocks until all forks finish
Environment Awareness:
Avoid forking in environments where it’s unsafe (e.g., shared hosting). Check app()->runningInConsole() or environment variables.
Logging: Redirect fork output to Laravel logs:
Fork::create(function () {
Log::info("Task running in fork");
})->run();
Configuration:
Adjust PHP settings (e.g., max_execution_time, memory_limit) if forks fail due to resource constraints.
Testing:
Mock Fork in tests using partialMock:
$mock = $this->partialMock(Fork::class, ['run']);
$mock->shouldReceive('run')->once();
State Sharing:
Resource Limits:
memory_limit and max_execution_time. Exceeding these may silently fail.ini_set() in the forked closure (if allowed by your environment):
Fork::create(function () {
ini_set('memory_limit', '512M');
// Task here
})->run();
Blocking Operations:
Windows Compatibility:
pcntl_fork, which is Unix-only).Signal Handling:
SIGTERM) gracefully.Output:
Redirect STDOUT/STDERR to a file for debugging:
Fork::create(function () {
file_put_contents('fork.log', print_r(['debug' => true], true), FILE_APPEND);
})->run();
Process Inspection:
Use ps aux | grep php (Linux/macOS) to check for zombie forks if tasks hang.
Timeouts: Set a timeout for the forked process:
$fork = Fork::create(fn () => sleep(10));
$fork->run();
if (!$fork->isFinished()) {
$fork->terminate(); // Force-kill if needed
}
Idempotency: Design forked tasks to be idempotent (retry-safe) since failures may go unnoticed.
Environment Checks: Disable forking in non-Unix environments:
if (!Fork::isSupported()) {
Log::warning("Forking not supported; falling back to synchronous execution.");
// Fallback logic
}
Performance Tuning:
$semaphore = new \Slim\Semaphore(5); // Max 5 concurrent forks
$semaphore->acquire();
Fork::create(fn () => $semaphore->release())->run();
Extension Points:
Spatie\Fork\Fork to add pre/post-fork hooks:
class CustomFork extends Fork {
public function run() {
$this->logStart();
parent::run();
$this->logEnd();
}
}
Alternatives:
laravel-horizon (for queues) or reactphp (for async I/O) if forking is too restrictive.How can I help you explore Laravel packages today?