php 发送http响应后继续处理php
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15273570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
continue processing php after sending http response
提问by user1700214
My script is called by server. From server I'll receive ID_OF_MESSAGEand TEXT_OF_MESSAGE.
我的脚本由服务器调用。从服务器我会收到ID_OF_MESSAGE和TEXT_OF_MESSAGE。
In my script I'll handle incoming text and generate response with params: ANSWER_TO_IDand RESPONSE_MESSAGE.
在我的脚本中,我将处理传入的文本并使用 params:ANSWER_TO_ID和RESPONSE_MESSAGE.
The problem is that I'm sending response to incomming "ID_OF_MESSAGE", but server which send me message to handle will set his message as delivered to me (It means I can send him response to that ID), after receiving http response 200.
问题是我正在向 incomming 发送响应"ID_OF_MESSAGE",但是在收到 http 响应 200 后,向我发送消息以处理的服务器会将他的消息设置为已发送给我(这意味着我可以向他发送对该 ID 的响应)。
One of solution is to save message to database and make some cron which will be running each minute, but I need to generate response message immediately.
解决方案之一是将消息保存到数据库并制作一些每分钟运行一次的 cron,但我需要立即生成响应消息。
Is there some solution how to send to server http response 200 and than continue executing php script?
是否有一些解决方案如何发送到服务器 http 响应 200 然后继续执行 php 脚本?
Thank you a lot
非常感谢
回答by vcampitelli
Yes. You can do this:
是的。你可以这样做:
ignore_user_abort(true);
set_time_limit(0);
ob_start();
// do initial processing here
echo $response; // send the response
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
// now the request is sent to the browser, but the script is still running
// so, you can continue...
回答by Kosta Kontos
I've seen a lot of responses on here that suggest using ignore_user_abort(true);but this code is not necessary. All this does is ensure your script continues executing before a response is sent in the event that the user aborts (by closing their browser or pressing escape to stop the request). But that's not what you're asking. You're asking to continue execution AFTER a response is sent. All you need is the following:
我在这里看到很多建议使用的回复,ignore_user_abort(true);但此代码不是必需的。所有这些都是为了确保您的脚本在用户中止(通过关闭浏览器或按 Esc 键停止请求)发送响应之前继续执行。但这不是你要问的。您要求在发送响应后继续执行。您只需要以下内容:
// Buffer all upcoming output...
ob_start();
// Send your response.
echo "Here be response";
// Get the size of the output.
$size = ob_get_length();
// Disable compression (in case content length is compressed).
header("Content-Encoding: none");
// Set the content length of the response.
header("Content-Length: {$size}");
// Close the connection.
header("Connection: close");
// Flush all output.
ob_end_flush();
ob_flush();
flush();
// Close current session (if it exists).
if(session_id()) session_write_close();
// Start your background work here.
...
If you're concerned that your background work will take longer than PHP's default script execution time limit, then stick set_time_limit(0);at the top.
如果您担心您的后台工作将花费比 PHP 的默认脚本执行时间限制更长的时间,那么请坚持 set_time_limit(0);在顶部。
回答by DarkNeuron
If you're using FastCGI processing or PHP-FPM, you can:
如果您使用 FastCGI 处理或 PHP-FPM,您可以:
session_write_close(); //close the session
ignore_user_abort(true); //Prevent echo, print, and flush from killing the script
fastcgi_finish_request(); //this returns 200 to the user, and processing continues
// do desired processing ...
$expensiveCalulation = 1+1;
error_log($expensiveCalculation);
Source: https://www.php.net/manual/en/function.fastcgi-finish-request.php
来源:https: //www.php.net/manual/en/function.fastcgi-finish-request.php
PHP issue #68722: https://bugs.php.net/bug.php?id=68772
回答by Ehsan
I spent a few hours on this issue and I have come with this function which works on Apache and Nginx:
我在这个问题上花了几个小时,我带来了这个适用于 Apache 和 Nginx 的函数:
/**
* respondOK.
*/
protected function respondOK()
{
// check if fastcgi_finish_request is callable
if (is_callable('fastcgi_finish_request')) {
/*
* This works in Nginx but the next approach not
*/
session_write_close();
fastcgi_finish_request();
return;
}
ignore_user_abort(true);
ob_start();
$serverProtocole = filter_input(INPUT_SERVER, 'SERVER_PROTOCOL', FILTER_SANITIZE_STRING);
header($serverProtocole.' 200 OK');
header('Content-Encoding: none');
header('Content-Length: '.ob_get_length());
header('Connection: close');
ob_end_flush();
ob_flush();
flush();
}
You can call this function before your long processing.
您可以在长时间处理之前调用此函数。
回答by Justin
Modified the answer by @vcampitelli a bit. Don't think you need the closeheader. I was seeing duplicate close headers in Chrome.
稍微修改了@vcampitelli 的答案。不要认为你需要close标题。我在 Chrome 中看到重复的关闭标题。
<?php
ignore_user_abort(true);
ob_start();
echo '{}';
header($_SERVER["SERVER_PROTOCOL"] . " 202 Accepted");
header("Status: 202 Accepted");
header("Content-Type: application/json");
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
sleep(10);
回答by martti
I use the php function register_shutdown_function for this.
为此,我使用了 php 函数 register_shutdown_function。
void register_shutdown_function ( callable $callback [, mixed $parameter [, mixed $... ]] )
http://php.net/manual/en/function.register-shutdown-function.php
http://php.net/manual/en/function.register-shutdown-function.php
Edit: The above is not working. It seems I was misled by some old documentation. The behaviour of register_shutdown_function has changed since PHP 4.1 linklink
编辑:以上不起作用。似乎我被一些旧文档误导了。从 PHP 4.1 开始, register_shutdown_function 的行为发生了变化链接链接
回答by Fil
I can't install pthread and neither the previous solutions work for me. I found only the following solution to work (ref: https://stackoverflow.com/a/14469376/1315873):
我无法安装 pthread,以前的解决方案都不适合我。我发现只有以下解决方案有效(参考:https: //stackoverflow.com/a/14469376/1315873):
<?php
ob_end_clean();
header("Connection: close");
ignore_user_abort(); // optional
ob_start();
echo ('Text the user will see');
$size = ob_get_length();
header("Content-Length: $size");
ob_end_flush(); // Strange behaviour, will not work
flush(); // Unless both are called !
session_write_close(); // Added a line suggested in the comment
// Do processing here
sleep(30);
echo('Text user will never see');
?>
回答by Matthew Slyman
I asked this question to Rasmus Lerdorf in April 2012, citing these articles:
我在 2012 年 4 月向 Rasmus Lerdorf 提出了这个问题,引用了这些文章:
- https://www.zulius.com/how-to/close-browser-connection-continue-execution/
- close a connection early
- http://php.net/manual/en/features.connection-handling.php
- https://www.zulius.com/how-to/close-browser-connection-continue-execution/
- 提前关闭连接
- http://php.net/manual/en/features.connection-handling.php
I suggested the development of a new PHP built-in function to notify the platform that no further output (on stdout?) will be generated (such a function might take care of closing the connection). Rasmus Lerdorf responded:
我建议开发一个新的 PHP 内置函数来通知平台不会生成进一步的输出(在 stdout 上?)(这样的函数可能会负责关闭连接)。拉斯穆斯·勒多夫回应道:
See Gearman. You really really don't want your frontend Web servers doing backend processing like this.
见齿轮工。您真的不希望您的前端 Web 服务器像这样进行后端处理。
I can see his point, and support his opinion for some applications/ loading scenarios! However, under some other scenarios, the solutions from vcampitelli et al, are good ones.
我可以理解他的观点,并支持他对某些应用程序/加载场景的看法!但是,在其他一些场景下,vcampitelli 等人的解决方案是很好的解决方案。
回答by Mayur S
I have something that can compressed and send the response and let other php code to execute.
我有一些可以压缩并发送响应并让其他 php 代码执行的东西。
function sendResponse($response){
$contentencoding = 'none';
if(ob_get_contents()){
ob_end_clean();
if(ob_get_contents()){
ob_clean();
}
}
header('Connection: close');
header("cache-control: must-revalidate");
header('Vary: Accept-Encoding');
header('content-type: application/json; charset=utf-8');
ob_start();
if(phpversion()>='4.0.4pl1' && extension_loaded('zlib') && GZIP_ENABLED==1 && !empty($_SERVER["HTTP_ACCEPT_ENCODING"]) && (strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') !== false) && (strstr($GLOBALS['useragent'],'compatible') || strstr($GLOBALS['useragent'],'Gecko'))){
$contentencoding = 'gzip';
ob_start('ob_gzhandler');
}
header('Content-Encoding: '.$contentencoding);
if (!empty($_GET['callback'])){
echo $_GET['callback'].'('.$response.')';
} else {
echo $response;
}
if($contentencoding == 'gzip') {
if(ob_get_contents()){
ob_end_flush(); // Flush the output from ob_gzhandler
}
}
header('Content-Length: '.ob_get_length());
// flush all output
if (ob_get_contents()){
ob_end_flush(); // Flush the outer ob_start()
if(ob_get_contents()){
ob_flush();
}
flush();
}
if (session_id()) session_write_close();
}
回答by Jérome S.
in case of php file_get_contents use, connection close is not enough. php still wait for eof witch send by server.
在 php file_get_contents 使用的情况下,连接关闭是不够的。php 仍然等待服务器发送的 eof 女巫。
my solution is to read 'Content-Length:'
我的解决方案是阅读“内容长度:”
here is sample :
这是示例:
response.php:
响应.php:
<?php
ignore_user_abort(true);
set_time_limit(500);
ob_start();
echo 'ok'."\n";
header('Connection: close');
header('Content-Length: '.ob_get_length());
ob_end_flush();
ob_flush();
flush();
sleep(30);
Note the "\n" in response to close line, if not the fget read while wait eof.
注意“\n”以响应关闭行,如果不是在等待 eof 时读取 fget。
read.php :
阅读.php:
<?php
$vars = array(
'hello' => 'world'
);
$content = http_build_query($vars);
fwrite($fp, "POST /response.php HTTP/1.1\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: " . strlen($content) . "\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");
fwrite($fp, $content);
$iSize = null;
$bHeaderEnd = false;
$sResponse = '';
do {
$sTmp = fgets($fp, 1024);
$iPos = strpos($sTmp, 'Content-Length: ');
if ($iPos !== false) {
$iSize = (int) substr($sTmp, strlen('Content-Length: '));
}
if ($bHeaderEnd) {
$sResponse.= $sTmp;
}
if (strlen(trim($sTmp)) == 0) {
$bHeaderEnd = true;
}
} while (!feof($fp) && (is_null($iSize) || !is_null($iSize) && strlen($sResponse) < $iSize));
$result = trim($sResponse);
As you can see this script dosent wait about eof if content length is reach.
如您所见,如果达到内容长度,此脚本不会等待 eof。
hope it will help
希望它会有所帮助

