Utilize Laravel Processes to run PHP code asynchronously, as if using Laravel Concurrency.
Laravel Processes was first introduced in Laravel 10. This library wraps around Process::start()
to let you execute code in the background to achieve async, albeit with some caveats:
- You may only execute PHP code
- Restrictions from
laravel/serializable-closure
apply (see their README) - Hands-off execution: no built-in result-checking, check the results yourself (e.g. via database, file cache, etc)
This library internally uses an Artisan command to run the async code, which is similar to Laravel 11 Concurrency.
This library is very helpful for these cases:
- You want a cross-platform minimal-setup async for easy vertical scaling
- You want to start quick-and-dirty async tasks right now (e.g. prefetching resources, pinging remote, etc.)
- Best is if your task only has very few lines of code
- Laravel 11 Concurrency is too limiting; e.g.:
- You want to do something else while waiting for results
- You want to conveniently limit the max (real) execution time of the concurrent tasks
- And perhaps more!
Of course, if you are considering extreme scaling (e.g. Redis queues in Laravel, multi-worker clusters, etc.) or guaranteed task execution, then this library is obviously not for you.
via Composer:
composer require vectorial1024/laravel-process-async
This library supports Unix and Windows; see the Testing section for more details.
If you are on Unix, check that you also have the following:
- GNU Core Utilities (
coreutils
)- MacOS do
brew install coreutils
! - Other Unix distros should check if
coreutils
is preinstalled
- MacOS do
Please see CHANGELOG.md
.
Tasks can be defined as PHP closures, or (recommended) as an instance of a class that implements AsyncTaskInterface
.
A very simple example task to write Hello World to a file:
// define the task...
$target = "document.txt";
$task = new AsyncTask(function () use ($target) {
file_put_contents($target, "Hello World!");
});
// if you are using interfaces, then it is just like this:
// $task = new AsyncTask(new WriteToFileTask($target, $message));
// then start it.
$task->start();
// the task is now run in another PHP process, and will not report back to this PHP process.
You can set task time limits before you start them, but you cannot change them after the tasks are started. When the time limit is reached, the async task is killed.
The default time limit is 30 real seconds. You can also choose to not set any time limit, in this case the (CLI) PHP max_execution_time
directive will control the time limit.
Note: AsyncTaskInterface
contains an implementable method handleTimeout
for you to define timeout-related cleanups (e.g. write to some log that the task has timed out). This method is still called when the PHP max_execution_time
directive is triggered.
// start with the default time limit...
$task->start();
// start task with a different time limit...
$task->withTimeLimit(15)->start();
// ...or not have any limits at all (beware of orphaned processes!)
$task->withoutTimeLimit()->start();
Some tips:
- Don't sleep too long! On Windows, timeout handlers cannot trigger while your task is sleeping.
- Use short but frequent sleeps instead.
- Avoid using
SIGINT
! On Unix, this signal is reserved for timeout detection.
PHPUnit via Composer script:
composer run-script test
Latest cross-platform testing results:
Runtime | MacOS | Ubuntu | Windows |
---|---|---|---|
Laravel 10 (PHP 8.1) | skipped* | skipped* | skipped* |
Laravel 11 (PHP 8.2) | |||
Laravel 12 (PHP ???) | 🛠️ | 🛠️ | 🛠️ |
*Note: tests for these Laravel versions are skipped because they have old artisan
file contents:
- It is difficult to mock multi-version
artisan
files for different Laravel versions (see #6). - It is rare for the
artisan
file at Laravel to be updated - The actual behavior is expected to be the same.