Cees-Jan

ReactPHP: HTTP

Now that we know how to use the react/filesystem it is time to build a HTTP server using react/http.

Websurfing

Installation

The installation, as with many ReactPHP packages, is simple and straight forward:

composer require react/http
Basic HTTP server

If you've searched for ReactPHP you probably ended up on reactphp.org which features the example below. That example is the simplest working HTTP in ReactPHP and returns nothing more then Hello world.

<?php

require 'vendor/autoload.php';

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket, $loop);

$http->on('request', function ($request, $response) {
    $response->writeHead(200, array('Content-Type' => 'text/plain'));
    $response->end("Hello World\n");
});

$socket->listen(1337);
$loop->run();
Basic text server

Now a webserver generally does more then outputting Hello world. Therefor we're combining it with the react/filesystem component to read files from the filesystem. To get started we list all the files in the webroot directory into the promise$files. This saves us from relisting the webroot directory over and over again. Then when a request comes in we loop through the file list and check if there is a matching file. If there isn't we read the contents of 404.txt and return it. Once we have a filename we open it and start reading it's contents into a bufferedsink. Now that we have the file contents we make sure to close open file in case we need it again. Only then we'll write the contents of the file back to the requesting browser. (There is much room for improvement here. For example a file can only be requested once at the same time. This does however demonstrates how to build a very simple webserver.)

<?php

use React\EventLoop\Factory;
use React\Filesystem\Filesystem;
use React\Filesystem\Node\File;
use React\Http\Request;
use React\Http\Response;

define('WEBROOT', __DIR__ . DIRECTORY_SEPARATOR . 'webroot');

require 'vendor/autoload.php';

$loop = Factory::create();
$socket = new React\Socket\Server($loop);
$http = new React\Http\Server($socket, $loop);
$filesystem = Filesystem::create($loop);
$files = $filesystem->dir(WEBROOT)->ls();

$http->on('request', function (Request $request, Response $response) use ($filesystem, $files) {
    echo 'Request for: ' . $request->getPath(), PHP_EOL;
    $files->then(function (SplObjectStorage $files) use ($filesystem, $request) {
        foreach ($files as $file) {
            if ($file->getPath() == WEBROOT . $request->getPath()) {
                return $file;
            }
        }

        return $filesystem->file(WEBROOT . DIRECTORY_SEPARATOR . '404.txt');
    })->then(function (File $file) {
        return $file->getContents()->then(function ($contents) use ($file) {
            return $file->close()->then(function () use ($contents) {
                return $contents;
            });
        });
    })->then(function ($fileContents) use ($response) {
        $response->writeHead(200, ['Content-Type' => 'text/plain']);
        $response->end($fileContents);
    });
});

$socket->listen(1337);
$loop->run();
Community example

This weeks community example sculpin is build and lead by Beau Simensen. Sculpin is a static site generator and also this blog is build using it. react/http is used in sculpin for the server flag. That flag turns sculpin into a miniature HTTP server just serving your blog for local development. This makes it really use to work on it and perfect it before pushing it to the world.

Examples

All the examples from this post can be found on Github.

Conclusion

Building a simple webserver with react/http is very simple and it gives your app it's own build in webserver you can use for specific use cases. Sculpin is the perfect example. But you could also use it to create a simple REST API for your app or microservice.


Categories: PHP - ReactPHP - ReactPHP SeriesTags: ReactPHP - HTTP - PHP