开发者

Fire-and-forget in PHP

开发者 https://www.devze.com 2023-03-24 03:34 出处:网络
Final update Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D

Final update

Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D


I'm trying to achieve fire-and-forget like functionality in PHP.

From php.net

<?php
ignore_user_abort(true);
header("Content-Length: 4");
header("Connection: Close");
echo "abcd";
flush();

sleep(5);    
echo "Text user should not see"; // because it should have terminated
?>

This works if I open the script with a browser. (shows "abcd"). But if I open it with file_get_contents or some stream library it will wait for ~5 seconds and show the second text as well.

I'm using PHP 5.2.11 / Apache 2.0


Update

I seems there is some confusion about what I'm trying to accomplish.

I don't want to hide output using output buffers (that's stupid). I want to have the client terminate before the server starts a possibly lengthy process (sleep(5)) and I don't want the client to wait for it (this is what fire-and-forget means, sort off). The use of output buffers is merely a side effect. I've amended the sample code without the use of output buffers.

What I don't understand is: why does this script behave differently when accessing it from the browser vs. fetching it in PHP with file_get_contents("http://dev/test.php") or some stream library? What I've seen in testing is that for开发者_如何转开发 instance stream_get_contents will actually block for 5 seconds before it returns any output at all, the is quite the opposite of what I want.

Update2

Some more results:

  • The browser somehow responds to the flush(). I can't figure out how to replicate this behavior with streams in PHP, my streams keep blocking.
  • I've tried fread and found that it behaves similar to stream_get_contents.
  • Specifying a maxlength has no effect, it will still block for ~5 seconds.
  • Changing the blocking mode has no effect (other than generating a bunch more calls to stream_get_contents()). It will wait ~5 seconds before returning anything.
  • stream_set_read_buffer has no effect (tested on a PHP 5.3.5 sever)


The second portion of text is showing up because you're stopping output buffering with ob_end_flush() and ob_end_clean(). When that happens PHP outputs content as normal. Try something like the following:

<?php

ob_start(); // turn on output buffering
print "Text the user will see.";
ob_flush(); // send above output to the user and keep output buffering on

print "Text the user will never see";

ob_end_clean(); // empty the buffer and turn off output buffering. your script should end here.

?>

It's important for ob_end_clean() to appear at the end of the script. It empties the buffer and does not send its contents to the user, thus keeping everything after ob_flush() hidden.


How do you access the script using file_get_contents? How do you access it with your browser? If you access the script without "http://", of course it will never get executed. Use the same URL as in the browser.

Edit:

The browser will render the page even before the connection is closed. Even if you flush, I don't think the connection is closed. You can fire up Wireshark and check. stream_get_contents and file_get_contents will block until they have all the output. Even if you flushed, they can't be sure that there isn't more content. Since the content-length header didn't seem to make {file,stream}_get_contents return earlier, you probably need to implement your own buffering, ala. fopen, read, fclose.


Seems like I did make a very simple error. Since I already have a stream implementation I can just not start reading from the stream :D

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号