diff --git a/README.md b/README.md index 1efb8e0..44b77bb 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Among others, multicast networking is the basis for: * [Factory](#factory) * [createSender()](#createsender) * [createReceiver()](#createreceiver) - * [Socket](#socket) + * [SocketInterface](#socketinterface) * [Install](#install) * [Tests](#tests) * [License](#license) @@ -48,7 +48,7 @@ See also the [examples](examples). ### Factory -The `Factory` is responsible for creating your [`Socket`](#socket) instances. +The `Factory` is responsible for creating your [`SocketInterface`](#socketinterface) instances. It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage). ```php @@ -58,7 +58,9 @@ $factory = new Factory($loop); #### createSender() -The `createSender()` method can be used to create a socket capable of sending outgoing multicast datagrams and receiving incoming unicast responses. It returns a [`Socket`](#socket) instance. +The `createSender(): SocketInterface` method can be used to +create a socket capable of sending outgoing multicast datagrams and receiving +incoming unicast responses. It returns a [`SocketInterface`](#socketinterface) instance. ```php $socket = $factory->createSender(); @@ -73,11 +75,13 @@ $socket->on('message', function ($data, $address) { ``` This method works on PHP versions as old as PHP 5.3 (and up), as its socket API has always been -[level 1 multicast conformant](http://www.tldp.org/HOWTO/Multicast-HOWTO-2.html#ss2.2). +[level 1 multicast conformant](https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html#ss2.2). #### createReceiver() -The `createReceiver($address)` method can be used to create a socket capable of receiving incoming multicast datagrams and sending outgoing unicast or multicast datagrams. It returns a [`Socket`](#socket) instance. +The `createReceiver(string $address): SocketInterface` method can be used to +create a socket capable of receiving incoming multicast datagrams and sending +outgoing unicast or multicast datagrams. It returns a [`SocketInterface`](#socketinterface) instance. ```php $socket = $factory->createReceiver('224.10.20.30:4050'); @@ -91,10 +95,10 @@ $socket->on('message', function ($data, $remote) use ($socket) { }); ``` -This method requires PHP 5.4 (or up) and ext-sockets. +This method requires PHP 5.4 (or up) and `ext-sockets`. Otherwise, it will throw a `BadMethodCallException`. This is a requirement because receiving multicast datagrams requires a -[level 2 multicast conformant](http://www.tldp.org/HOWTO/Multicast-HOWTO-2.html#ss2.2) +[level 2 multicast conformant](https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html#ss2.2) socket API. The required multicast socket options and constants have been added with [PHP 5.4](http://php.net/manual/en/migration54.global-constants.php) (and up). @@ -104,12 +108,14 @@ to the newer stream based networking API. Internally, this library uses a workaround to create stream based sockets and then sets the required socket options on its underlying low level socket resource. -This is done because React PHP is built around the general purpose stream based API +This is done because ReactPHP is built around the general purpose stream based API and has only somewhat limited support for the low level socket API. -### Socket +### SocketInterface -The [`Factory`](#factory) creates instances of the `React\Datagram\Socket` class from the [react/datagram](https://github.com/reactphp/datagram) package. +The [`Factory`](#factory) creates instances of the `React\Datagram\SocketInterface` +from the [react/datagram](https://github.com/reactphp/datagram) package. +This means that you can use all its normal methods like so: ```php $socket->send($message, $address); @@ -140,6 +146,10 @@ extensions and supports running on legacy PHP 5.3 through current PHP 7+ and HHVM. It's *highly recommended to use PHP 7+* for this project. +The [`createSender()`](#createsender) method works on all supported platforms +without any additional requirements. However, the [`createReceiver()`](#createreceiver) +method requires PHP 5.4 (or up) and `ext-sockets`. See above for more details. + ## Tests To run the test suite, you first need to clone this repo and then install all diff --git a/composer.json b/composer.json index 31f670d..2866c2c 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,6 @@ "phpunit/phpunit": "^6.0 || ^5.7 || ^4.8.35" }, "suggest": { - "php": "PHP 5.4+ is required for listening on multicast addresses (socket options to send IGMP announcements)", - "ext-sockets": "Low level socket API required for listening on multicast addresses (socket options to send IGMP announcements)" + "ext-sockets": "Requires PHP 5.4+ and the low level socket API for listening on multicast addresses (socket options to send IGMP announcements)" } } diff --git a/examples/ssdp.php b/examples/ssdp.php index 514f9f6..c92a7d5 100644 --- a/examples/ssdp.php +++ b/examples/ssdp.php @@ -1,6 +1,6 @@ createSender(); // dump all incoming messages -$sender->on('message', function ($data, $remote) use ($hex) { +$sender->on('message', function ($data, $remote) { echo 'Received from ' . $remote . PHP_EOL; echo $data . PHP_EOL; }); @@ -24,7 +24,7 @@ $sender->pause(); }); -// send a discovery message that all upnp/ssdp aware devices will respond to +// send a discovery message that all UPnP/SSDP aware devices will respond to $data = "M-SEARCH * HTTP/1.1\r\n"; $data .= "HOST: " . $address . "\r\n"; $data .= "MAN: \"ssdp:discover\"\r\n"; diff --git a/src/Factory.php b/src/Factory.php index 2e9cc25..333e790 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -11,11 +11,44 @@ class Factory { private $loop; + /** + * The `Factory` is responsible for creating your [`SocketInterface`](#socketinterface) instances. + * It also registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage). + * + * ```php + * $loop = React\EventLoop\Factory::create(); + * $factory = new Factory($loop); + * ``` + * + * @param LoopInterface $loop + */ public function __construct(LoopInterface $loop) { $this->loop = $loop; } + /** + * Creates a socket capable of sending outgoing multicast datagrams and receiving + * incoming unicast responses. It returns a [`SocketInterface`](#socketinterface) instance. + * + * ```php + * $socket = $factory->createSender(); + * + * // send a multicast message to everybody listening on the given address + * $socket->send('hello?', '224.10.20.30:4050'); + * + * // report incoming unicast replies + * $socket->on('message', function ($data, $address) { + * echo 'received ' . strlen($data) . ' bytes from ' . $address . PHP_EOL; + * }); + * ``` + * + * This method works on PHP versions as old as PHP 5.3 (and up), as its socket API has always been + * [level 1 multicast conformant](https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html#ss2.2). + * + * @return \React\Datagram\SocketInterface + * @throws RuntimeException + */ public function createSender() { $stream = @stream_socket_server('udp://0.0.0.0:0', $errno, $errstr, STREAM_SERVER_BIND); @@ -26,6 +59,43 @@ public function createSender() return new DatagramSocket($this->loop, $stream); } + /** + * Creates a socket capable of receiving incoming multicast datagrams and sending + * outgoing unicast or multicast datagrams. It returns a [`SocketInterface`](#socketinterface) instance. + * + * ```php + * $socket = $factory->createReceiver('224.10.20.30:4050'); + * + * // report incoming multicast messages + * $socket->on('message', function ($data, $remote) use ($socket) { + * echo 'Sending back ' . strlen($data) . ' bytes to ' . $remote . PHP_EOL; + * + * // send a unicast reply to the remote + * $socket->send($data, $remote); + * }); + * ``` + * + * This method requires PHP 5.4 (or up) and `ext-sockets`. + * Otherwise, it will throw a `BadMethodCallException`. + * This is a requirement because receiving multicast datagrams requires a + * [level 2 multicast conformant](https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html#ss2.2) + * socket API. + * The required multicast socket options and constants have been added with + * [PHP 5.4](http://php.net/manual/en/migration54.global-constants.php) (and up). + * These options are only available to the low level socket API (ext-sockets), not + * to the newer stream based networking API. + * + * Internally, this library uses a workaround to create stream based sockets + * and then sets the required socket options on its underlying low level socket + * resource. + * This is done because ReactPHP is built around the general purpose stream based API + * and has only somewhat limited support for the low level socket API. + * + * @param string $address + * @return \React\Datagram\SocketInterface + * @throws BadMethodCallException + * @throws RuntimeException + */ public function createReceiver($address) { if (!defined('MCAST_JOIN_GROUP')) {