WebSockets with PHP: Mission impossible

Post on 15-Jul-2015

770 views 2 download

Tags:

transcript

WebSockets with PHP:Mission Impossible

Who am I?

✔ Yoan-Alexander Grigorov

✔ Proud PHP developer

✔ Likes to screw around with OOP

So what is a WebSocket after all?

WebSocket boosts UX

✔ Forget client pulls

✔ Forget long polling

✔ Make your application event based

What google say about WebSockets:

✔ It's easy to build WebSocket server with node.js

✔ It's hard to build WebSocket server with PHP

Research! Research! Research!

Available tools for PHP

✔ Ratchet

✔ … it's only that. Seriously, it's only Ratchet

✔ Ok, there are some other libraries, all of them named PHPWS

Ratchethttp://socketo.me

First things first

Install with composer

Setting up a server with Ratchet<?php

use Ratchet\MessageComponentInterface;

use Ratchet\ConnectionInterface;

class Handler implements MessageComponentInterface {

public function onOpen(ConnectionInterface $conn) {}

public function onMessage(ConnectionInterface $from, $msg) {}

public function onClose(ConnectionInterface $conn) {}

public function onError(ConnectionInterface $conn, \Exception $e) {}

}

Setting up a server with Ratchet<?php

use Ratchet\Server\IoServer;

use Ratchet\Http\HttpServer;

use Ratchet\WebSocket\WsServer;

$handler = new App\Handler();

$server = IoServer::factory(

new HttpServer(new WsServer(

$handler

)

), 8123);

$server->run();

In the browservar ws = new WebSocket('ws://127.0.0.1:8123');

// ... in some event

ws.send('Гледай кви ръки, чуек!');

// ... listener for messages:

ws.onmessage = function (message) {

alert(message);

};

Let's make a chat!

What do we need?

✔ Keep all active connection objects

✔ When a connection sends a message, notify all others

✔ On disconnect – remove connection object from the in-memory cache (map)

Connections handling<?php

class Handler implements MessageComponentInterface {

private $connections = [];

public function onOpen(ConnectionInterface $conn) {

$this->connections[$conn->resourceId] = $conn;

}

public function onMessage(ConnectionInterface $from, $msg) {

foreach ($this->connections as $receiver) {

$receiver->send($msg);

}

}

public function onClose(ConnectionInterface $conn) {

unset($this->connections[$conn->resourceId]);

}

}

That's it!

Keep an eye on:

✔ Run it like a daemon (/etc/init.d/skeleton)

✔ Fatal errors and exceptions – they kill the daemon

✔ ORMs like Doctrine help you a lot

Doctrine uses Identity Map

Identity map gives you wings!

Web Servers and Web Sockets

Don't forget good old Ajax!

But how ajax could send messages to the WebSocket server?

Message Queues

ZeroMQ

✔ Libzmq

✔ Has a PHP extension in PECL

✔ React/ZMQ (PHP wrapper)

✔ "react/zmq": "0.2.*|0.3.*" in Composer

What you need to know

✔ There are ZeroMQ listeners– They use simple TCP

✔ There are ZeroMQ contexts (senders)– They connect to ZeroMQ listeners and send

messages

What you need to know

✔ You need ZeroMQ listener in the WebSocket daemon

✔ Ordinary PHP scripts send messages to the listener

✔ Since ZeroMQ is in the WebSocket daemon, it could easily interact with WebSocket connections

Let's add ZeroMQ to our application!

Add callback for ZeroMQ messages<?php

class Handler implements MessageComponentInterface

{

// ...

// this is CUSTOM method (not from interface)

public function onZMQMessage($message)

{

}

}

Add ZeroMQ listener to the daemon<?php

use ...

$handler = new App\Handler();

$server = IoServer::factory(

new HttpServer(new WsServer(

$handler

)

), 8123);

// Listen on 127.0.0.1:4444

$context = new \React\ZMQ\Context($server->loop);

$pull = $context->getSocket(\ZMQ::SOCKET_PULL);

$pull->bind("tcp://127.0.0.1:4444");

$pull->on('message', function ($data) use ($handler) {

$handler->onZMQMessage($data);

});

$server->run();

In your Ajax server script<?php

// ... add stuff to the database, other things...

// notify using ZeroMQ:

$context = new \ZMQContext();

$socket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'pusher');

$socket->connect("tcp://127.0.0.1:4444");

$socket->send('Глей кви ръки! Туй аз ли съм!?');

Go! Try it yourself!

Working code examples from this talk:

https://github.com/sasquatch-mc/ratchet-example(check the branches)

References

✔ http://socketo.me

✔ http://zeromq.org

Thanks and good luck!

✔ joan.grigorov@gmail.com

✔ bgscripts.com