Reading Time: 3 minutes
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:
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
- Run
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.
Then we need to tell PHP to handle ticks using declare(ticks = 1)
, and register two signal handlers using pcntl_signal()
.
Update 13–11–2018: on PHP 7.1 and later it’s more efficient to enable asyncronous signal handling by calling
pcntl_async_signals(true)
instead ofdeclare(ticks = 1).
See the full code below:
Conclusion
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.