Skip to content

Commit 2d0245c

Browse files
committed
Improve error reporting when custom error handler is used
1 parent 745a49c commit 2d0245c

3 files changed

Lines changed: 90 additions & 17 deletions

File tree

src/functions.php

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -104,13 +104,21 @@
104104
*/
105105
function append($stream, $callback, $read_write = STREAM_FILTER_ALL)
106106
{
107-
$ret = @\stream_filter_append($stream, register(), $read_write, $callback);
107+
$errstr = '';
108+
\set_error_handler(function ($_, $error) use (&$errstr) {
109+
// Match errstr from PHP's warning message.
110+
// stream_filter_append() expects parameter 1 to be resource,...
111+
$errstr = $error;
112+
});
113+
114+
$ret = \stream_filter_append($stream, register(), $read_write, $callback);
115+
116+
\restore_error_handler();
108117

109118
// PHP 8 throws above on type errors, older PHP and memory issues can throw here
110119
// @codeCoverageIgnoreStart
111120
if ($ret === false) {
112-
$error = \error_get_last() + array('message' => '');
113-
throw new \RuntimeException('Unable to append filter: ' . $error['message']);
121+
throw new \RuntimeException('Unable to append filter: ' . $errstr);
114122
}
115123
// @codeCoverageIgnoreEnd
116124

@@ -147,13 +155,21 @@ function append($stream, $callback, $read_write = STREAM_FILTER_ALL)
147155
*/
148156
function prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)
149157
{
150-
$ret = @\stream_filter_prepend($stream, register(), $read_write, $callback);
158+
$errstr = '';
159+
\set_error_handler(function ($_, $error) use (&$errstr) {
160+
// Match errstr from PHP's warning message.
161+
// stream_filter_prepend() expects parameter 1 to be resource,...
162+
$errstr = $error;
163+
});
164+
165+
$ret = \stream_filter_prepend($stream, register(), $read_write, $callback);
166+
167+
\restore_error_handler();
151168

152169
// PHP 8 throws above on type errors, older PHP and memory issues can throw here
153170
// @codeCoverageIgnoreStart
154171
if ($ret === false) {
155-
$error = \error_get_last() + array('message' => '');
156-
throw new \RuntimeException('Unable to prepend filter: ' . $error['message']);
172+
throw new \RuntimeException('Unable to prepend filter: ' . $errstr);
157173
}
158174
// @codeCoverageIgnoreEnd
159175

@@ -242,16 +258,25 @@ function prepend($stream, $callback, $read_write = STREAM_FILTER_ALL)
242258
function fun($filter, $parameters = null)
243259
{
244260
$fp = \fopen('php://memory', 'w');
261+
262+
$errstr = '';
263+
\set_error_handler(function ($_, $error) use (&$errstr) {
264+
// Match errstr from PHP's warning message.
265+
// stream_filter_append() expects parameter 1 to be resource,...
266+
$errstr = $error;
267+
});
268+
245269
if (\func_num_args() === 1) {
246-
$filter = @\stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE);
270+
$filter = \stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE);
247271
} else {
248-
$filter = @\stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE, $parameters);
272+
$filter = \stream_filter_append($fp, $filter, \STREAM_FILTER_WRITE, $parameters);
249273
}
250274

275+
\restore_error_handler();
276+
251277
if ($filter === false) {
252278
\fclose($fp);
253-
$error = \error_get_last() + array('message' => '');
254-
throw new \RuntimeException('Unable to access built-in filter: ' . $error['message']);
279+
throw new \RuntimeException('Unable to access built-in filter: ' . $errstr);
255280
}
256281

257282
// append filter function which buffers internally
@@ -301,10 +326,20 @@ function fun($filter, $parameters = null)
301326
*/
302327
function remove($filter)
303328
{
304-
if (@\stream_filter_remove($filter) === false) {
329+
$errstr = '';
330+
\set_error_handler(function ($_, $error) use (&$errstr) {
331+
// Match errstr from PHP's warning message.
332+
// stream_filter_remove() expects parameter 1 to be resource,...
333+
$errstr = $error;
334+
});
335+
336+
$ret = \stream_filter_remove($filter);
337+
338+
\restore_error_handler();
339+
340+
if ($ret === false) {
305341
// PHP 8 throws above on type errors, older PHP and memory issues can throw here
306-
$error = \error_get_last();
307-
throw new \RuntimeException('Unable to remove filter: ' . $error['message']);
342+
throw new \RuntimeException('Unable to remove filter: ' . $errstr);
308343
}
309344
}
310345

tests/FilterTest.php

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,37 +335,66 @@ public function testAppendThrowsShouldTriggerEndButIgnoreExceptionDuringEnd()
335335
$this->assertContainsString('test', $errors[0]);
336336
}
337337

338-
public function testAppendInvalidStreamIsRuntimeError()
338+
public function testAppendInvalidStreamIsRuntimeErrorWithoutCallingCustomErrorHandler()
339339
{
340340
if (PHP_VERSION >= 8) $this->markTestSkipped('Not supported on PHP 8+ (PHP 8 throws TypeError automatically)');
341341

342342
$this->setExpectedException('RuntimeException');
343343
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid stream)');
344+
345+
$error = null;
346+
set_error_handler(function ($_, $errstr) use (&$error) {
347+
$error = $errstr;
348+
});
349+
344350
StreamFilter\append(false, function () { });
351+
352+
restore_error_handler();
353+
$this->assertNull($error);
345354
}
346355

347-
public function testPrependInvalidStreamIsRuntimeError()
356+
public function testPrependInvalidStreamIsRuntimeErrorWithoutCallingCustomErrorHandler()
348357
{
349358
if (PHP_VERSION >= 8) $this->markTestSkipped('Not supported on PHP 8+ (PHP 8 throws TypeError automatically)');
350359

351360
$this->setExpectedException('RuntimeException');
352361
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid stream)');
362+
363+
$error = null;
364+
set_error_handler(function ($_, $errstr) use (&$error) {
365+
$error = $errstr;
366+
});
367+
353368
StreamFilter\prepend(false, function () { });
369+
370+
restore_error_handler();
371+
$this->assertNull($error);
354372
}
355373

356-
public function testRemoveInvalidFilterIsRuntimeError()
374+
public function testRemoveInvalidFilterIsRuntimeErrorWithoutCallingCustomErrorHandler()
357375
{
358376
if (PHP_VERSION >= 8) $this->markTestSkipped('Not supported on PHP 8+ (PHP 8 throws TypeError automatically)');
359377

360378
$this->setExpectedException('RuntimeException', 'Unable to remove filter: stream_filter_remove() expects parameter 1 to be resource, ');
361379
if (defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on HHVM (does not reject invalid filters)');
380+
381+
$error = null;
382+
set_error_handler(function ($_, $errstr) use (&$error) {
383+
$error = $errstr;
384+
});
385+
362386
StreamFilter\remove(false);
387+
388+
$error = null;
389+
set_error_handler(function ($_, $errstr) use (&$error) {
390+
$error = $errstr;
391+
});
363392
}
364393

365394
/**
366395
* @depends testAppendEndEventWillBeCalledOnRemove
367396
*/
368-
public function testRemoveThrowsWhenAppendThrowsOnEvent()
397+
public function testRemoveThrowsWhenAppendThrowsOnEventWithoutCallingCustomErrorHandler()
369398
{
370399
if (PHP_VERSION < 7 || defined('HHVM_VERSION')) $this->markTestSkipped('Not supported on legacy PHP (engine crashes)');
371400

tests/FunTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,16 @@ public function testFunWriteAfterCloseRot13()
3838
public function testFunInvalid()
3939
{
4040
$this->setExpectedException('RuntimeException');
41+
42+
$error = null;
43+
set_error_handler(function ($_, $errstr) use (&$error) {
44+
$error = $errstr;
45+
});
46+
4147
Filter\fun('unknown');
48+
49+
restore_error_handler();
50+
$this->assertNull($error);
4251
}
4352

4453
public function testFunInBase64()

0 commit comments

Comments
 (0)