Yii 1.1: yii-node-socket

Connect php, javascript, nodejs in one Yii application.
15 followers

If you need create more live application this extension helps you with it.

Based on nodejs socket.io library.

Features:

  • emit events to all connected clients
  • create rooms and emit events into some room
  • set up data in nodejs memory from php and get in from javascript
  • invoke any function or method of object in javascript (in window scope) from php
  • browser support

Github page

Requirements

OS: linux/unix/windows
git installed

Installation

Install nodejs, if not installed see nodejs official site
Install extension

  • Using git clone
$> git clone https://github.com/oncesk/yii-node-socket.git

Now go to the folder where you install extension application.ext.yii-node-socket and execute

$> git submodule init
$> git submodule update

Yii configuration

  • Configure console command in (main/console.php). You can use config below:
'commandMap' => array(
    'node-socket' => 'application.extensions.yii-node-socket.lib.php.NodeSocketCommand'
)
  • Register Yii component, need to add into main.php and console.php:
'nodeSocket' => array(
    'class' => 'application.extensions.yii-node-socket.lib.php.NodeSocket',
    'host' => 'localhost',  // default is 127.0.0.1, can be ip or domain name, without http
    'port' => 3001      // default is 3001, should be integer
)

Install nodejs components in application.ext.yii-node-socket.lib.js.server:

$> npm install

Congratulation, installation completed!

Notice: if the name of the component will not be nodeSocket, your need to use special key in console command --componentName=component_name

Console command actions

$> ./yiic node-socket # show help
$> ./yiic node-socket start # start server
$> ./yiic node-socket stop # stop server
$> ./yiic node-socket restart # restart server
$> ./yiic node-socket getPid # show pid of nodejs process

Javascript

before work in javascript you need start server (./yiic node-socket start) and register clients scripts in PHP (see below, into PHP section)

// create object and connect to web socket server
var socket = new YiiNodeSocket();

// enable debug mode
socket.debug(true);

// add event listener
socket.on('updateBoard', function (data) {
    // do any action
});

socket.room('testRoom').join(function (success, numberOfRoomSubscribers) {
    // success - boolean, numberOfRoomSubscribers - number of room members
    // if error occurred then success = false, and numberOfRoomSubscribers - contains error message
    if (success) {
        console.log(numberOfRoomSubscribers + ' clients in room: ' + roomId);
        // do something

        // bind events
        this.on('join', function (newMembersCount) {
            // fire on client join
        });

        this.on('data', function (data) {
            // fire when server send frame into this room with 'data' event
        });
    } else {
        // numberOfRoomSubscribers - error message
        alert(numberOfRoomSubscribers);
    }
});

PHP

Registering client scripts

Yii::app()->nodeSocket->registerClientScripts();

Frames

Frame - data package for nodejs server wrapped into Class. Per one request to nodejs server you can send only 1 frame. For send several frames at a time use Multiple frame.

Frames:

  • Event - send event to javascript
  • Invoke - invoke javascript function or method of object in window scope
  • PublicData - set up shared data into nodejs memory, any client can get it
  • Multiple - needed for sending more several frames per a time

Event frame

// create event frame
$frame = Yii::app()->nodeSocket->createEventFrame();
 
// set event name
$frame->setEventName('updateBoard');
 
// set data using ArrayAccess interface
$frame['boardId'] = 25;
$frame['boardData'] = $html;
 
// or you can use setData(array $data) method
// setData overwrite data setted before
 
$frame->send();

Set up shared data

Notice: You can set expiration using setLifeTime(integer $lifetime) method of class PublicData

// create frame
$frame = Yii::app()->nodeSocket->createPublicDataFrame();
 
// set key in storage
$frame->setKey('error.strings');
 
// set data
$frame->setData($errorStrings);
 
// you can set data via ArrayAccess interface
// $frame['empty_name'] = 'Please enter name';
 
// set data lifetime
$frame->setLifeTime(3600*2);    // after two hours data will be deleted from storage
 
// send
$frame->send();

Send event into room

// create frame
$frame = Yii::app()->nodeSocket->createEventFrame();
 
// set event name
$frame->setEventName('updateBoard');
 
// set room name
$frame->setRoom('testRoom');
 
// set data
$frame['key'] = $value;
 
// send
$frame->send();

Invoke javascript function

$invokeFrame = Yii::app()->nodeSocket->createInvokeFrame();
$invokeFrame->invokeFunction('alert', array('Hello world'));
$invokeFrame->send();   // alert will be showed on all clients

DOM manipulations with jquery

Task: you need update price on client side after price update in each product

...
 
$product = Product::model()->findByPk($productId);
if ($product) {
    $product->price = $newPrice;
    if ($product->save()) {
        $jFrame = Yii::app()->nodeSocket->createJQueryFrame();
        $jFrame
            ->createQuery('#product' . $product->id)
            ->find('span.price')
            ->text($product->price);
        $jFrame->send();
        // and all connected clients will can see updated price
    }
}
 
...

Send more than one frame per a time

Example 1:

$multipleFrame = Yii::app()->nodeSocket->createMultipleFrame();
 
$eventFrame = Yii::app()->nodeSocket->createEventFrame();
 
$eventFrame->setEventName('updateBoard');
$eventFrame['boardId'] = 25;
$eventFrame['boardData'] = $html;
 
$dataEvent = Yii::app()->nodeSocket->createPublicDataFrame();
 
$dataEvent->setKey('error.strings');
$dataEvent['key'] = $value;
 
$multipleFrame->addFrame($eventFrame);
$multipleFrame->addFrame($dataEvent);
$multipleFrame->send();

Example 2:

$multipleFrame = Yii::app()->nodeSocket->createMultipleFrame();
 
$eventFrame = $multipleFrame->createEventFrame();
 
$eventFrame->setEventName('updateBoard');
$eventFrame['boardId'] = 25;
$eventFrame['boardData'] = $html;
 
$dataEvent = $multipleFrame->createPublicDataFrame();
 
$dataEvent->setKey('error.strings');
$dataEvent['key'] = $value;
 
$multipleFrame->send();

See github for more documentation above!

Resources

Total 20 comments

#17496 report it
inProcess at 2014/06/23 10:58am
Re: userEventFrame

Thanks once! You are awesome :-)

It works! I had to put the on.socket frame script also on the outside the room page.

#17494 report it
once at 2014/06/23 09:31am
userEventFrame

Hi, i can not test it right now, but by the code you can send and catch it inside the room or outside

if outside code should be like that

var socket = new YiiNodeSocket();
socket.onConnect(function () {
  socket.on('user-event', function () {

  });
});

or inside

var socket = new YiiNodeSocket();
socket.onConnect(function () {
  socket.room('testRoom').join(function (success, numberOfRoomSubscribers) {
        if (success) {
            console.log(numberOfRoomSubscribers + ' clients in room: ' + roomId);
            // do something

            // bind events
            this.on('user-event', function (newMembersCount) {
                // fire on client join
            });
        } else {
            // numberOfRoomSubscribers - error message
            alert(numberOfRoomSubscribers);
        }
    });
});

maybe this helps you

#17488 report it
inProcess at 2014/06/21 10:39pm
Send a notification to a user outside of the room

Hi! I would like to send a user a notification/message even though he may be outside of the room. I am using createUserEventFrame(), but the user only gets the notification if he is inside of the room. If I am inside the room, is there a way to send a user an event frame no matter if he is inside of the room or outside of the room?

#17379 report it
Todd Anstis at 2014/05/30 03:39pm
Session ID

I changed the value of the property like you advised, but it looks like for some reason the session ID isn't being passed as a parameter in the cookie. If I look at the cookie in Firebug, and I look at it in the server log, it looks like this:

fbm_111062145712496=base_domain=.mydomain.com; path=/

Correct me if I'm wrong but shouldn't there be another parameter like sessionid=foo?

#17376 report it
once at 2014/05/30 02:42am
session problems

Hi, you can set up your own session variable name, NodeSocket have variable

public $sessionVarName = 'PHPSESSID';

you can change it into your config, but need chane console and main configs

#17375 report it
Todd Anstis at 2014/05/29 06:16pm
DB sessions?

I'm using DB sessions and I think that's the problem - in firebug it looks like the session var name is "sessionid." Not sure where to adjust it though? I changed it in the server.config.js, and in NodeSocket.php and it doesn't seem to be helping?

#17374 report it
Todd Anstis at 2014/05/29 03:44pm
Getting closer...

Instead of messing with CORS I just set up an A record so that my client and server are on the same domain. Which helped (no more "NO COOKIE TRANSMITTED" error) but now I'm seeing this in my server log:

out Thu, 29 May 2014 19:25:13 GMT warn: handshake error Have no session id for /client
#17372 report it
Todd Anstis at 2014/05/29 01:16pm
CORS

How can I make this work with CORS?

Right now I have this:

//  accept all connections from local server
if (serverConfiguration.checkClientOrigin) {
    console.log('Set origin: ' + serverConfiguration.origin);
    log.info('Set origin: ' + serverConfiguration.origin);
    io.set("origins", serverConfiguration.origin);
}
 
// Enables CORS
var enableCORS = function(req, res, next) {
    res.header('Access-Control-Allow-Origin', serverConfiguration.origin);
    res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
 
    // intercept OPTIONS method
    if ('OPTIONS' == req.method) {
        res.send(200);
    }
    else {
        next();
    }
};
 
// enable CORS!
app.use(enableCORS);
#17371 report it
once at 2014/05/29 11:27am
handshake error

NO COOKIE TRANSMITTED

seems like you use cross domain request and cookie not transmitted

We need to define problem on your local server

#17370 report it
Todd Anstis at 2014/05/29 10:47am
Server log

My server logs show the following:

out Thu, 29 May 2014 14:45:40 GMT debug: websocket writing 7::/client:undefined
out Thu, 29 May 2014 14:45:40 GMT debug: set heartbeat interval for client iBo7qBfaDhf-1jX5cYr5
out Thu, 29 May 2014 14:45:40 GMT debug: setting request GET /socket.io/1/websocket/iBo7qBfaDhf-1jX5cYr5
out Thu, 29 May 2014 14:45:40 GMT debug: client unauthorized for /client
out Thu, 29 May 2014 14:45:40 GMT debug: websocket writing 1::
out Thu, 29 May 2014 14:45:40 GMT warn: handshake error NO COOKIE TRANSMITTED for /client
out Thu, 29 May 2014 14:45:40 GMT debug: client authorized for 
out Thu, 29 May 2014 14:45:40 GMT debug: setting request GET /socket.io/1/websocket/5Gkyx96_LJ8ZzEbacYr4
out Thu, 29 May 2014 14:45:40 GMT debug: set heartbeat interval for client 5Gkyx96_LJ8ZzEbacYr4
out Thu, 29 May 2014 14:46:05 GMT debug: emitting heartbeat for client Z1UzPzyUnt05av7RcYr2
out Thu, 29 May 2014 14:46:05 GMT debug: websocket writing 2::
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat timeout for client Z1UzPzyUnt05av7RcYr2
out Thu, 29 May 2014 14:46:05 GMT debug: cleared heartbeat timeout for client Z1UzPzyUnt05av7RcYr2
out Thu, 29 May 2014 14:46:05 GMT debug: got heartbeat packet
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat interval for client Z1UzPzyUnt05av7RcYr2
out Thu, 29 May 2014 14:46:05 GMT debug: emitting heartbeat for client 5CwW3_QpZIp3NW8RcYr3
out Thu, 29 May 2014 14:46:05 GMT debug: cleared heartbeat timeout for client 5CwW3_QpZIp3NW8RcYr3
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat interval for client 5CwW3_QpZIp3NW8RcYr3
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat timeout for client 5CwW3_QpZIp3NW8RcYr3
out Thu, 29 May 2014 14:46:05 GMT debug: emitting heartbeat for client 5Gkyx96_LJ8ZzEbacYr4
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat timeout for client 5Gkyx96_LJ8ZzEbacYr4
out Thu, 29 May 2014 14:46:05 GMT debug: cleared heartbeat timeout for client 5Gkyx96_LJ8ZzEbacYr4
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat interval for client 5Gkyx96_LJ8ZzEbacYr4
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat timeout for client iBo7qBfaDhf-1jX5cYr5
out Thu, 29 May 2014 14:46:05 GMT debug: set heartbeat interval for client iBo7qBfaDhf-1jX5cYr5
out Thu, 29 May 2014 14:46:05 GMT debug: cleared heartbeat timeout for client iBo7qBfaDhf-1jX5cYr5
out Thu, 29 May 2014 14:46:05 GMT debug: emitting heartbeat for client iBo7qBfaDhf-1jX5cYr5
out Thu, 29 May 2014 14:46:30 GMT debug: emitting heartbeat for client Z1UzPzyUnt05av7RcYr2
#17363 report it
once at 2014/05/29 02:41am
command notworking

Hi, can you create other command for tests with command map?

Tell me,please, yii version and can you show your console.php config?

Check class path, maybe its invalid

#17362 report it
once at 2014/05/29 02:36am
nodejitsu

Hi, so, can you show server logs? And you can see socket.io version into node_modules/socket.io/package.json

#17360 report it
Todd Anstis at 2014/05/29 02:04am
Error cont.

looking in Firebug, it appears that my client is never connected:

1401342848310: Creating new event emitter with scope global
client.js (line 37)
GET http://mydomain.nodejitsu.com/socket.io/1/?t=1401342848316
 
200 OK
        134ms   
socket.io.js (line 1659)
1401342848326: Attach event listener for event: invoke
client.js (line 37)
1401342848328: Compiled event name: system:invoke
client.js (line 37)
1401342848330: Attach event listener for event: jquery
client.js (line 37)
1401342848332: Compiled event name: system:jquery
client.js (line 37)
1401342848335: Attach event listener for event: updateBoard
client.js (line 37)
1401342848337: Compiled event name: global:updateBoard
client.js (line 37)
1401342848551: Creating new event emitter with scope room:myroom
client.js (line 37)
1401342848553: Attach event listener for event: system:update.members_count
client.js (line 37)
1401342848555: Compiled event name: room:myroom:system:update.members_count
client.js (line 37)
connecting...
nodesockettest (line 163)
GET http://mydomain.nodejitsu.com/socket.io/1/?t=1401343190413
 
200 OK
        194ms   
socket.io.js (line 1659)
connecting...

then nothing. I put some code in to be written to the console when the client connects and its never being written - so no connection. thoughts?

#17358 report it
Todd Anstis at 2014/05/28 06:08pm
Still no dice?

I've got this set up to connect to a Nodejitsu socket.io server, and still getting this error. Is there something wrong with my elephant.io version?

#17356 report it
Alpesh Vaghela at 2014/05/28 01:15pm
Yes i added

Yes i added this already

'commandMap' => array( 'node-socket' => 'application.extensions.yii-node-socket.lib.php.NodeSocketCommand' )

but its not working :(

#17352 report it
Todd Anstis at 2014/05/28 09:40am
Error cont.

I'm not sure about socket.io version - is there a minimum version that's required?

#17351 report it
Todd Anstis at 2014/05/28 09:37am
Error line
$sess = explode(':', $res);
            $this->session['sid'] = $sess[0];
Line 325->  $this->session['heartbeat_timeout'] = $sess[1];
            $this->session['connection_timeout'] = $sess[2];
            $this->session['supported_transports'] = array_flip(explode(',',    $sess[3]));
#17350 report it
once at 2014/05/28 08:38am
Not working command, why?

Hello, did you add this code in config/console.php?

'commandMap' => array(
    'node-socket' => 'application.extensions.yii-node-socket.lib.php.NodeSocketCommand'
)

See commandMap

#17349 report it
Alpesh Vaghela at 2014/05/28 08:16am
Commands are not working (Server not starting :()

$> ./yiic node-socket # show help $> ./yiic node-socket start # start server $> ./yiic node-socket stop # stop server $> ./yiic node-socket restart # restart server $> ./yiic node-socket getPid # show pid of nodejs process

// GETTING THIS ERROR

Yii command runner (based on Yii v1.1.8) Usage: ./yiic [parameters...]

The following commands are available: - message - migrate - shell - webapp

To see individual command help, use the following: ./yiic help

#17347 report it
once at 2014/05/28 02:32am
Can you provide ore information

Hello, can you provide more information.

  • can you show your 325 line in Client.php
  • socket.io version

Leave a comment

Please to leave your comment.

Create extension
Downloads
No downloadable files yet