Skip to content

Commit 62a939f

Browse files
committed
Data callback is allowed to throw Exception
1 parent e1ddc34 commit 62a939f

3 files changed

Lines changed: 85 additions & 10 deletions

File tree

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,23 @@ $through->on('data', $this->expectCallableOnceWith("[2, true]\n"));
972972
$through->write(array(2, true));
973973
```
974974

975+
The callback function is allowed to throw an `Exception`. In this case,
976+
the stream will emit an `error` event and then [`close()`](#close-1) the stream.
977+
978+
```php
979+
$through = new ThroughStream(function ($data) {
980+
if (!is_string($data)) {
981+
throw new \UnexpectedValueException('Only strings allowed');
982+
}
983+
return $data;
984+
});
985+
$through->on('error', $this->expectCallableOnce()));
986+
$through->on('close', $this->expectCallableOnce()));
987+
$through->on('data', $this->expectCallableNever()));
988+
989+
$through->write(2);
990+
```
991+
975992
## Usage
976993
```php
977994
$loop = React\EventLoop\Factory::create();

src/ThroughStream.php

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@
5151
* $through->write(array(2, true));
5252
* ```
5353
*
54+
* The callback function is allowed to throw an `Exception`. In this case,
55+
* the stream will emit an `error` event and then [`close()`](#close-1) the stream.
56+
*
57+
* ```php
58+
* $through = new ThroughStream(function ($data) {
59+
* if (!is_string($data)) {
60+
* throw new \UnexpectedValueException('Only strings allowed');
61+
* }
62+
* return $data;
63+
* });
64+
* $through->on('error', $this->expectCallableOnce()));
65+
* $through->on('close', $this->expectCallableOnce()));
66+
* $through->on('data', $this->expectCallableNever()));
67+
*
68+
* $through->write(2);
69+
* ```
70+
*
5471
* @see WritableStreamInterface::write()
5572
* @see WritableStreamInterface::end()
5673
* @see DuplexStreamInterface::close()
@@ -109,7 +126,18 @@ public function write($data)
109126
return false;
110127
}
111128

112-
$this->emit('data', array($this->filter($data)));
129+
if ($this->callback !== null) {
130+
try {
131+
$data = call_user_func($this->callback, $data);
132+
} catch (\Exception $e) {
133+
$this->emit('error', array($e));
134+
$this->close();
135+
136+
return false;
137+
}
138+
}
139+
140+
$this->emit('data', array($data));
113141

114142
if ($this->paused) {
115143
$this->drain = true;
@@ -127,6 +155,11 @@ public function end($data = null)
127155

128156
if (null !== $data) {
129157
$this->write($data);
158+
159+
// return if write() already caused the stream to close
160+
if (!$this->writable) {
161+
return;
162+
}
130163
}
131164

132165
$this->readable = false;
@@ -153,13 +186,4 @@ public function close()
153186

154187
$this->emit('close');
155188
}
156-
157-
private function filter($data)
158-
{
159-
if ($this->callback !== null) {
160-
$data = call_user_func($this->callback, $data);
161-
}
162-
163-
return $data;
164-
}
165189
}

tests/ThroughStreamTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,40 @@ public function itShouldEmitAnyDataWrittenToItPassedThruCallback()
5252
$through->write('foo');
5353
}
5454

55+
/** @test */
56+
public function itShouldEmitErrorAndCloseIfCallbackThrowsException()
57+
{
58+
$through = new ThroughStream(function () {
59+
throw new \RuntimeException();
60+
});
61+
$through->on('error', $this->expectCallableOnce());
62+
$through->on('close', $this->expectCallableOnce());
63+
$through->on('data', $this->expectCallableNever());
64+
$through->on('end', $this->expectCallableNever());
65+
66+
$through->write('foo');
67+
68+
$this->assertFalse($through->isReadable());
69+
$this->assertFalse($through->isWritable());
70+
}
71+
72+
/** @test */
73+
public function itShouldEmitErrorAndCloseIfCallbackThrowsExceptionOnEnd()
74+
{
75+
$through = new ThroughStream(function () {
76+
throw new \RuntimeException();
77+
});
78+
$through->on('error', $this->expectCallableOnce());
79+
$through->on('close', $this->expectCallableOnce());
80+
$through->on('data', $this->expectCallableNever());
81+
$through->on('end', $this->expectCallableNever());
82+
83+
$through->end('foo');
84+
85+
$this->assertFalse($through->isReadable());
86+
$this->assertFalse($through->isWritable());
87+
}
88+
5589
/** @test */
5690
public function itShouldReturnFalseForAnyDataWrittenToItWhenPaused()
5791
{

0 commit comments

Comments
 (0)