Skip to content
Chris edited this page Feb 13, 2022 · 3 revisions

In order to run applications over a WebSocket written in PHP you'll need to be using the (CLI) compiler. To find out what PHP version your command line interface defaults to use php -v

If your compiler defaulted to something like PHP-fastcgi, ect. you'll need to find the path to the CLI in your system.

In my particular build, I had to add #!/usr/local/bin/php in a shebang at the top of my script. This allows you to not use the # websocketd --port=8080 Socket.php rather than # websocketd --port=8080 php Socket.php

This is a simple echo server as well as a fifo named pipe implementation. If user A sends a message to user B I can look up what FIFO may be present if user B is online. I store IP addresses in the database so I'll link that with the user id. Then store the file as

$fifo = $_SESSION['user_id'] .'.fifo' 

if the file_exist() you can send the messages directly after posting to the database (successfully) to the fifo in a json array (or however you choose to implement).

Happy programming

  • Richard Miles

This example will echo a message sent from a WS client back to the WS client, and will also broadcast the same message to any other connected WS client.

Socket.php

#!/usr/local/bin/php
<?php

# https://www.leaseweb.com/labs/2013/08/catching-signals-in-php-scripts/
pcntl_async_signals(true);
pcntl_signal(SIGINT, 'signalHandler');  // Interrupted ( Ctrl-C is pressed, or `kill -SIGINT <pid>` occurred)

$FIFODIR=__DIR__;

$unique_id = $_SERVER['UNIQUE_ID'];
$fifoPath = $FIFODIR . '/' . $unique_id . '.fifo';

print $fifoPath;

if (!file_exists( $fifoPath )) shell_exec("mkfifo " . $fifoPath);

if (pcntl_fork() == 0)
{   // child - create writer on fifo
    $file = fopen($fifoPath,"w");
    sleep(1);
    exit(0);
} else {
    $fifoFile = fopen( $fifoPath, 'r' );  // the writer above allows this fopen to unblock.
    stream_set_blocking($fifoFile, false); // when using stream_select to block, all handles should be set to non blocking.
}
$stdin = fopen('php://stdin', 'r');

echo "COMMUNICATION STARTED\n ID :: " . $unique_id . "\n\n";

$running = true;
while($running) {
    // build up $readers array for every stream_select call since stream_select will remove any unchanged fd's from the lists.
    $readers = array($fifoFile, $stdin);
    if (($stream = stream_select( $readers, $writers, $except, 1)) === false) {
        print "A stream error occurred\n";
        break;
    } else {
        foreach ($readers as $input => $fd) {
            if ($fd == $stdin) {
                $line = fgets($stdin);
                if ($line === false) {
                  // EOF
                  $running = false;
                  break;
                }
                // TODO - search in app
                print "You sent :: $line \n";

                // send message to all other connected WS clients
                if ($dh = opendir($FIFODIR)) {
                    while (($file = readdir($dh)) !== false) {
                        if (substr($file, -5) == '.fifo') {
                            $handle = fopen($FIFODIR . '/' . $file, "w");
                            fwrite($handle, $unique_id . ' says ');
                            fwrite($handle, $line);
                            fclose($handle);
                        }
                    }
                    closedir($dh);
                }
            } elseif ($fd == $fifoFile) {
                $data = fread( $fifoFile, $bytes = 124 );
                if (!empty( $data )) print "App Sent :: " . $data . "\n";
            }
        }
    }
}

@fclose($stdin);
@fclose($fifoFile);

@unlink($fifoPath);

// clients will not see this when they initiate the disconnect.
print "Safe Exit \n\n";

exit(0);

function signalHandler($signal)
{
    print "Signal :: $signal\n";
    global $running;
    $running = false;
    // stream_select becomes unblocked after this signalHandler runs
}
Clone this wiki locally