kev*_*314 7 php connection timeout infinite-loop
我有以下代码:
ignore_user_abort(true);
while(!connection_aborted()) {
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
根据 PHP 文档,这应该运行到连接关闭为止,但由于某种原因,它没有运行,而是一直运行到脚本超时。我在网上浏览了一下,有些建议添加
echo chr(0);
flush();
Run Code Online (Sandbox Code Playgroud)
进入循环,但这似乎也没有做任何事情。更糟糕的是,如果我把它保留为
while(true) {
// do stuff
}
Run Code Online (Sandbox Code Playgroud)
客户端断开连接后,PHP 仍然继续运行脚本。有谁知道如何让它工作?我是否在某处缺少 php.ini 设置?
如果重要的话,我正在运行 PHP 5.3.5。提前致谢!
JS_*_*ler 10
我参加这个聚会有点晚了,但我刚刚遇到了这个问题并找到了答案。这里发生了很多事情——这里提到了其中一些: PHP根本不检测连接中止
要点: 为了connection_aborted()
工作,PHP 需要尝试将数据发送到客户端。
如前所述,PHP 在尝试实际向客户端发送数据之前不会检测到连接已断开。这并不像执行 操作那么简单echo
,因为echo
将数据发送到任何output buffers
可能存在的缓冲区,并且在这些缓冲区足够满之前,PHP 不会尝试真正的发送。我不会详细介绍输出缓冲,但值得一提的是,可以有多个嵌套缓冲区。
无论如何,如果您想测试connection_abort(),您必须首先结束所有缓冲区:
while (ob_get_level()){ ob_end_clean(); }
Run Code Online (Sandbox Code Playgroud)
现在,只要您想测试连接是否中止,您就必须尝试向客户端发送数据:
echo "Something.";
flush();
// returns expected value...
// ... but only if ignore_user_abort is false!
connection_aborted();
Run Code Online (Sandbox Code Playgroud)
这是一个非常重要的设置,它决定了当调用上述内容flush()
并且用户已中止连接(例如:点击浏览器中的“停止”按钮)时 PHP 将执行的操作。
如果true,脚本将愉快地运行。flush()
基本上什么也不做。
如果为false(默认设置),执行将按以下方式立即停止:
如果 PHP 尚未关闭,它将开始其关闭过程。
如果 PHP 已经关闭,它将退出它所在的任何关闭函数并继续执行下一个。
如果您想在用户中止连接时执行某些操作,则需要执行三件事:
检测用户中止连接。这意味着您必须flush
定期尝试与用户联系,如上所述。清除所有输出缓冲区、回显、刷新。
A。如果ignore_connection_aborted
是 true,则需要connection_aborted()
在每次刷新后手动测试。
b. 如果ignore_connection_aborted
为 false,则调用flush
将导致关闭过程开始。然后,您必须特别小心,不要从关闭函数内部引起flush
,否则 PHP 将立即停止执行该函数并继续执行下一个关闭函数。
将所有这些放在一起,让我们做一个检测用户点击“停止”并执行操作的示例。
class DestructTester {
private $fileHandle;
public function __construct($fileHandle){
// fileHandle that we log to
$this->fileHandle = $fileHandle;
// call $this->onShutdown() when PHP is shutting down.
register_shutdown_function(array($this, "onShutdown"));
}
public function onShutdown() {
$isAborted = connection_aborted();
fwrite($this->fileHandle, "PHP is shutting down. isAborted: $isAborted\n");
// NOTE
// If connection_aborted() AND ignore_user_abort = false, PHP will immediately terminate
// this function when it encounters flush. This means your shutdown functions can end
// prematurely if: connection is aborted, ignore_user_abort=false, and you try to flush().
echo "Test.";
flush();
fwrite($this->fileHandle, "This was written after a flush.\n");
}
public function __destruct() {
$isAborted = connection_aborted();
fwrite($this->fileHandle, "DestructTester is getting destructed. isAborted: $isAborted\n");
}
}
// Create a DestructTester
// It'll log to our file on PHP shutdown and __destruct().
$fileHandle = fopen("/path/to/destruct-tester-log.txt", "a+");
fwrite($fileHandle, "---BEGINNING TEST---\n");
$dt = new DestructTester($fileHandle);
// Set this value to see how the logs end up changing
// ignore_user_abort(true);
// Remove any buffers so that PHP attempts to send data on flush();
while (ob_get_level()){
ob_get_contents();
ob_end_clean();
}
// Let's loop for 10 seconds
// If ignore_user_abort=true:
// This will continue to run regardless.
// If ignore_user_abort=false:
// This will immediate terminate when the user disconnects and PHP tries to flush();
// PHP will begin its shutdown process.
// In either case, connection_aborted() should subsequently return "true" after the user
// has disconnected (hit STOP button in browser), AND after PHP has attempted to flush().
$numSleeps = 0;
while ($numSleeps++ < 10) {
$connAbortedStr = connection_aborted() ? "YES" : "NO";
$str = "Slept $numSleeps times. Connection aborted: $connAbortedStr";
echo "$str<br>";
// If ignore_user_abort = false, script will terminate right here.
// Shutdown functions will being.
// Otherwise, script will continue for all 10 loops and then shutdown.
flush();
$connAbortedStr = connection_aborted() ? "YES" : "NO";
fwrite($fileHandle, "flush()'d $numSleeps times. Connection aborted is now: $connAbortedStr\n");
sleep(1);
}
echo "DONE SLEEPING!<br>";
die;
Run Code Online (Sandbox Code Playgroud)
评论说明了一切。您可以摆弄ignore_user_abort
并查看日志,看看这会如何改变事情。
我希望这可以帮助任何遇到connection_abort
、register_shutdown_function
和问题的人__destruct
。