John John - 5 months ago 35
SQL Question

Socket.io and Phalcon PHP

I'm using Phalcon PHP and I want to try socket.io for the first time. I did the tutorial chat message with socket.io. But now I want to select some data in my database to count the number of rows in the table 'product' with query Phalcon :

$count_products = Product::count();


For example in my HTML page I have 5 products and when I'll add one product or more into my table Product I want an auto refresh to see 6 products in my HTML page.

Could you help me to do that ?

Answer

Once you are using sockets over ajax requests, you should keep your Phalcon as it is and try to implement simple tool using node.js+socket.io.

Simplest approach would be to create in Node an event forwarder what would listen on events on one side, and forward them to users browser. More is described here.

In your case I would recommend to add to your Phalcon model an afterSave listener as it is described in documentation. During afterSave method you will be able to pass eg. an UDP packet to your Node service with an information, that there are new records in X table.

Than your Node service should forward this event to clients' browsers, where javascript should decide, if X table is being viewed by current user. If it is, it should lock view to prevent any actions and fire up an ajax request to Phalcon service, which should ask your DB for actual data and refresh HTML content within view.

Of course you can make a Node service that would listen to changes directly on DB and send proper events to proper users with all content prepared to update fully dynamically. But this is an advanced approach and may be a bit overkilling solution. I'd like to stress out here, that proposed solution do work in fractions of second, giving a real-time feeling, as long as your Phalcon services are optimised for speed and data sent are not too big.

Of course you can create your forwarding service using Phalcon instead of Node, but after time you would regret it, as it is just easier to maintain such an event-based script using actually an event-based tools. Also for advanced PHP programmers that do work with JavaScript from time to time, this is not even a challenge to quickly learn such a small bit of Note.js to make such a simple solution.

Sending package over socket with PHP

$host = array(
    'scheme' => 'udp', // udp makes it lightweight and connectionless
    'host' => '192.168.10.10' // choose an IP where node is running
    'port' => '8888' // choose one > 1023
);
$param = sprintf('%s://%s:%s', $host['scheme'], $host['host'], $host['port']);
$socket = fsockopen($param, $errno, $errstr, $timeout);

I do have something like this enclosed in class Socket. I'd rather am using persistent socket (pfsockopen), so few processes at one time will use this one socket even if it is looking otherwise. In afterSave I am using Send method, which does more or less:

fwrite($socket, json_encode($msg));

Node.js event forwarding script over websocket

Exampleous configuration file node.json:

{
    "wsServer": {
        "listeners": {
            "udp": {
                "port": 8888
            }
        },
        "server": {
            "port": 8000
        }
    }
}

Your dependencies would be socket.io and dgram. You will know later what I mean, once you learn how to create Node application.

To make easier to understand whats following:

var config = require('node.json').wsServer;
var app = require('http').createServer().listen(config.server.port);
var io = require('socket.io').listen(app);
var listenerDgram = require('dgram').createSocket('udp4');
listenerDgram.bind(config.listeners.udp.port);

var users = {};

io.sockets.on('connection', function(socket) {
    // if you make user to connect by his individual ID during
    // websocket connection, providen after ?, like ?1234
    var user = parseInt(socket.handshake.query.user);

    // here to save user into var users
    if (!users[user]) {
        users[user] = {
            sockets: [socket]
        };
    } else {
        users[user].sockets.push(socket);
    }

    socket.on('disconnect', function() {
        // removing user from var users
        // warning: socket by socket, and if last
        //   socket is closed, remove whole user section
    });

});

// to emit data to all sockets of choosen users
emit = function(sockets, message, data) {
    for (var x in sockets) {
        if (sockets[x]) {
            sockets[x].emit(message, data);
        }
    }
};

// now UDP listening section
listenerDgram.on('message', function(msg, rinfo) {

    // you can declare checkIncoming if you have standarized
    // frames, or just use msg as it is
    var _dat = checkIncoming(msg.toString().trim(), true);
    var _response = {
        action: _dat.action,
        data: _dat.data
    };

    // you can declare standarizeFrame to define your own protocol,
    // or just use _response.
    var frame = standarizeFrame(_response);

    if (_dat.user) { // emitting to single user declared
        emit(users[_dat.user].sockets, 'notification', frame);
    } else { // emitting to all connected users
        io.emit('notification', frame);
    }
});

So you send JSONed string to server where Node is running on port 8888, and users web interfaces connects to same host on port 8000 to receive them back. Lots of debugging ahead of you.

PS: sorry for such an arbitrary answer, but I walked over it by myself, as I'm in love with Phalcon and have had no knowledge about that Node.js does even exist. Giving myself time to learn Node helped me to reduce my event based solutions to absolute minimum, enclosing them withing 100 lines instead of thousands, made them easy to maintain, gave my servers deep breath on CPU and satisfied client even more than php-based ones.

Comments