Real-time with PHP

VilniusPHP 0x41

I'll be talking about...

  • A bit of HTTP
  • Classic "real-time" approaches
  • Synchronous nature of PHP
  • Real-time through additional services
  • Authentication

Marius BalĨytis

Real-time use-cases

  • Chat!
  • Background processing completed
  • Notification received from partner
  • New information in your account
  • Status of some item has changed
  • Administrator has approved something
  • Your items have been shipped
  • Flow involving browser and mobile app

demo

demo

demo

demo

WebSockets

We'll see them later, no basic implementation

Problems

  • too many requests
  • many open requests
  • sometimes - latency
  • hard to integrate in PHP side

(A)synchronous code

Single process = several requests


const http = require('http');
let i = 0;
http.createServer((req, res) => {
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    setTimeout(function() {
        res.end('Response: ' + i++);
    }, 1000);
}).listen(8080);
					

ReactPHP

Event-driven, non-blocking I/O with PHP


$loop = React\EventLoop\Factory::create();
$i = 0;
$server = new React\Http\Server(function (Psr\Http\Message\ServerRequestInterface $request) {
    return new React\Http\Response(
        200,
        array('Content-Type' => 'text/plain'),
        "Response: " . $i++
    );
});
$socket = new React\Socket\Server(8080, $loop);
$server->listen($socket);
$loop->run();
                    

Yay!?


$loop = React\EventLoop\Factory::create();
$i = 0;
$server = new React\Http\Server(function (Psr\Http\Message\ServerRequestInterface $request) {
    sleep(1);
    return new React\Http\Response(
        200,
        array('Content-Type' => 'text/plain'),
        "Response: " . $i++
    );
});
$socket = new React\Socket\Server(8080, $loop);
$server->listen($socket);
$loop->run();
                    

PHP real-time problems

  • PHP is synchronous (blocking) by default
  • Hard to integrate with standard frameworks

Hello pusher!


                        $pusher->trigger(
                            'my-channel',
                            'my-event',
                            array('message' => 'hello world')
                        );
                    

                        var channel = pusher.subscribe('my-channel');
                        channel.bind('my-event', function(data) {
                          alert('Received my-event with message: ' + data.message);
                        });
                    

demo

  • Lots of libraries (https://pusher.com/docs/libraries)
  • Free up to some limits
  • Client-compatible Open-source alternatives
    • Elixir (with docker support): https://github.com/edgurgel/poxa
    • Ruby: https://github.com/stevegraham/slanger
    • Go: https://github.com/dimiro1/ipe

<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script>
    Pusher.logToConsole = true; // for dev
    var pusher = new Pusher('app_key', {
        wsHost: 'localhost',
        wsPort: 8080
    });
    var channel = pusher.subscribe('my-channel');
    channel.bind('my-event', function(data) {
        alert(data.message);
    });
</script>
                    

                        docker run -it --rm -p 8080:8080 edgurgel/poxa-automated
                        composer require pusher/pusher-php-server
                    

require __DIR__ . '/vendor/autoload.php';

$pusher = new Pusher\Pusher(
    'app_key',
    'secret',
    'app_id',
    [],
    'localhost',
    8080
);
$data['message'] = 'hello world';
$pusher->trigger('my-channel', 'my-event', $data);
                    

demo

Private channels

  • Starts with private-
  • Calls given URL in our server to grant access

demo

Summary

  • PHP is synchronous
  • Difficult to integrate classic HTTP requests with web sockets
  • We can use socket proxies for real-time events
  • Pusher is nice service with lots of libs
  • Pusher has Open-souce lib-compatible alternatives (poxa)