Skip to content

Commit d5a88ba

Browse files
authored
Merge pull request #22 from clue-labs/default-loop
Simplify usage by supporting new default loop
2 parents 3807456 + 13eaceb commit d5a88ba

File tree

5 files changed

+68
-53
lines changed

5 files changed

+68
-53
lines changed

README.md

+17-18
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,12 @@ Once [installed](#install), you can use the following code to stream messages
2323
from any Server-Sent Events (SSE) server endpoint:
2424

2525
```php
26-
$loop = Factory::create();
27-
$es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', $loop);
26+
$es = new Clue\React\EventSource\EventSource('https://example.com/stream.php');
2827

2928
$es->on('message', function (Clue\React\EventSource\MessageEvent $message) {
3029
//$data = json_decode($message->data);
3130
var_dump($message);
3231
});
33-
34-
$loop->run();
3532
```
3633

3734
See the [examples](examples).
@@ -46,36 +43,38 @@ The `EventSource` object works very similar to the one found in common
4643
web browsers. Unless otherwise noted, it follows the same semantics as defined
4744
under https://html.spec.whatwg.org/multipage/server-sent-events.html
4845

49-
It requires the URL to the remote Server-Sent Events (SSE) endpoint and also
50-
registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage)
51-
in order to handle async HTTP requests.
46+
Its constructor simply requires the URL to the remote Server-Sent Events (SSE) endpoint:
5247

5348
```php
54-
$loop = React\EventLoop\Factory::create();
55-
56-
$es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', $loop);
49+
$es = new Clue\React\EventSource\EventSource('https://example.com/stream.php');
5750
```
5851

52+
This class takes an optional `LoopInterface|null $loop` parameter that can be used to
53+
pass the event loop instance to use for this object. You can use a `null` value
54+
here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
55+
This value SHOULD NOT be given unless you're sure you want to explicitly use a
56+
given event loop instance.
57+
5958
If you need custom connector settings (DNS resolution, TLS parameters, timeouts,
6059
proxy servers etc.), you can explicitly pass a custom instance of the
6160
[`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface)
6261
to the [`Browser`](https://github.com/reactphp/http#browser) instance
6362
and pass it as an additional argument to the `EventSource` like this:
6463

6564
```php
66-
$connector = new React\Socket\Connector($loop, array(
65+
$connector = new React\Socket\Connector(null, [
6766
'dns' => '127.0.0.1',
68-
'tcp' => array(
67+
'tcp' => [
6968
'bindto' => '192.168.10.1:0'
70-
),
71-
'tls' => array(
69+
],
70+
'tls' => [
7271
'verify_peer' => false,
7372
'verify_peer_name' => false
74-
)
75-
));
76-
$browser = new React\Http\Browser($loop, $connector);
73+
]
74+
]);
75+
$browser = new React\Http\Browser(null, $connector);
7776

78-
$es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', $loop, $browser);
77+
$es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', null, $browser);
7978
```
8079

8180
## Install

composer.json

+9-9
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@
77
"authors": [
88
{
99
"name": "Christian Lück",
10-
"email": "christian@lueck.tv"
10+
"email": "christian@clue.engineering"
1111
}
1212
],
13-
"autoload": {
14-
"psr-4": { "Clue\\React\\EventSource\\": "src/" }
15-
},
16-
"autoload-dev": {
17-
"psr-4": { "Clue\\Tests\\React\\EventSource\\": "tests/" }
18-
},
1913
"require": {
2014
"php": ">=5.4",
2115
"evenement/evenement": "^3.0 || ^2.0",
22-
"react/event-loop": "^1.0",
23-
"react/http": "^1.0"
16+
"react/event-loop": "^1.2",
17+
"react/http": "^1.4"
2418
},
2519
"require-dev": {
2620
"phpunit/phpunit": "^9.3 || ^5.7 || ^4.8.35"
21+
},
22+
"autoload": {
23+
"psr-4": { "Clue\\React\\EventSource\\": "src/" }
24+
},
25+
"autoload-dev": {
26+
"psr-4": { "Clue\\Tests\\React\\EventSource\\": "tests/" }
2727
}
2828
}

examples/stream.php

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
<?php
22

3-
use React\EventLoop\Factory;
4-
use Clue\React\EventSource\EventSource;
5-
63
require __DIR__ . '/../vendor/autoload.php';
74

85
if (!isset($argv[1]) || isset($argv[2])) {
96
exit('Usage error: stream.php <uri>' . PHP_EOL);
107
}
118

12-
$loop = Factory::create();
13-
$es = new EventSource($argv[1], $loop);
9+
$es = new Clue\React\EventSource\EventSource($argv[1]);
1410

15-
$es->on('message', function ($message) {
11+
$es->on('message', function (Clue\React\EventSource\MessageEvent $message) {
1612
//$data = json_decode($message->data);
1713
var_dump($message);
1814
});
@@ -22,11 +18,9 @@
2218
});
2319

2420
$es->on('error', function (Exception $e) use ($es) {
25-
if ($es->readyState === EventSource::CLOSED) {
21+
if ($es->readyState === Clue\React\EventSource\EventSource::CLOSED) {
2622
echo 'Permanent error: ' . $e->getMessage() . PHP_EOL;
2723
} else {
2824
echo 'Temporary error: ' . $e->getMessage() . PHP_EOL;
2925
}
3026
});
31-
32-
$loop->run();

src/EventSource.php

+26-17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Evenement\EventEmitter;
66
use Psr\Http\Message\ResponseInterface;
7+
use React\EventLoop\Loop;
78
use React\EventLoop\LoopInterface;
89
use React\Http\Browser;
910
use React\Stream\ReadableStreamInterface;
@@ -15,36 +16,38 @@
1516
* web browsers. Unless otherwise noted, it follows the same semantics as defined
1617
* under https://html.spec.whatwg.org/multipage/server-sent-events.html
1718
*
18-
* It requires the URL to the remote Server-Sent Events (SSE) endpoint and also
19-
* registers everything with the main [`EventLoop`](https://github.com/reactphp/event-loop#usage)
20-
* in order to handle async HTTP requests.
19+
* Its constructor simply requires the URL to the remote Server-Sent Events (SSE) endpoint:
2120
*
2221
* ```php
23-
* $loop = React\EventLoop\Factory::create();
24-
*
25-
* $es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', $loop);
22+
* $es = new Clue\React\EventSource\EventSource('https://example.com/stream.php');
2623
* ```
2724
*
25+
* This class takes an optional `LoopInterface|null $loop` parameter that can be used to
26+
* pass the event loop instance to use for this object. You can use a `null` value
27+
* here in order to use the [default loop](https://github.com/reactphp/event-loop#loop).
28+
* This value SHOULD NOT be given unless you're sure you want to explicitly use a
29+
* given event loop instance.
30+
*
2831
* If you need custom connector settings (DNS resolution, TLS parameters, timeouts,
2932
* proxy servers etc.), you can explicitly pass a custom instance of the
3033
* [`ConnectorInterface`](https://github.com/reactphp/socket#connectorinterface)
3134
* to the [`Browser`](https://github.com/reactphp/http#browser) instance
3235
* and pass it as an additional argument to the `EventSource` like this:
3336
*
3437
* ```php
35-
* $connector = new React\Socket\Connector($loop, array(
38+
* $connector = new React\Socket\Connector(null, [
3639
* 'dns' => '127.0.0.1',
37-
* 'tcp' => array(
40+
* 'tcp' => [
3841
* 'bindto' => '192.168.10.1:0'
39-
* ),
40-
* 'tls' => array(
42+
* ],
43+
* 'tls' => [
4144
* 'verify_peer' => false,
4245
* 'verify_peer_name' => false
43-
* )
44-
* ));
45-
* $browser = new React\Http\Browser($loop, $connector);
46+
* ]
47+
* ]);
48+
* $browser = new React\Http\Browser(null, $connector);
4649
*
47-
* $es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', $loop, $browser);
50+
* $es = new Clue\React\EventSource\EventSource('https://example.com/stream.php', null, $browser);
4851
* ```
4952
*/
5053
class EventSource extends EventEmitter
@@ -78,18 +81,24 @@ class EventSource extends EventEmitter
7881
private $timer;
7982
private $reconnectTime = 3.0;
8083

81-
public function __construct($url, LoopInterface $loop, Browser $browser = null)
84+
/**
85+
* @param string $url
86+
* @param ?LoopInterface $loop
87+
* @param ?Browser $browser
88+
* @throws \InvalidArgumentException for invalid URL
89+
*/
90+
public function __construct($url, LoopInterface $loop = null, Browser $browser = null)
8291
{
8392
$parts = parse_url($url);
8493
if (!isset($parts['scheme'], $parts['host']) || !in_array($parts['scheme'], array('http', 'https'))) {
8594
throw new \InvalidArgumentException();
8695
}
8796

97+
$this->loop = $loop ?: Loop::get();
8898
if ($browser === null) {
89-
$browser = new Browser($loop);
99+
$browser = new Browser($this->loop);
90100
}
91101
$this->browser = $browser->withRejectErrorResponse(false);
92-
$this->loop = $loop;
93102
$this->url = $url;
94103

95104
$this->readyState = self::CONNECTING;

tests/EventSourceTest.php

+13
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ public function testConstructorThrowsIfUriArgumentIncludesInvalidScheme()
3434
new EventSource('ftp://example.com', $loop);
3535
}
3636

37+
public function testConstructWithoutLoopAssignsLoopAutomatically()
38+
{
39+
$es = new EventSource('http://example.invalid');
40+
41+
$ref = new \ReflectionProperty($es, 'loop');
42+
$ref->setAccessible(true);
43+
$loop = $ref->getValue($es);
44+
45+
$this->assertInstanceOf('React\EventLoop\LoopInterface', $loop);
46+
47+
$es->close();
48+
}
49+
3750
public function testConstructorCanBeCalledWithoutBrowser()
3851
{
3952
$loop = $this->getMockBuilder('React\EventLoop\LoopInterface')->getMock();

0 commit comments

Comments
 (0)