我有以下代码:
ignore_user_abort(true);
while(!connection_aborted()) {
// do stuff
}
并且根据PHP文档,它应该一直运行到关闭连接为止,但是由于某种原因,它不会一直运行,而是一直运行直到脚本超时。我在线上看了看,建议添加一些
echo chr(0);
flush();
进入循环,但这似乎也不起作用。更糟糕的是,如果我将其保留为
while(true) {
// do stuff
}
客户端断开连接后,PHP仍继续运行脚本。有谁知道如何使它工作?我在某处缺少php.ini设置吗?
[如果有关系,我正在运行PHP 5.3.5。预先感谢!
尝试:
ignore_user_abort(true);
echo "Testing connection handling";
while (1) {
if (connection_status() != CONNECTION_NORMAL)
break;
sleep(1);
echo "test";
flush();
}
尝试在ob_flush();
之前使用flush();
,并且某些浏览器在添加某些数据之前不会更新页面。
尝试做类似的事情
<? php
// preceding scripts
ignore_user_abort(true);
$i = 0;
while(!connection_aborted())
{ $i++;
echo $i;
echo str_pad('',4096); // yes i know this will increase the overhead but that can be reduced afterwords
ob_flush();
flush();
usleep(30000); // see what happens when u run this on my WAMP this runs perfectly
}
// Ending scripts
?>
实际上,Chrome浏览器在此代码上存在问题;它不能很好地支持流。
不幸的是,我有output_buffering = Off,无论如何,即使客户端关闭了浏览器,我的while循环仍会继续运行,并且connection_aborted()仍为0。还有其他提示吗?
我参加这个聚会有点晚了,但是我遇到了这个问题,并且已经深究了。这里发生了很多事情-这里提到了其中的一些:PHP doesn't detect connection abort at all
要点:为了使connection_aborted()
正常工作,PHP需要尝试将数据发送到客户端。
如前所述,PHP在尝试将数据实际发送到客户端之前,不会检测到连接已死。这并不像echo
那样简单,因为echo
将数据发送到可能存在的任何output buffers
,并且PHP将不会尝试真正发送,除非这些缓冲区已满。我将不讨论输出缓冲的细节,但是值得一提的是,可以有multiple嵌套缓冲区。
无论如何,如果您想测试connection_abort(),必须首先结束所有缓冲区:
while (ob_get_level()){ ob_end_clean(); }
现在,无论何时您想测试连接是否中止,都必须尝试将数据发送到客户端:
echo "Something.";
flush();
// returns expected value...
// ... but only if ignore_user_abort is false!
connection_aborted();
这是一个非常重要的设置,它决定了在调用上述flush()
且用户中止连接后(例如,在浏览器中单击STOP按钮),PHP将执行的操作。
如果true,脚本将轻松运行。 flush()
基本上什么也不做。
如果false,作为默认设置,将以下列方式立即停止执行:
如果PHP尚未关闭,它将开始关闭过程。
如果PHP已经关闭,它将退出任何关闭状态功能,然后移至下一个。
如果您想在用户中止连接时做一些事情,则需要做三件事:
检测到用户中止了连接。这意味着您必须定期向用户尝试flush
,如上所述。清除所有输出缓冲区,回显,刷新。
a。如果ignore_connection_aborted
为真,则在每次刷新后需要手动测试connection_aborted()
。
b。如果ignore_connection_aborted
为假,则对flush
的调用将导致关机过程开始。 然后您必须特别小心,不要在关闭函数中引起flush
],否则PHP将立即停止执行该函数并继续执行下一个关闭函数。
将所有内容放在一起,让我们举个例子来检测用户按下“ STOP”并执行操作。
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;
评论说明了一切。您可以摆弄ignore_user_abort
并查看日志,以了解情况如何变化。
我希望这可以帮助任何在connection_abort
,register_shutdown_function
和__destruct
上遇到麻烦的人。