Asynchronous WebSocket server for Icicle
Branch: master
Clone or download
sagebind Merge pull request #5 from pdelre/patch-1
fix closing of README code block
Latest commit 516ec96 Oct 18, 2016
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples Update with PHP 7 features Feb 23, 2016
src Merge branch 'v1.x' Mar 9, 2016
tests Update with PHP 7 features Feb 23, 2016
.gitattributes Initial commit Dec 22, 2015
.gitignore Initial commit Dec 22, 2015
.travis.yml Update with PHP 7 features Feb 23, 2016
CHANGELOG.md Add changelog; update readme Mar 11, 2016
CONTRIBUTING.md Initial commit Dec 22, 2015
LICENSE Initial commit Dec 22, 2015
README.md
composer.json Update with PHP 7 features Feb 23, 2016
phpunit.xml.dist

README.md

WebSockets for Icicle

Asynchronous, non-blocking WebSocket server.

This library is a component for Icicle that provides an asynchronous WebSocket server that can handle normal HTTP requests and WebSocket requests on the same port. Like other Icicle components, this library uses Coroutines built from Awaitables and Generators to make writing asynchronous code more like writing synchronous code.

Build Status Coverage Status Semantic Version MIT License @icicleio on Twitter

Documentation and Support

Requirements
  • PHP 5.5+ for v0.1.x branch (current stable) and v1.x branch (mirrors current stable)
  • PHP 7 for v2.0 (master) branch supporting generator delegation and return expressions
Suggested
Installation

The recommended way to install is with the Composer package manager. (See the Composer installation guide for information on installing and using Composer.)

Run the following command to use this library in your project:

composer require icicleio/websocket

You can also manually edit composer.json to add this library as a project requirement.

// composer.json
{
    "require": {
        "icicleio/websocket": "^0.1"
    }
}

Example

The example below creates a simple HTTP server that accepts WebSocket connections on the path /echo, otherwise responding with a 404 status code on other paths.

#!/usr/bin/env php
<?php

require '/vendor/autoload.php';

use Icicle\Http\Message\{BasicResponse, Request, Response};
use Icicle\Http\Server\RequestHandler;
use Icicle\Loop;
use Icicle\Socket\Socket;
use Icicle\WebSocket\{Application, Connection, Server\Server};

$echo = new class implements Application {
    /**
     * {@inheritdoc}
     */
    public function onHandshake(Response $response, Request $request, Socket $socket): Response
    {
        // This method provides an opportunity to inspect the Request and Response before a connection is accepted.
        // Cookies may be set and returned on a new Response object, e.g.: return $response->withCookie(...);

        return $response; // No modification needed to the response, so the passed Response object is simply returned.
    }

    /**
     * {@inheritdoc}
     */
    public function onConnection(Connection $connection, Response $response, Request $request): Generator
    {
        // The Response and Request objects used to initiate the connection are provided for informational purposes.
        // This method will primarily interact with the Connection object.

        yield from $connection->send('Connected to echo WebSocket server powered by Icicle.');

        // Messages are read through an Observable that represents an asynchronous set. There are a variety of ways
        // to use this asynchronous set, including an asynchronous iterator as shown in the example below.

        $observable = $connection->read(); // Connection::read() returns an observable of received Message objects.
        $iterator = $observable->getIterator();

        while (yield from $iterator->isValid()) {
            /** @var \Icicle\WebSocket\Message $message */
            $message = $iterator->getCurrent();

            if ($message->getData() === 'close') {
                yield from $connection->close(); // Close the connection if the message contains only 'close'
            } else {
                yield from $connection->send($message); // Echo the message back to the client.
            }
        }

        /** @var \Icicle\WebSocket\Close $close */
        $close = $iterator->getReturn(); // Only needs to be called if the close reason is needed.
    }
};

$server = new Server(new class ($echo) implements RequestHandler {
    /** @var \Icicle\WebSocket\Application */
    private $application;

    public function __construct(Application $application)
    {
        $this->application = $application;
    }

    public function onRequest(Request $request, Socket $socket): Generator
    {
        if ($request->getUri()->getPath() === '/echo') {
            return $this->application; // Return a WebSocket Application to create a WebSocket connection.
        }

        $response = new BasicResponse(Response::NOT_FOUND, [
            'Content-Type' => 'text/plain',
        ]);

        yield from $response->getBody()->end('Resource not found.');

        return $response; // Return a regular HTTP response for other request paths.
    }

    public function onError(int $code, Socket $socket): Response
    {
        return new BasicResponse($code);
    }
});

$server->listen(8080);

Loop\run();