Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save clue/6ba091ed298b1ecec528a3a6c201a2d1 to your computer and use it in GitHub Desktop.
Save clue/6ba091ed298b1ecec528a3a6c201a2d1 to your computer and use it in GitHub Desktop.
Getting started with ReactPHP – Pushing real-time data to the browser (PHPYorkshire)

Think about "PHP" for a few seconds… What came to mind? It’s very likely you thought about your average product catalog, a blogging platform or how the platform is inferior to things like Node.js. But wait, it’s 2018! What if I told you PHP’s huge ecosystem has way more to offer and PHP is not inferior at all to its evil cousin Node.js?

Hands-on workshop given at PHPYorkshire (2018-04-13)

Getting started with ReactPHP – Pushing real-time data to the browser

In this hands-on tutorial you will learn about the core concepts of async PHP and why you too should care about ReactPHP being a real thing. The workshop has a strong focus on sparking the idea that PHP can be way faster and more versatile than you probably thought. Bring along an open mind and through lots of interesting examples and live demos learn why what sounds crazy at first might soon be a valuable addition in your toolbox.

You’re already familiar with PHP and want to learn what ReactPHP is all about? Then this tutorial is for you! We will start from scratch and build a demo application that pushes data from your command line in real-time to your browser. Several scenarios are prepared, but ideally you bring in your own ideas: Let’s build an interactive chat bot, an interactive CLI tool or even an actual desktop GUI, it’s up to you!

The tutorial assumes people are familiar with PHP, have PHP and their favorite IDE already setup, but does not require people to be familiar with ReactPHP.

Slides

The slides are available on https://speakerdeck.com/clue/getting-started-with-reactphp-pushing-real-time-data-to-the-browser-phpyorkshire.

These slides were used as part of a hands-on workshop at @PHPYorkshire. The full workshop took 3h with basic project setup, getting started with ReactPHP's core components and eventually implementing an HTTP server using Server-Sent Events (EventSource).

Resulting source code can be found on https://gist.github.com/clue/6ba091ed298b1ecec528a3a6c201a2d1.

Conclusions

Overall, I'm really happy with how the workshop went and this seems to be confirmed by some very positive feedback that attendeed left.

One minor issue was an outdated installation of clue/packagist-api-react during the workshop, which we managed to work around without too much trouble. As a result, I've just updated released v1.2.0 to make sure this is now compatible with all the latest versions (no code changes required) and have updated the resulting workshop code.

{
"require": {
"react/event-loop": "^0.5.1",
"react/stream": "^0.7.7",
"react/socket": "^0.8.10",
"clue/buzz-react": "^2.3",
"clue/packagist-api-react": "^1.2",
"react/http": "^0.8.3"
}
}
<?php
require __DIR__ . '/vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$browser = new Clue\React\Buzz\Browser($loop);
$packagist = new Clue\React\Packagist\Api\Client($browser);
$packagist->search('reactphp')->then(function ($info) {
var_dump($info);
});
$loop->run();
<?php
require __DIR__ . '/vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$stream = new React\Stream\WritableResourceStream(STDOUT, $loop);
$stream->write('hello');
$stdin = new React\Stream\ReadableResourceStream(STDIN, $loop);
// $stdin->on('data', function ($data) use ($stream) {
// $stream->write(strtoupper($data));
// });
// $stdin->on('end', function () {
// echo 'DONE';
// });
$dummy = new React\Stream\ThroughStream(function ($data) {
return '[' . $data . ']';
});
$stdin->pipe($dummy)->pipe($stream);
$loop->run();
<?php
require __DIR__ . '/vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$connector = new React\Socket\Connector($loop);
for ($i = 0; $i < 10; ++$i) {
$connector->connect('www.google.com:80')->then(function (React\Socket\ConnectionInterface $http) {
$http->write("GET / HTTP/1.0\r\n\r\n");
$http->on('data', function ($data) {
echo $data;
});
$http->on('close', function () {
echo 'CLOSED';
});
});
}
$loop->run();
<?php
require __DIR__ . '/vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
$handler = function (Psr\Http\Message\ServerRequestInterface $request) use ($loop) {
if ($request->getUri()->getPath() === '/') {
$html = <<<HTML
<html>
<head>
<script>
var live = new EventSource("/live");
live.addEventListener("message", function (message) {
document.body.innerHTML += message.data;
});
</script>
</head>
<body style="background-color:#ddd;">
hello world
</body>
</html>
HTML;
return new React\Http\Response(
200,
[
'Content-Type' => 'text/html'
],
$html
);
}
if ($request->getUri()->getPath() === '/live') {
$stream = new React\Stream\ThroughStream(function ($data) {
return 'data: ' . $data . "\n\n";
});
$loop->addPeriodicTimer(1.0, function () use ($stream) {
$stream->write(microtime(true) . " hello world");
});
return new React\Http\Response(
200,
[
'Content-Type' => 'text/event-stream'
],
$stream
);
}
return new React\Http\Response(404);
};
$http = new React\Http\Server($handler);
$server = new React\Socket\Server(8080, $loop);
$http->listen($server);
$loop->run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment