10

I want to run a function that iterates through a generator class. The generator functions would run as long as the Ratchet connection is alive. All I need to do is to make this happen after the run method is executed:

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

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

 $server = IoServer::factory(

    new HttpServer(
        new WsServer(
            new Chat()
        )
    ),

    8180,
    '0.0.0.0'

);

$server->run();

This is the method I need to run in the server after it is started:

function generatorFunction()
{

$products = r\table("tableOne")->changes()->run($conn);
 foreach ($products as $product) {
   yield $product['new_val'];
 }

}

Previously I was calling the function before $server->run() like this:

for ( $gen = generatorFunction(); $gen->valid(); $gen->next()) {
 var_dump($gen->current());
}
$server->run();

But this doesn't allow the client to establish a connection to the Ratchet server. I suspect it never comes to $server->run() as the generator class is being iterated.

So now, I want to start the server first, then call this generator method so that it can keep listening to changes in rethinkdb.

How do I do that?

Don't Panic
  • 41,125
  • 10
  • 61
  • 80
112233
  • 2,406
  • 3
  • 38
  • 88

1 Answers1

4

Let's start by example:

<?php

require 'vendor/autoload.php';

class Chat implements \Ratchet\MessageComponentInterface {
    function onOpen(\Ratchet\ConnectionInterface $conn) { echo "connected.\n"; }
    function onClose(\Ratchet\ConnectionInterface $conn) {}
    function onError(\Ratchet\ConnectionInterface $conn, \Exception $e) {}
    function onMessage(\Ratchet\ConnectionInterface $from, $msg) {}
}

$loop = \React\EventLoop\Factory::create(); // create EventLoop best for given environment
$socket = new \React\Socket\Server('0.0.0.0:8180', $loop); // make a new socket to listen to (don't forget to change 'address:port' string)
$server = new \Ratchet\Server\IoServer(
    /* same things that go into IoServer::factory */
    new \Ratchet\Http\HttpServer(
        new \Ratchet\WebSocket\WsServer(
            new Chat() // dummy chat to test things out
        )
    ), 
    /* our socket and loop objects */
    $socket, 
    $loop
);

$loop->addPeriodicTimer(1, function (\React\EventLoop\Timer\Timer $timer) {
    echo "echo from timer!\n";
});

$server->run();

To achieve what you need you don't have to run the loop before or after the $server->run() but it needs to be run simultaneously.

For that you need to get deeper than Ratchet - to ReactPHP and its EventLoop. If you have access to the loop interface then adding a timer (that executes once) or a periodic timer (every nth second) is a piece of cake.

Michal Bieda
  • 954
  • 5
  • 13
  • I'm already using the event loop which fetches data every second. But I want to avoid that and make use of changes() method found in rethinkdb or in other words a generator function which would halt other process. So for that I think I need to run the server and then attach this in the background. – 112233 Mar 28 '18 at 04:36
  • 1
    The `php-rql` client library will stop everything on `r\Cursor::requestMoreIfNecessary()` and poll db until there is any new data. So yes, what you have left is running separate process in the background and use [IPC](https://github.com/reactphp/react/wiki/FAQ) to get data back. Or use concurrency framework like [Amphp](https://amphp.org/) and write own non-blocking driver for rethinkdb – Michal Bieda Mar 28 '18 at 09:27
  • @MichalBieda In this case how can I send messages on timer for chat users ? For example : `$chat = new Chat(); $loop = \React\EventLoop\Factory::create(); $socket = new \React\Socket\Server('0.0.0.0:8080', $loop); $server = new \Ratchet\Server\IoServer(new \Ratchet\Http\HttpServer(new \Ratchet\WebSocket\WsServer($chat)),$socket, $loop); $loop->addPeriodicTimer(1, function (\React\EventLoop\Timer\Timer $timer) { if (!isset($chat)) return; $msg = "echo from timer!"; $chat->broadcast($msg); });` – MCunha98 Jan 27 '20 at 22:14
  • @MCunha98 in the addPeriodicTimer when you declare a function you must add `use ($chat)` - so it looks like this: `function (\ReactEventLoop\Timer\Timer $timer) use ($chat) { ...`. Without this the `$chat` variable inside the function body will always be undefined/null – Michal Bieda Jan 29 '20 at 08:15
  • Thanks @MichalBieda , I follow the subscribe example of Ratchet with ZeroMQ and its worked (after a big fight with dll instalations on windows). – MCunha98 Jan 29 '20 at 15:25