Getting started with ReactPHP

    Cees-Jan Kiewiet

  • @WyriHaximus
  • ReactPHP Team member
  • Sculpin Team member
  • Lead Software Engineer at 

 

 

What is ReactPHP

Building a sample app:

  • Analyzes hostnames
  • Looks up IP address
  • Fetches page title
  • Fetches GeoIP

Demo time!

Requirements

  • react/event-loop
  • react/dns
  • react/http
  • react/filesystem
  • wyrihaximus/react-guzzle-psr7
  • clue/sse-react
  • reactjs

https://github.com/WyriHaximus/reactphp-reactjs-hostname-analyzer-example

The Event Loop

The Heart of asynchronous code

Streams
Timers
Ticks

Loop creation

$loop = React\EventLoop\Factory::create();

$loop->run();

Available loops

  • StreamSelectLoop
  • LibEventLoop
  • LibEvLoop
  • ExtEventLoop

HTTP Server

Listens for incoming requests

Requests

Not
Connections

Creating the HTTP Server

$socket = new SocketServer($loop);

Creating the HTTP Server

$http = new HttpServer($socket, $loop);

Creating the HTTP Server

$http->on(
    'request',
    function ($request, $response) {
        // Handle Request
    }
);

Creating the HTTP Server

$socket->listen(2406);

SSE listener

if ($request->getPath() !== '/sse') {
    return;
}

SSE listener

$response->writeHead(
    200,
    ['Content-Type' => 
    'text/event-stream']
);

SSE listener

$channel->connect($response);
$response->on('close', function () {
    $channel->disconnect($response);
});

Broadcast listener

function broadcast($event)
{
    $this->
        channel->
        writeMessage(
            json_encode($event->data())
        );
}

Lookup listener

if ($request->getPath() !== 
    '/lookup.json') {
    return;
}
lookUp('php.net');
$response->writeHead(200);
$response->end('{}');

Filesystem

Evented filesystem access

Promises
Streams
Threads

Set up

$files = Filesystem::create($loop);

Set up

$files = Filesystem::create($loop)
    ->dir('/var/www/app/public');

Set up

$files = Filesystem::create($loop)
    ->dir('/var/www/app/public')
    ->lsRecursive();

Request listener

foreach ($listing as $node) {
    if (!($node instanceof FileInterface)) {
        continue;
    }
}

Request listener

if ($request->getPath() == 
    $node->getPath()) {
    $response->writeHead(200);
    $node->pipe($response);
}

DNS Resolver

Resolves DNS records by hostname

Hostnames
Cache
Promises

Creating the resolver

use React\Dns\Resolver\Factory;

$factory = new ResolverFactory();

Creating the resolver

$resolver = $factory->
    createCached('8.8.8.8', $loop);

Looking up the IP

$resolver->resolve('php.net');

Looking up the IP

resolve()->then(function ($ip) {
    broadcast([
        'type' => 'ip',
        'data' => $ip,
    ]);
});

HTTP Client

Basic HTTP 1.0 client

Streaming
Responses
Explicit

Setting up the HTTP client

$httpClient = new Client([
    'handler' => new Adapter(
        $loop,
        null,
        $dns
    ),
]);

GeoIP lookup

$httpClient->getAsync(
    'https://freegeoip.net/json/php.net'
)

GeoIP lookup

getAsync()->then(function ($response) {
    $body = $response->
        getBody()->getContents();
    broadcast([
        'type' => 'geoip',
        'data' => json_decode($body),
    ]);
}

Title lookup

$httpClient->getAsync('http://php.net');

Title lookup

getAsync()->then(function ($response) {
    $body = $response->
        getBody()->
        getContents();
    if (hasTitle($body)) {
        broadcast([
            'type' => 'title',
            'data' => getTitle($body),
        ]);
    }
});

Questions?

Getting started with ReactPHP (DPC 2016)

By wyrihaximus

Getting started with ReactPHP (DPC 2016)

  • 2,407