diff --git a/README.md b/README.md index 1cd8058..a3112b0 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,14 @@ -Websocket Client for PHP -======================== +# Websocket Client and Server for PHP -[![Build Status](https://travis-ci.org/Textalk/websocket-php.png)](https://travis-ci.org/Textalk/websocket-php) -[![Coverage Status](https://coveralls.io/repos/Textalk/websocket-php/badge.png)](https://coveralls.io/r/Textalk/websocket-php) +[![Build Status](https://travis-ci.org/Textalk/websocket-php.svg?branch=master)](https://travis-ci.org/Textalk/websocket-php) +[![Coverage Status](https://coveralls.io/repos/github/Textalk/websocket-php/badge.svg?branch=master)](https://coveralls.io/github/Textalk/websocket-php) -This package mainly contains a WebSocket client for PHP. +This library contains WebSocket client and server for PHP. -I made it because the state of other WebSocket clients I could found was either very poor -(sometimes failing on large frames) or had huge dependencies (React…). +The client and server provides methods for reading and writing to WebSocket streams. +It does not include convenience operations such as listeners and implicit error handling. -The Client should be good. - -The Server there because much of the code would be identical in writing a server, and because it is -used for the tests. To be really useful though, there should be a Connection-class returned from a -new Connection, and the Server-class only handling the handshake. Then you could hold a full array -of Connections and check them periodically for new data, send something to them all or fork off a -process handling one connection. But, I have no use for that right now. (Actually, I would -suggest a language with better asynchronous handling than PHP for that.) - -Installing ----------- +## Installing Preferred way to install is with [Composer](https://getcomposer.org/). ``` @@ -28,22 +17,177 @@ composer require textalk/websocket Currently support PHP versions `^5.4` and `^7.0`. +## Client + +The client can read and write on a WebSocket stream. +It internally supports Upgrade handshake and implicit close and ping/pong operations. + +### Class synopsis -Client usage: -------------- ```php -require('vendor/autoload.php'); +WebSocket\Client { + + public __construct(string $uri, array $options = []) + public __destruct() + + public send(mixed $payload, string $opcode = 'text', bool $masked = true) : void + public receive() : mixed + public close(int $status = 1000, mixed $message = 'ttfn') : mixed + + public getLastOpcode() : string + public getCloseStatus() : int + public isConnected() : bool + public setTimeout(int $seconds) : void + public setFragmentSize(int $fragment_size) : self + public getFragmentSize() : int +} +``` + +### Example: Simple send-receive operation -use WebSocket\Client; +This example send a single message to a server, and output the response. -$client = new Client("ws://echo.websocket.org/"); +```php +$client = new WebSocket\Client("ws://echo.websocket.org/"); $client->send("Hello WebSocket.org!"); +echo $client->receive(); +$client->close(); +``` + +### Example: Listening to a server + +To continuously listen to incoming messages, you need to put the receive operation within a loop. +Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out. +By consuming exceptions, the code will re-connect the socket in next loop iteration. + +```php +$client = new WebSocket\Client("ws://echo.websocket.org/"); +while (true) { + try { + $message = $client->receive(); + // Act on received message + // Break while loop to stop listening + } catch (\WebSocket\ConnectionException $e) { + // Possibly log errors + } +} +$client->close(); +``` + +### Constructor options + +The `$options` parameter in constructor accepts an associative array of options. + +* `timeout` - Time out in seconds. Default 5 seconds. +* `fragment_size` - Maximum payload size. Default 4096 chars. +* `context` - A stream context created using [stream_context_create](https://www.php.net/manual/en/function.stream-context-create). +* `headers` - Additional headers as associative array name => content. + +```php +$context = stream_context_create(); +stream_context_set_option($context, 'ssl', 'verify_peer', false); +stream_context_set_option($context, 'ssl', 'verify_peer_name', false); + +$client = new WebSocket\Client("ws://echo.websocket.org/", [ + 'timeout' => 60, // 1 minute time out + 'context' => $context, + 'headers' => [ + 'Sec-WebSocket-Protocol' => 'soap', + 'origin' => 'localhost', + ], +]); +``` + +## Server + +The library contains a rudimentary single stream/single thread server. +It internally supports Upgrade handshake and implicit close and ping/pong operations. -echo $client->receive(); // Will output 'Hello WebSocket.org!' +Note that it does **not** support threading or automatic association ot continuous client requests. +If you require this kind of server behavior, you need to build it on top of provided server implementation. + +### Class synopsis + +```php +WebSocket\Server { + + public __construct(array $options = []) + public __destruct() + + public accept() : bool + public send(mixed $payload, string $opcode = 'text', bool $masked = true) : void + public receive() : mixed + public close(int $status = 1000, mixed $message = 'ttfn') : mixed + + public getPort() : int + public getPath() : string + public getRequest() : array + public getHeader(string $header_name) : string|null + + public getLastOpcode() : string + public getCloseStatus() : int + public isConnected() : bool + public setTimeout(int $seconds) : void + public setFragmentSize(int $fragment_size) : self + public getFragmentSize() : int +} ``` -Development and contribution ------------------ +### Example: Simple receive-send operation + +This example reads a single message from a client, and respond with the same message. + +```php +$server = new WebSocket\Server(); +$server->accept(); +$message = $server->receive(); +$server->send($message); +$server->close(); +``` + +### Example: Listening to clients + +To continuously listen to incoming messages, you need to put the receive operation within a loop. +Note that these functions **always** throw exception on any failure, including recoverable failures such as connection time out. +By consuming exceptions, the code will re-connect the socket in next loop iteration. + +```php +$server = new WebSocket\Server(); +while ($server->accept()) { + try { + $message = $server->receive(); + // Act on received message + // Break while loop to stop listening + } catch (\WebSocket\ConnectionException $e) { + // Possibly log errors + } +} +$server->close(); +``` + +### Constructor options + +The `$options` parameter in constructor accepts an associative array of options. + +* `timeout` - Time out in seconds. Default 5 seconds. +* `port` - The server port to listen to. Default 8000. +* `fragment_size` - Maximum payload size. Default 4096 chars. + +```php +$server = new WebSocket\Server([ + 'timeout' => 60, // 1 minute time out + 'port' => 9000, +]); +``` + +## Exceptions + +* `WebSocket\BadOpcodeException` - Thrown if provided opcode is invalid. +* `WebSocket\BadUriException` - Thrown if provided URI is invalid. +* `WebSocket\ConnectionException` - Thrown on any socket I/O failure. + + +## Development and contribution Install or update dependencies using [Composer](https://getcomposer.org/). ``` @@ -66,13 +210,9 @@ Unit tests with [PHPUnit](https://phpunit.readthedocs.io/). make test ``` -License ([ISC](http://en.wikipedia.org/wiki/ISC_license)) ---------------------------------------------------------- +## License ([ISC](http://en.wikipedia.org/wiki/ISC_license)) -Copyright (C) 2014-2020 Textalk/Abicart -Copyright (C) 2015 Patrick McCarren - added payload fragmentation for huge payloads -Copyright (C) 2015 Ignas Bernotas - added stream context options -Copyright (C) 2015 Patrick McCarren - added ping/pong support +Copyright (C) 2014-2020 Textalk/Abicart and contributors. Websocket PHP is free software: Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and @@ -85,11 +225,24 @@ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -See COPYING. +See [Copying](COPYING). + +### Contributors + +Fredrik Liljegren, Armen Baghumian Sankbarani, Ruslan Bekenev, +Joshua Thijssen, Simon Lipp, Quentin Bellus, Patrick McCarren, swmcdonnell, +Ignas Bernotas, Mark Herhold, Andreas Palm, Sören Jensen, pmaasz, Alexey Stavrov. + + +## Changelog +1.3.0 -Changelog ---------- + * Implements ping/pong frames (@pmccarren) + * Correct ping-pong behaviour (@Logioniz) + * Correct close behaviour (@sirn-se) + * Various fixes concerning connection handling (@sirn-se) + * Overhaul of Composer, Travis and Coveralls setup, PSR code standard and unit tests (@sirn-se) 1.2.0