I'm trying to catch signals in terminal. I know that I can use pcntl_signal() function but it doesn't work for me.
I am trying with this code:
public function handle() {
pcntl_signal(SIGINT, function ($signo) {
echo "CATCH!\n";
exit;
});
while (true) { echo("!\n"); sleep(2); }
}
When I press Ctrl+C I don't see any result, because program still is working. What do I wrong?
I want to stop program after press Ctrl+C and call __destruct() method.
The pcntl_signal() function won't work without this line in your file:
declare(ticks = 1);
It should probably be at the top of the file, before the class definition.
A tick is an event that occurs regularly during the program execution, and in this case each tick triggers the program to check the signals coming in. I think. I don't know why it's not enabled by default, possibly due to increased CPU load while it's in operation?
I've seen some assertions that ticks were deprecated after PHP 5.3, but I just tested in PHP 7.0.9 and can confirm that pcntl_signal() doesn't work without it.
This can be done with the Laravel SignalableCommandInterface now
class MyCommand extends Command implements SignalableCommandInterface
{
public function handleSignal(int $signal): void
{
echo "CATCH!\n";
exit;
}
// Which signals will be handled
public function getSubscribedSignals(): array
{
return [SIGINT, SIGTERM];
}
}
Related
Unfortunately calling php exit() on php-fpm/nginx configuration does not stop the script immediately while file handles might not be closed.
Some developers suggest calling fastcgi_finish_request() but also this does not stop the script.
Others suggest wrapping all code in a catch block:
<?php
class SystemExit extends Exception {}
try {
/* PUT ALL THE CODE HERE */
} catch (SystemExit $e) { /* do nothing */ }
?>
and throwing an exception where code stop is needed:
if (SOME_EXIT_CONDITION)
throw new SystemExit(); // instead of exit()
This would mean editing all php files to include try/catch block and seems tedious to me.
Are there any other clean solutions?
So we found out that it's a register_shutdown_function callback that prevents your script from exiting immediately.
PHP shutdown function is designed to be called on any script shutdown when possible. But it has a feature: if one of shutdown callbacks calls exit — script is exiting without calling any other callbacks.
So if you really want to skip a shutdown function in some cases, you should register some killer-function as a very first shutdown callback. Inside that killer-function you will check a kind of singleton for state: do we want to exit? — call exit(), otherwise — return.
<?php
function killer_function() {
if ($someGlobalThing->needToExitRightNow()) {
exit();
}
}
register_shutdown_function('killer_function');
// ...
function exit_now() {
$someGlobalThing->exitNow();
exit();
}
($someGlobalThing can be a singleton or some super-global variable or a global registry or whatever you like)
Then calling exit_now() will do the trick.
I have this function I want to test looking like this:
class Logger {
function error($msg){
if (is_string($msg)){
error_log($msg);
die($msg);
} elseif (is_object($msg)){
error_log($msg.' '.$msg->getTraceAsString());
die('exception');
} else {
var_dump($msg);
die('error');
}
}
I want to test this function without logging the $msg. Is there a way to determine if error_log works without logging? I tried using setExpectedException but I wasn't able to catch the error and it kept logging.
The obvious answer is a simple alias/proxy-function that itself called error_log in the Logger class (which can be easily mocked, and checked to see what is set to it),
To actually test the native error_log function however (without a proxy in the original class), can be done with namespaces. The test would end up defined to be the same namespace as the original code, and then after the test class, add a function - in this case error_log() - but that function is also defined in the namespace - and so would be run in preference to the root-namespace-equivalent from the native functions.
Unfortunately, you can't do the same overriding with die (or its alias, exit). They are 'language constructs', and cannot be overridden like error_log can.
<?php
namespace abc;
use abc\Logger;
class ThreeTest extends \PHPUnit_Framework_TestCase
{
public function setUp() { $this->l = new Logger(); }
// test code to exercise 'abc\Logger'
}
// Now define a function, still inside the namespace '\abc'.
public function error_log($msg)
{
// this will be called from abc\Logger::error
// instead of the native error_log() function
echo "ERR: $msg, ";
}
you can use a function-mocking framework like php-mock (there are others as well) to mock the call to error_log (and check whether it is called with your expected parameters).
Unfortunately you will not be able to use that for the die-construct as that is not a normal function but anlanguage construct.
I'd replace the die() with a 'throw new \Exception()' (or any other appropriate exception) as you can then
test for the thrown exception and
can decide in your programming whether execution shall be stopped on calling the logger or whether you want to go on by wrapping the call into a try/catch
But I'd also ask myself whether the execution has to stop when calling a logger
Capturing error_log() output in a variable
If you want to redirect the error_log() output in a way that lets you inspect it with PHPUnit assertions, the following code works for me:
$errorLogTmpfile = tmpfile();
$errorLogLocationBackup = ini_set('error_log', stream_get_meta_data($errorLogTmpfile)['uri']);
error_log("Test for this message");
ini_set('error_log', $errorLogLocationBackup);
$result = stream_get_contents($errorLogTmpfile);
// Result: [11-May-2022 22:27:08 UTC] Test for this message
As you can see, it uses a temporary file to collect the output, then grabs the content into a variable and resets the error_log config.
Re-usable methods
Personally, I've organized this into a pair of methods that I inject into the PHPUnit object with a trait so I can re-use them.
Of course the code below won't work out of the box, but it serves to demonstrate how you can make this system re-usable:
trait WithWPTestCaseGeneralTools {
var $gvErrorLogLocationBackup = "";
var $gvErrorLogTmpfile = "";
public function gvErrorLogStartListening() {
$this->gvErrorLogTmpfile = tmpfile();
$streamUri = stream_get_meta_data($this->gvErrorLogTmpfile)['uri'];
$this->gvErrorLogLocationBackup = ini_set('error_log', $streamUri);
}
public function gvErrorLogGetContents() {
ini_set('error_log', $this->gvErrorLogLocationBackup);
return stream_get_contents($this->gvErrorLogTmpfile);
}
}
You could of course achieve the same things with a couple of functions that use globals, I'll leave that to you if it's what you need!
Developing a wordpress blog locally, my PHP server is dying with SIGSEGV half-way through rendering the page.
Here's my startup script: sudo php -S 127.0.0.1:80 -t ../reece-blog-local
I wish I could provide more details, but I can't seem to get any information out of PHP about this. The only reason I know it's exiting with SIGSEGV is that I'm running this process with foreman, otherwise the process just exits silently.
There does not seem to be a particular line of code that's making it break, I can track down the area of the error based on the HTML that's managed to render before exit, and I can stick in a few echo "Hello, world!"s and it breaks on any given one of them.
Notes:
server was working fine until I upgraded to Mavericks, so thinking that something there could be interfering.
confirmed this happens when CURLing any URL, so likely rules out this being an issue with serving static files.
not exhausting my memory limit
How can I further debug this issue, in lieu of any specific errors output by PHP?
PHP server will exit with segmentation fault (SIGSEGV) error in the event of infinite recursion, e.g.:
function test() {
test();
}
If the output is buffered and you use echo to debug an error like this, the error may appear to occur in strange places (hence why it would seem to break on a line like echo "Hello, world!";)
Instead, you can use error_log($yourMessage, 4); to output a message to the console. This helped track down the error for me (though from reading around it seems XDebug may be the preferred solution for finding errors like this.)
For what it's worth, here's effectively how I had infinite recursion:
class TheParent {
public function __call($method, $args) {
return call_user_func_array(array($this, $method), $args);
}
}
class TheChild extends TheParent {
protected function thisIsOkay() {
echo 'yay';
}
private function sigsegv() {
echo 'no';
}
}
$c = new TheChild;
$c->thisIsOkay();
$c->sigsegv();
If we attempt to call a private function in TheChild, __call will be invoked, attempt to access the private function, invoking __call, etc.
I have a Symfony Console command that iterates over a potentially big collection of items and does a task with each of them. Since the collection can be big, the command can take a long time to run (hours). Once the command finishes, it displays some statistics.
I'd like to make it possible to abort the command in a nice way. Right now if I abort it (ie with ctrl+c in the CLI), there is no statistics summary and no way to output the parameters needed to resume the command. Another issue is that the command might be terminated in the middle of handling an item - it'd be better if it could only terminate in between handling items.
So is there a way to tell a command to "abort nicely as soon as possible", or have the ctrl+c command be interpreted as such?
I tried using the ConsoleEvents::TERMINATE event, though the handlers for this only get fired on command completion, not when I ctrl+c the thing. And I've not been able to find further info on making such resumable commands.
This is what worked for me. You need to call pcntl_signal_dispatch before the signal handlers are actually executed. Without it, all tasks will finish first.
<?php
use Symfony\Component\Console\Command\Command;
class YourCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
pcntl_signal(SIGTERM, [$this, 'stopCommand']);
pcntl_signal(SIGINT, [$this, 'stopCommand']);
$this->shouldStop = false;
foreach ( $this->tasks as $task )
{
pcntl_signal_dispatch();
if ( $this->shouldStop ) break;
$task->execute();
}
$this->showSomeStats($output);
}
public function stopCommand()
{
$this->shouldStop = true;
}
}
You should take a look at RabbitMqBundle's signal handling. Its execute method just links some callbacks via the pcntl_signal() function call. A common case should look pretty much like this:
<?php
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand as Command;
class YourCommand extends Command
{
protected function execute(InputInterface $input, OutputInterface $output)
{
pcntl_signal(SIGTERM, array(&$this, 'stopCommand', $output));
pcntl_signal(SIGINT, array(&$this, 'stopCommand', $output));
pcntl_signal(SIGHUP, array(&$this, 'restartCommand', $output));
// The real execute method body
}
public function stopCommand(OutputInterface $output)
{
$output->writeln('Stopping');
// Do what you need to stop your process
}
public function restartCommand(OutputInterface $output)
{
$output->writeln('Restarting');
// Do what you need to restart your process
}
}
The answers are more complex than they need to be. Sure, you can register POSIX signal handlers, but if the only signals that need to be handled are basic interrupts and the like, you should just define a destructor on the Command.
class YourCommand extends Command
{
// Other code goes here.
__destruct()
{
$this->shouldStop = true;
}
}
A case where you would want to register a POSIX signal is for the SIGCONT signal, which can handle the resumption of a process that was stopped (SIGSTOP).
Another case would be where you want every signal to behave differently; for the most part, though, SIGINT and SIGTERM and a handful of others would be registered with the same "OMG THE PROCESS HAS BEEN KILLED" operation.
Aside from these examples, registering signal events is unnecessary. This is why destructors exist.
You can even extend Symfony's base Command class with a __destruct method, which would automatically provide cleanup for every command; should a particular command require additional operations, just overwrite it.
Essentially I have a method of a class called killProgram, which is intended to send a hTTP redirect and then kill PHP.
How am I supposed to test this? When I run phpunit it doesn't return anything for that test, and closes completely.
Right now I'm considering having the killProgram function throw an exception which shouldn't get handled, which would allow me to assert that an exception was thrown.
Is there a better way?
It's obviously an old question but my suggestion would be to move the code that die()'s into a separate method that you can then mock.
As an example, instead of having this:
class SomeClass
{
public function do()
{
exit(1);
// or
die('Message');
}
}
do this:
class SomeClass
{
public function do()
{
$this->terminate(123);
// or
$this->terminate('Message');
}
protected function terminate($code = 0)
{
exit($code);
}
// or
protected function terminate($message = '')
{
die($message);
}
}
That way you can easily mock the terminate method and you don't have to worry about the script terminating without you being able to catch it.
Your test would look something like this:
class SomeClassTest extends \PHPUnit_Framework_TestCase
{
/**
* #expectedExceptionCode 123
*/
public function testDoFail()
{
$mock = $this->getMock('SomeClass');
$mock->expects($this->any())
->method('terminate')
->will($this->returnCallback(function($code) {
throw new \Exception($code);
}));
// run to fail
$mock->do();
}
}
I haven't tested the code but should be pretty close to a working state.
As every tests are run by the same PHPUnit process, if you use exit/die in your PHP code, you will kill everything -- as you noticed ^^
So, you have to find another solution, yes -- like returning instead of dying ; or throwing an exception (you can test if some tested code has thrown an expected exception).
Maybe PHPUnit 3.4 and it's --process-isolation switch (see Optionally execute each test using a separate PHP process) might help (by not having everything dying), but you still wouldn't be able to get the result of the test, if PHPUnit doesn't get the control back.
I've had this problem a couple of times ; solved it by returning instead of dying -- even returning several times, if needed, to go back "high enough" in the call stack ^^
In the end, I suppose I don't have any "die" anymore in my application... It's probably better, when thinking about MVC, btw.
There's no need to change the code just to be able to test it, you can simply use set_exit_overload() (provided by test_helpers from same author as PHPUnit).
I realise you've already accepted an answer for this and it's an old question, but I figure this might be useful for someone, so here goes:
Instead of using die(), you could use throw new RuntimeException() (or an exception class of your own), which will also halt program execution (albeit in a different fashion) and use PHPUnit's setExpectedException() to catch it. If you want your script to die() when that exception is encountered, printing absolutely nothing up at level of the user, take a look at set_exception_handler().
Specifically, I'm thinking of a scenario in which you'd place the set_exception_handler()-call into a bootstrap file that the tests don't use, so the handler won't fire there regardless of scenario, so nothing interferes with PHPUnit's native exception handling.
This relates to set of issues I've been having getting some legacy code to pass a test. So I've come up with a Testable class like this...
class Testable {
static function exitphp() {
if (defined('UNIT_TESTING')) {
throw new TestingPhpExitException();
} else {
exit();
}
}
}
Now I simply replace calls to exit() with Testable::exitphp().
If it's under test I just define UNIT_TESTING, in production I don't. Seems like a simple Mock.
You can kill the script or throw an exception, depending on the value of an environmental variable...
So you kill in production or throw an exception in test environment.
Any call to die or exit, Kills the whole process...
This was supposed to be a comment but I can't comment with the level of my reputation points.