I have a PHP command line app with a custom shutdown handler:
<?php
declare(ticks=1);
$shutdownHandler = function () {
echo 'Exiting';
exit();
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
If I kill the script with Ctrl+C while a CURL request is in progress, it has no effect. The command just hangs. If I remove my custom shutdown handler, Ctrl+C kills the CURL request immediately.
Why is CURL unkillable when I define a SIGINT
handler?
What really seems to work is giving the whole thing some space to work its signal handling magic. Such space seems to be provided by enabling cURL's progress handling, while also setting a "userland" progress callback:
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_NOPROGRESS, false); // "true" by default
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
usleep(100);
});
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
Seems like there needs to be "something" in the progress callback function. Empty body does not seem to work, as it probably just doesn't give PHP much time for signal handling (hardcore speculation).
Putting pcntl_signal_dispatch()
in the callback seems to work even without declare(ticks=1);
on PHP 7.1
.
...
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {
pcntl_signal_dispatch();
});
...
Using pcntl_async_signals(true)
instead of declare(ticks=1);
works even with empty progress callback function body.
This is probably what I, personally, would use, so I'll put the complete code here:
<?php
pcntl_async_signals(true);
$shutdownHandler = function() {
die("Exiting\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function() {});
curl_setopt($ch, CURLOPT_URL, 'http://blackhole.webpagetest.org');
curl_exec($ch);
curl_close($ch);
}
All these three solutions cause the PHP 7.1
to quit almost instantly after hitting CTRL+C
.
When you send the Ctrl + C
command, PHP
tries to finish the current action before exiting.
SEE FINAL THOUGHTS AT THE END FOR A MORE DETAILED EXPLANATION
Your code does not exit because cURL
doesn't finish, so PHP
cannot exit until it finishes the current action.
The website you've chosen for this exercise never loads.
To fix, replace the URL with something that does load, like https://google.com, for instance
I wrote my own code sample to show me exactly when/where PHP
decides to exit:
<?php
declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
exit("EXITING NOW\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
print "$t\n";
$t++;
}
When running this in the terminal, you get a much clearer idea of how PHP is operating:
In the image above, you can see that when I issue the SIGINT command via Ctrl + C
(shown by the arrow), it finishes up the action it is doing, then exits.
This means that if my fix is correct, all it should take to kill curl in OP's code, is a simple URL
change:
<?php
declare(ticks=1);
$t = 1;
$shutdownHandler = function () {
exit("\nEXITING\n");
};
pcntl_signal(SIGINT, $shutdownHandler);
while (true) {
echo "CURL$t\n";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://google.com');
curl_exec($ch);
curl_close($ch);
}
And then running:
Viola! As expected, the script terminated after the current process was finished, like its supposed to.
The site you're attempting to curl is effectively sending your code on a trip that has no end. The only actions capable of stopping the process are CTRL + X
, the max execution time setting, or the CURLOPT_TIMEOUT
cURL option. The reason CTRL+C
works when you take OUT pcntl_signal(SIGINT, $shutdownHandler);
is because PHP no longer has the burden of graceful shutdown by an internal function. Since PHP isn't concurrent, when you do have the handler in, it has to wait its turn before it is executed - which it will never get because the cURL
task will never finish, thus leaving you with the never-ending results.
I hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With