Ayan Ayan - 1 year ago 53
PHP Question

How can I keep a script running in the background-not working

I am trying to fire up the Ratchet's chat-server.php file which is needed to fire up the socket server from a php file so that whenever a client visits the page the server is started but its not happening. This chat-server.php file needs to be run from the terminal but I am trying to run it from the main page on user visit.

FILE STRUCTURE


./index.php

./config.php

./vendor/(all vendor files)

./src/MyApp/Chat.php

./js/app.js

./inc/connect.php <--Database connection file

./inc/send-message.php

./inc/startServer.php

./inc/bg.php

./inc/serverStatus.txt

./css/index.css

./css/reset.css

./bin/chat-server.php


config.php

<?php
include_once ("./inc/startServer.php");
?>


chat-server.php

<?php

use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use MyApp\Chat;


if(isset($startNow)){

require dirname(__DIR__) . '/vendor/autoload.php';

$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);

$server->run();

}

?>


bg.php

<?php
$startNow = 1;
include_once ("../bin/chat-server.php");
?>


startServer.php

<?php

$statusFile = dirname(__FILE__).'/serverStatus.txt';
$status = file_get_contents($statusFile);
if($status == "0"){

function execInBackground($cmd) {
if(substr(php_uname(), 0, 7) == "WINDOWS") {
pclose(popen('start /B ' . $cmd, 'r'));
file_put_contents($statusFile, 1);
}
else {
exec($cmd . ' > /dev/null &');
file_put_contents($statusFile, 1);
}
}

execInBackground("php " . dirname(__FILE__) . "/bg.php");

}


?>

serverStatus.txt

Its default value has been set to 0

NOTE: Updated the code accordingly but doesn't work still now.

Plus I don't have any idea about restarting the server incase of any failure or how to shutdown the server when all clients leave.

Answer Source

When you including files using relative paths you must remember that they are relative to the current running file, not just to current file.

In your case. When you accessing index.php and including in it config.php and from then startServer.php it's all ok, because you are including ./inc/startServer.php which is right, relative to index.php.
But than you are including ../inc/serverStatus.txt, which is not right, because you are still running index.php and that relative path resolves to /../inc/serverStatus.txt in other words you are trying to access a file that is one level higher than your DOCUMENT_ROOT. So you will never get into your if statement.
You can use a __FILE__ constant which is always resolves to the absolute path of the current file. So you can change your $statusFile to

$statusFile = dirname(__FILE__) . '/serverStatus.txt';

Next thing is regards your exec() and popen(pclose()) commands. If you want to execute some command that is not in your PATH environment variable, than you must specify an absolute path to your file. Because you don't know from which folder it will be started. It may be started from Apache home dir or maybe started from your user's home dir. It depends on the setup of your PHP is it sapi module or is it fcgi. Anyway, you need to change your execInBackground() to

execInBackground("php " . dirname(__FILE__) . "/bg.php");

And add some spaces in your commands, like this:

pclose(popen('start /B ' . $cmd, 'r'));
exec($cmd . ' > /dev/null &');

this way you will not have some strange errors that might occur.

In your bg.php you need to change current working directory, like this:

<?php
    $startNow = 1;
    chdir(dirname(__FILE__));
    include_once ("../bin/chat-server.php");
?>

This way, PHP will find chat-server.php no matter from what folder you will run it.

BTW, how did you planning to stop your server? =)


UPDATE: Also you have a variable scope issue in your function execInBackground(). Your function relies on the variable $statusFile, but this variable defined in the global scope and is not visible inside the function. To make it visible, you must include statement global $statusFile; in your function before accessing $statusFile. Like this:

    function execInBackground($cmd) {
            global $statusFile;
            if (false !== stripos(PHP_OS, 'win')) {
                pclose(popen('start /B ' . $cmd, 'r'));
                file_put_contents($statusFile, 1);
            } else {
                exec($cmd . ' > /dev/null &');
                file_put_contents($statusFile, 1);
            }
    }

and I've changed the way you are checking the OS, because your approach contained an error.


UPDATE2:

After a bunch of tests, I figured out that when php-script running in the background (under Windows) it can't use "echo()" to write to STDOUT. When it happens, php-script will silently die, with no errors. So change all echo()'s to some log-function.

Change listening port to some other, that is free for sure, like 50000. Because, 8080 is often may be already taken by some web server or other service.

And remember! You can start your chat server only if you have your own server (like VPS/VDS or dedicated server), on the public hosting no one will allow you to open some port for incoming connections for security reasons. On the public servers there are often disabled functions like exec(), popen() and similar for the same reasons.