Search code examples
phptimerratchetreactphp

Periodically send messages to specific clients in Ratchet using ReactPHP


General info
Working on a chatsystem using Ratchet PHP. On request of my users, I'm trying to implement a Triviabot in a specific chatroom. Chatrooms don't actually "exist". Instead it checks which client ids are listed in an array and only sends the messages to those clients.

The idea is to ask a question every x amount of seconds.

The problem
Implementing a ReactPHP periodic timer isn't the issue. This is working as expected. The problem is that it blocks any connections towards the websocket as soon as the timer is enabled. There are no errors except for client side ones saying a connection can't be made.

Relevant code

<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use React\EventLoop\Factory;

class Chat implements MessageComponentInterface {
    private $msgLoop;

    public function __construct() {
        $this->startTriviaBot();
    }

    private function startTriviaBot(){
        $this->msgLoop = Factory::create();

        $this->msgLoop->addPeriodicTimer(1, function (){

            // This is working as expected confirming the timer works fine
            echo 'test'.PHP_EOL;

            foreach($this->clientIDsPerRoom['trivia'] as $receiverID){
                if(array_key_exists($receiverID, $this->userObjects)){

                    $this->sendMessage('Trivia', 'trivia', 'Bot test message', $receiverID);
                }
            }
        });

        // Commenting this makes the websocket accept connections again
        $this->msgLoop->run();
    }
}

Research I've done that didn't help

Periodically sending messages to clients in Ratchet - This one is sending messages to all clients directly from creating the websocket and not within the MessageComponentInterface. It has no access to specific clients.

Create a timer with Ratchet php - This is basicly what I'm doing, but it's causing the issue I'm having.


Solution

  • Hey ReactPHP core team member here. Looks like you're creating a new loop to run the timer. This will indeed stop the initial event loop you created to start Ratchet with. You need to pass on the event loop you created for Ratchet into this class as well and use that one. There can only be one event loop. (If you didn't pass in an event loop to Ratchet it will create one for you and use that. Passing one into Ratchet will override that behaviour.)