How to gracefully stop a Laravel CLI command
Last week I was building a console command on top of Laravel that could be running for a long time. It was a long-running process that runs a queue worker. It reads from a queue, does some processing on the data and, then forward it to another queue. And the important bit: the item that is currently processed by the worker should be finished before the worker can quit. Here’s how I did that.
Overview of the CLI command
First let’s start with what the actual worker call looks like. It’s a single while loop:
This loop will run forever and each call to
Worker::work() does one iteration of the read -> process -> queue sequence.
The entire Laravel CLI command will look like this:
Handling a CLI command’s exit signals
Say we run this command using
php artisan my-worker and we want to stop it, there are two common ways to do so:
- CTRL+C on the terminal — This will send an interrupt signal (SIGINT) to the process
kill [process id]- This will send a termination signal (SIGTERM) to the process
What we need to do is make our script so that it receives these signals and let it break out of our
while loop when one occurs. To handle these signals in PHP we need to do the following:
- Depending on your PHP version:
1. PHP 7.0 and before: Configure our PHP script to handle so-called “ticks”. Without this we can’t listen to the signals.
2. PHP 7.1 and later: enable async pcntl signals
- Set handler function to SIGINT and SIGTERM.
- Tell the loop to stop when the signals are received
Let’s start with how we can stop the while loop. First, we need a variable that tells whether or not the loop should be running. We can create a class-level field for this called
run and we set it to
true by default. When set to false, the while loop will stop running and the program is stopped.
Update 13–11–2018: on PHP 7.1 and later it’s more efficient to enable asyncronous signal handling by calling
declare(ticks = 1).
See the full code below:
We’ve learned to handle process signals in a Laravel CLI command and in PHP in general. With the help of
pcntl_signal and a shutdown function we took control of how our PHP script exits, giving the ability to clean up what's going on in the script.