0

I'm using Ratchet PHP to send messages to clients, and I'm using

$server->loop->addPeriodicTimer(1, function () use ($row, $server) {...

To send a message every second. I can echo the message and the MySQL query works, but I am unable to actually access the clients object in $server, I can get to $server->app, but then when I do ->clients after that, it tells me that $clients doesn't exist.

To clarify, this isn't a problem when I don't use new HttpServer(...) but, without it, the browser console says the websocket handshake isn't valid, so that isn't a good workaround.

I've used print_r($server) and have confirmed that the clients object is inside a _httpServer:protected item. If I can access this, I'd be able to send messages, I think.

The code for the actual server video-server.php:

<?php
include "../../include/db.info.php";

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

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

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

$pdo = new PDO("mysql:host=localhost;port=3306;dbname=erewhon", "root", "");

$getUsername = $pdo->prepare("SELECT * FROM messages WHERE id=201");
$getUsername->execute();

$row = $getUsername->fetch(PDO::FETCH_ASSOC);

$server->loop->addPeriodicTimer(1, function () use ($row, $server) {        
    /*foreach ($server->app->component->clients as $client) {                  
            $client->send("hello client");          
    }*/
    print_r($server->app);
});

$server->run();
?>

The code for the classes file, chat.php:

<?php
namespace MyApp;
header("Content-Type: application/json; charset=UTF-8");


//include "../../db.info.php";


use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface {


    public $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
        echo "Congratulations! the server is now running\n";
    }

    public function onOpen(ConnectionInterface $conn) {
        // Store the new connection to send messages to later
        $this->clients->attach($conn);

        echo "New connection! ({$conn->resourceId})\n";
    }

    public function onMessage(ConnectionInterface $from, $msg) {
        //dont need this
    }

    public function onClose(ConnectionInterface $conn) {
        // The connection is closed, remove it, as we can no longer send it messages
        $this->clients->detach($conn);

        echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        echo "An error has occurred: {$e->getMessage()}\n";

        $conn->close();
    }

}
?>

2 Answers2

0

It seems that you can't get that data the way you want. The HttpServer define a protected variable.

protected $_httpServer; //<--- protected, you can't read from outside.
public function __construct(HttpServerInterface $component) {
    $this->_httpServer = $component;
    $this->_reqParser  = new HttpRequestParser;
}

But, you can pass a instance of Chat and keep track of it. It will point to the same memory address.

Make a try:

$chat = new Chat(); //<--- ADD THIS LINE
$server = IoServer::factory(
            new HttpServer(
              new WsServer(
                $chat //<----- USE HERE
              )
            ), 888
          );

....

$server->loop->addPeriodicTimer(1, function () use ($row, $server, $chat) {        
    /*foreach ($server->app->component->clients as $client) {                  
        $client->send("hello client");          
    }*/
    print_r($server->app);
    print_r($chat->clients); // <---- PRINT HERE TO GET THE INFO
});
Felippe Duarte
  • 14,901
  • 2
  • 25
  • 29
0

I've got my original answer below just in case, but I'd like to highlight that the accepted answer is the correct method, just don't forget to pass it to use

I know I probably shouldn't have done this to work around my problem, but it resolved it, so it is sufficient:

I went to vendor\cboden\ratchet\src\Ratchet\Http and edited HttpServer.php, specifically the variable protected $_httpServer, and changed it to public $_httpServer, which I probably shouldn't have, but that resolved my problem.

I could access the clients item by doing $server->app->_httpServer->component->clients.

Thanks to Felippe Duarte for highlighting this attribute, I didn't think of that.

  • Don't do this. Try the solution I gave to you. Doing what you just did will generate future errors, will break the app whenever you try to update this library. – Felippe Duarte Jul 16 '18 at 18:06
  • I did indeed try what you said, but it did not work, so I had to resort to this – fate.299792458 Jul 16 '18 at 20:27
  • @fate.299792458 As Felippe said doing this will only cause you problems. If you ever run composer update your changes will be lost. You need to pass $chat into the use statement and then access $chat->clients. Alternatively you could pass the $loop into your chat app and set the timer in the constructor there. – MC57 Jul 20 '18 at 19:41
  • Silly me, that did it, I totally forgot to pass it to the `use`, this is definitely incorrect – fate.299792458 Jul 20 '18 at 22:10