-
-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix error handling in Stream::getContents() #252
Conversation
guzzle/psr7 looks OK BTW: |
laminas/diactoros is affected: https://github.com/laminas/laminas-diactoros/blob/3.4.x/src/Stream.php#L282 /cc @weierophinney |
@nicolas-grekas is this a case for which we could write a test in |
slim/psr7 is affected: https://github.com/slimphp/Slim-Psr7/blob/4299f1650acad6df77fbaeb6f18c59e46af761b9/src/Stream.php#L357 /cc @l0gicgate |
ringcentral/psr7 affected too: https://github.com/ringcentral/psr7/blob/360faaec4b563958b673fb52bbe94e37f14bc686/src/Stream.php#L102 /cc @mtdowling |
workerman/psr7 affected: https://github.com/walkor/psr7/blob/e21521b72b5a891bd1a7a51376da12801863ce5b/src/Stream.php#L99 /cc @walkor |
httpsoft/http-message affected too: https://github.com/httpsoft/http-message/blob/6d86446b9d8f92cc06c60375d3e67cf3407a57a5/src/StreamTrait.php#L332 /cc @devanych |
f143df4
to
ff5243c
Compare
I added a test case at php-http/psr7-integration-tests#74 and updated the implementation to cover more situations (like guzzle does). |
9cf0693
to
aa17767
Compare
aa17767
to
8fb6d3f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lovely. Thank you for the tests and for checking all PRS7 implementation.
Well done!
Hi @nicolas-grekas, thanks for fixing such a lurking bug! @Nyholm, if possible, when can we expect a release with the fix? |
It was released in 1.8.1. |
\set_error_handler(static function ($type, $message) use (&$exception) { | ||
throw $exception = new \RuntimeException('Unable to read stream contents: ' . $message); | ||
}); | ||
|
||
try { | ||
return \stream_get_contents($this->stream); | ||
} catch (\Throwable $e) { | ||
throw $e === $exception ? $e : new \RuntimeException('Unable to read stream contents: ' . $e->getMessage(), 0, $e); | ||
} finally { | ||
\restore_error_handler(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand how setting an error handler fixes this issue when other tools and even Guzzle check for a false
return. Nothing about this code seems like it will actually solve the issue, because the return value of stream_get_contents
is not being checked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When stream_get_contents returns false, it first always triggers a PHP warning. Since we catch those in the error handler, the function will never really return false.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Considering that such behavior is not documented, why take that approach over checking the return value is false? Every other fix I've looked at around this issue is doing a $ret === false
check...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be dead code. Many things are not documented, especially regarding error handling. Returning false always means an error, and PHP has to trigger a warning to tell about the error so this is still solid IMHO. Of course, feel free to add the check on your side if you're not comfortable with the logic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be dead code.
Are you saying that checking === false
does not work, or are you saying that this monkey patching with the error handler identifies the impending return false
more quickly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a question to understand this patch a little better.
Why do you declare an "$exception" variable for the Exception although the catch block gets the same Exception instance? Is there some PHP specific logic / behavior behind this (that is not documented)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we remove this check, we would always wrap the exception thrown in the error handler in another exception. This check allows removing this needless wrapping exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, thanks. Then my next question is, why not move the set_error_handler
into the try-block to handle that (scope) in the same place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if set_error_handler fails for any reasons, we don't want restore_error_handler to be called
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And in PHP, try/catch does not create a scope anyway.
It might also be helpful to have a test case for this in http-factory-tests. |
Hi @nicolas-grekas, thanks 👍 |
stream_get_contents()
swallows errors. This creates issues like symfony/symfony#52490, where errors reported by the stream layer aren't propagated to user-land.This is even in the C source:
https://github.com/php/php-src/blob/311cae03e730c76aed343312319ed8cf1c37ade0/main/streams/streams.c#L1512