8000 Merge pull request #245 from clue-labs/warnings · reactphp/event-loop@4d3e4aa · GitHub
[go: up one dir, main page]

Skip to content

Commit 4d3e4aa

Browse files
authored
Merge pull request #245 from clue-labs/warnings
Improve default `StreamSelectLoop` to report any warnings for invalid streams passed to `stream_select()`
2 parents f84ddde + 2c8533c commit 4d3e4aa

File tree

2 files changed

+101
-2
lines changed

2 files changed

+101
-2
lines changed

src/StreamSelectLoop.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,28 @@ private function streamSelect(array &$read, array &$write, $timeout)
287287
}
288288
}
289289

290-
// suppress warnings that occur, when stream_select is interrupted by a signal
291-
$ret = @\stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
290+
/** @var ?callable $previous */
291+
$previous = \set_error_handler(function ($errno, $errstr) use (&$previous) {
292+
// suppress warnings that occur when `stream_select()` is interrupted by a signal
293+
$eintr = \defined('SOCKET_EINTR') ? \SOCKET_EINTR : 4;
294+
if ($errno === \E_WARNING && \strpos($errstr, '[' . $eintr .']: ') !== false) {
295+
return;
296+
}
297+
298+
// forward any other error to registered error handler or print warning
299+
return ($previous !== null) ? \call_user_func_array($previous, \func_get_args()) : false;
300+
});
301+
302+
try {
303+
$ret = \stream_select($read, $write, $except, $timeout === null ? null : 0, $timeout);
304+
\restore_error_handler();
305+
} catch (\Throwable $e) { // @codeCoverageIgnoreStart
306+
\restore_error_handler();
307+
throw $e;
308+
} catch (\Exception $e) {
309+
\restore_error_handler();
310+
throw $e;
311+
} // @codeCoverageIgnoreEnd
292312

293313
if ($except) {
294314
$write = \array_merge($write, $except);

tests/StreamSelectLoopTest.php

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,85 @@ public function testStreamSelectTimeoutEmulation()
4040
$this->assertGreaterThan(0.04, $interval);
4141
}
4242

43+
public function testStreamSelectReportsWarningForStreamWithFilter()
44+
{
45+
if (defined('HHVM_VERSION')) {
46+
$this->markTestSkipped('Not supported on legacy HHVM');
47+
}
48+
49+
$stream = tmpfile();
50+
stream_filter_append($stream, 'string.rot13');
51+
52+
$this->loop->addReadStream($stream, $this->expectCallableNever());
53+
54+
$loop = $this->loop;
55+
$this->loop->futureTick(function () use ($loop, $stream) {
56+
$loop->futureTick(function () use ($loop, $stream) {
57+
$loop->removeReadStream($stream);
58+
});
59+
});
60+
61+
$error = null;
62+
$previous = set_error_handler(function ($_, $errstr) use (&$error) {
63+
$error = $errstr;
64+
});
65+
66+
try {
67+
$this->loop->run();
68+
} catch (\ValueError $e) {
69+
// ignore ValueError for PHP 8+ due to empty stream array
70+
}
71+
72+
restore_error_handler();
73+
74+
$this->assertNotNull($error);
75+
76+
$now = set_error_handler(function () { });
77+
restore_error_handler();
78+
$this->assertEquals($previous, $now);
79+
}
80+
81+
public function testStreamSelectThrowsWhenCustomErrorHandlerThrowsForStreamWithFilter()
82+
{
83+
if (defined('HHVM_VERSION')) {
84+
$this->markTestSkipped('Not supported on legacy HHVM');
85+
}
86+
87+
$stream = tmpfile();
88+
stream_filter_append($stream, 'string.rot13');
89+
90+
$this->loop->addReadStream($stream, $this->expectCallableNever());
91+
92+
$loop = $this->loop;
93+
$this->loop->futureTick(function () use ($loop, $stream) {
94+
$loop->futureTick(function () use ($loop, $stream) {
95+
$loop->removeReadStream($stream);
96+
});
97+
});
98+
99+
$previous = set_error_handler(function ($_, $errstr) {
100+
throw new \RuntimeException($errstr);
101+
});
102+
103+
$e = null;
104+
try {
105+
$this->loop->run();
106+
restore_error_handler();
107+
$this->fail();
108+
} catch (\RuntimeException $e) {
109+
restore_error_handler();
110+
} catch (\ValueError $e) {
111+
restore_error_handler(); // PHP 8+
112+
$e = $e->getPrevious();
113+
}
114+
115+
$this->assertInstanceOf('RuntimeException', $e);
116+
117+
$now = set_error_handler(function () { });
118+
restore_error_handler();
119+
$this->assertEquals($previous, $now);
120+
}
121+
43122
public function signalProvider()
44123
{
45124
return array(

0 commit comments

Comments
 (0)
0