-
Notifications
You must be signed in to change notification settings - Fork 62
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
Message: support body to be a callback #65
Conversation
df35734
to
161dd98
Compare
@@ -71,6 +71,11 @@ static function sendResponse(ResponseInterface $response) { | |||
$body = $response->getBody(); | |||
if (is_null($body)) return; | |||
|
|||
if (is_callable($body)) { | |||
$body(); |
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.
shouldn't we pass the $response object into the callback so it can handle the case of existing Content-Length header etc? (like wie do for non-callback bodies a few lines below)
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'm not sure if it's necessary - i think that the same layer which is setting the headers (e.g. content length) is also setting the body (callback), so it can also pass the content length to the callback (e.g. function() use ($contentLength) {...}
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.
looking at your generateMultiStatus example tells me that this assumption doesnt work?
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 can imagine getting around that (e.g. passing some more context in CorePlugin::httpPropFind()
into the generateMultiStatus()
method), however, i've added the $response
as an argument to the body callback function (see 47c4b1f)
i usually prefer not to add features which are not immediately required, however i understand you want to make it simple for the callback to do the same as Sapi::sendResponse()
does without any other "external information"
let me know what you guys think now :)
thanks!
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'm with @petrkotek on this. generateMultiStatus
really should move out of Sabre\DAV\Server
anyway. It's a leftover from the extremely early days.
Usually I think it's reasonable to assume that the thing that sets the body, also has the ability to access the response object.
can i get some 👀 on this? thanks! |
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.
Aside from those small suggestions, I'm happy with the change. I do think that its time to give the sabre/http package a major version bump though. So I might need some patience until a full release. That doesn't mean we can't have master of sabre/http and sabre/dav reflect these changes though, so hopefully you're able to use just that.
@@ -74,6 +77,9 @@ function getBodyAsString() { | |||
if (is_null($body)) { | |||
return ''; | |||
} | |||
if (is_callable($body)) { | |||
throw new \UnexpectedValueException('Callback to string not supported'); |
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.
We can definitely support this with output buffering.
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.
ob_start
/ ob_get_clean
and stuff ;)
@@ -53,6 +53,9 @@ function getBodyAsStream() { | |||
rewind($stream); | |||
return $stream; | |||
} | |||
if (is_callable($body)) { | |||
throw new \UnexpectedValueException('Callback to stream not supported'); |
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.
This can also definitely be done, and would be nice to have! With output buffering this can be turned into a stream.
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.
More than 'nice to have' actually ;) I think this needs to be an absolute requirement to not break existing stuff using the HTTP response.
6cbe577
to
b907b64
Compare
…se when is a callback function
b907b64
to
a95cb49
Compare
so in the last commit i implemented I'm not sure if there is more effective way how to convert the I also removed the commit 47c4b1f which was changing |
throw new \RuntimeException('Cannot start output buffering'); | ||
} | ||
$callback(); | ||
$content = ob_get_contents(); |
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.
ob_get_clean()
would do the same in 1 step
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.
yay, very good point. i don't use this ob_*
stuff too much :) updated
(note that i can always rebase & squash the commits if needed)
private function captureCallbackOutput($callback) | ||
{ | ||
$success = ob_start(); | ||
if ($success === 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.
You don't really need this check. I've never encountered a situation where this can possibly fail.
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.
✅
{ | ||
return function() { | ||
return function() use ($content) { | ||
$fd = fopen('php://output', 'r+'); |
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.
This is a nitpick and not that important, but instead of opening a stream to php://output
and writing the contents there, you can just do:
echo $content;
.
And this will have an identical effect, and possibly even a bit faster. Not important for unittests though.
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.
thanks, good suggestions; fixed ✅
… doesn't fail in common cases; MessageTest: use echo rather then complicated fwrite to php://output
What?
In
Sabre\HTTP\MessageInterface
(andSabre\HTTP\Message
), support$body
to be a callback outputting the body intophp://output
Why?
Some responses may be big, so it's not scalable to hold the whole response in a variable (in memory), see https://github.com/fruux/sabre-dav/pull/890.
Limitation
getBodyAsString()
andgetBodyAsStream()
doesn't currently work with the callback (throws an exception). It's implementation seem to be a bit complicated - mostly because we are able to produce output only once, but i'm open to ideas.Usage example from sabre/dav
Snippet from
sabre/dav
to makegenerateMultiStatus()
method work as a callback: