Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement event listening in PHP

here is my problem: I have a script (let's call it comet.php) whic is requsted by an AJAX client script and wait for a change to happen like this:

while(no_changes){
    usleep(100000);
    //check for changes
}

I don't like this too much, it's not very scalable and it's (imho) "bad practice" I would like to improve this behaviour with a semaphore(?) or anyway concurrent programming technique. Can you please give me some tips on how to handle this? (I know, it's not a short answer, but a starting point would be enough.)

Edit: what about LibEvent?

like image 382
ArtoAle Avatar asked Sep 19 '11 10:09

ArtoAle


People also ask

Is there an event listener in PHP?

Listener - A Listener is any PHP callable that expects to be passed an Event. Zero or more Listeners may be passed the same Event. A Listener MAY enqueue some other asynchronous behavior if it so chooses. Emitter - An Emitter is any arbitrary code that wishes to dispatch an Event.

How is event handling done in PHP?

To handle events user may register a callback which is called when event becomes active. If event is configured persistent , it remains pending. If it is not persistent, it stops being pending when it's callback runs.

How do you generate event and listeners?

The first step is to register the event and the respective listener. For that, Laravel provides an EventServiceProvider. php file where you can define events and listeners. The $listen property contains an array for registering all the events and listeners as key-value pairs.

How does event listener work?

An event listener is a function that initiates a predefined process if a specific event occurs. So, an event listener “listens” for an action, then calls a function that performs a related task. This event can take one of many forms. Common examples include mouse events, keyboard events, and window events.


2 Answers

You can solve this problem using ZeroMQ.

ZeroMQ is a library that provides supercharged sockets for plugging things (threads, processes and even separate machines) together.

I assume you're trying to push data from the server to the client. Well, a good way to do that is using the EventSource API (polyfills available).

client.js

Connects to stream.php through EventSource.

var stream = new EventSource('stream.php');

stream.addEventListener('debug', function (event) {
    var data = JSON.parse(event.data);
    console.log([event.type, data]);
});

stream.addEventListener('message', function (event) {
    var data = JSON.parse(event.data);
    console.log([event.type, data]);
});

router.php

This is a long-running process that listens for incoming messages and sends them out to anyone listening.

<?php

$context = new ZMQContext();

$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind("tcp://*:5555");

$pub = $context->getSocket(ZMQ::SOCKET_PUB);
$pub->bind("tcp://*:5556");

while (true) {
    $msg = $pull->recv();
    echo "publishing received message $msg\n";
    $pub->send($msg);
}

stream.php

Every user connecting to the site gets his own stream.php. This script is long-running and waits for any messages from the router. Once it gets a new message, it will output this message in EventSource format.

<?php

$context = new ZMQContext();

$sock = $context->getSocket(ZMQ::SOCKET_SUB);
$sock->setSockOpt(ZMQ::SOCKOPT_SUBSCRIBE, "");
$sock->connect("tcp://127.0.0.1:5556");

set_time_limit(0);
ini_set('memory_limit', '512M');

header("Content-Type: text/event-stream");
header("Cache-Control: no-cache");

while (true) {
    $msg = $sock->recv();
    $event = json_decode($msg, true);
    if (isset($event['type'])) {
        echo "event: {$event['type']}\n";
    }
    $data = json_encode($event['data']);
    echo "data: $data\n\n";
    ob_flush();
    flush();
}

To send messages to all users, just send them to the router. The router will then distribute that message to all listening streams. Here's an example:

<?php

$context = new ZMQContext();

$sock = $context->getSocket(ZMQ::SOCKET_PUSH);
$sock->connect("tcp://127.0.0.1:5555");

$msg = json_encode(array('type' => 'debug', 'data' => array('foo', 'bar', 'baz')));
$sock->send($msg);

$msg = json_encode(array('data' => array('foo', 'bar', 'baz')));
$sock->send($msg);

This should prove that you do not need node.js to do realtime programming. PHP can handle it just fine.

Apart from that, socket.io is a really nice way of doing this. And you could connect to socket.io to your PHP code via ZeroMQ easily.

See also

  • ZeroMQ
  • ZeroMQ PHP Bindings
  • ZeroMQ is the Answer - Ian Barber (Video)
  • socket.io
like image 50
igorw Avatar answered Oct 06 '22 06:10

igorw


It really depends on what you are doing in your server side script. There are some situations in which your have no option but to do what you are doing above.

However, if you are doing something which involves a call to a function that will block until something happens, you can use this to avoid racing instead of the usleep() call (which is IMHO the part that would be considered "bad practice").

Say you were waiting for data from a file or some other kind of stream that blocks. You could do this:

while (($str = fgets($fp)) === FALSE) continue;
// Handle the event here

Really, PHP is the wrong language for doing stuff like this. But there are situations (I know because I have dealt with them myself) where PHP is the only option.

like image 22
DaveRandom Avatar answered Oct 06 '22 06:10

DaveRandom