-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
WiP: Non buffered body #2504
WiP: Non buffered body #2504
Conversation
Is it worth doing this in Slim-Psr7 for use with Slim 4? |
I believe it is, yes. |
Looks good to me. We need a docs PR for it on Slim-Website. |
Where are you at with this @1ma do you need more feedback? Just a couple things from my end:
Thanks! |
My problem is that I don't see a satisfactory way to test the For this kind of thing I'd usually wrap the system under test in a |
I understand @1ma. After some thought I think this PR is fine, it's an opt-in use with some gotchas described above. Could you make a PR on the |
Great, will do. |
Let's port this to Slim-Psr7 as well @1ma. I'll try and do the docs for it actually this weekend, I'll have you review them. |
Works for me. I don't intend to shy away from the work, but writing in English is not really my forte :) Thank you. |
This PR implements the proposal made in #2481 as a new subtype of
StreamInterface
.Code
The implementation of most methods is trivial because the interface was designed for classes wrapping an internal stream, and in this case there simply isn't one.
write($string)
essentially destroys all active Output Buffering levels (but saving their contents), then prepends this to$string
and flushes everything to the webserver. Successive calls toNonBufferedBody::write()
won't destroy anything else (unless the programmer runsob_start()
andecho
in-between calls for any reason) and flush$string
right away.Any previously buffered data is displayed in a FIFO order. In other words, data echoed at the output buffer level 1 will be printed before data echoed at level 2.
Additionally, I had to modify
Response::withHeader($name, $value)
to send the headers eagerly when the body is of typeNonBufferedBody
, otherwise the framework won't attempt to send the headers until the end stages ofApp::run()
. This change is quite delicate but I believe it cannot break any previous code becauseNonBufferedBody
simply did not exist before.Working example
Gotchas
HTTP headers must be sent before the content
When pushing data to the webserver while still running PHP code, one must send the headers first. Calling
Response::withHeader($name, $value)
after any call toNonBufferedBody::write($string)
will trigger anCannot modify header information - headers already sent
PHP error. Obviously, after sending the first chunks of data you cannot push more headers in the middle of the body.NonBufferedBody must be set on the response before setting any HTTP header
Since
Response
now looks at the type of the$body
attribute to determine whether to send HTTP headers eagerly or not, it is imperative to replace the underlyingStreamInterface
of the response withResponse::withBody(new NonBufferedBody)
before doing any call toResponse::withHeader($name, $value)
.I believe that this issue is best avoided with a "non-buffered middleware" that can be attached to the routes that need to behave this way, like in the example.
Some middlewares may break
As noted by @llvdl in the original issue, outgoing middlewares won't be able to modify the body of unbuffered responses nor add any HTTP headers. Only appending new data to the body will be possible in this scenario, which is generally not terribly useful.
Webservers buffer output by default
Even after calling PHP's
flush()
function webservers usually buffer the output on their own.Nginx + php-fpm
Nginx has the
fastcgi_buffering off;
directive to explicitly disable output buffering. Additionally, you can send a specialX-Accel-Buffering
header set tono
to do it dynamically from PHP (see example).Apache
??? TBD
TODO