PHP connection_aborted() 无法正常工作

发布于 2024-11-16 00:31:19 字数 435 浏览 2 评论 0原文

我有以下代码:

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。提前致谢!

I have the following code:

ignore_user_abort(true);
while(!connection_aborted()) {
    // do stuff
}

and according to the PHP documentation, this should run until the connection is closed, but for some reason, it doesn't, instead it keeps running until the script times out. I've looked around online and some recommended adding

echo chr(0);
flush();

into the loop, but that doesn't seem to do anything either. Even worse, if I just leave it as

while(true) {
    // do stuff
}

PHP still continues to run the script after the client disconnects. Does anyone know how to get this working? Is there a php.ini setting that I'm missing somewhere?

If it matters, I'm running PHP 5.3.5. Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烟─花易冷 2024-11-23 00:31:19

我参加这个聚会有点晚了,但我刚刚遇到了这个问题并找到了答案。这里发生了很多事情——这里提到了其中一些:
PHP 根本不检测连接中止

其要点: 为了使 connection_aborted() 工作,PHP 需要尝试将数据发送到客户端。

输出缓冲区

如前所述,PHP 不会检测到连接已断开,直到连接终止为止。尝试实际发送数据到客户端。这并不像执行echo那么简单,因为echo将数据发送到任何可能存在的输出缓冲区,并且PHP不会尝试真正的发送直到这些缓冲区足够满。我不会详细介绍输出缓冲,但值得一提的是,可以有多个嵌套缓冲区。

无论如何,如果你想测试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 已经关闭,它将退出任何关闭
    如果 PHP 已经关闭

析构函数

如果您想在用户中止连接时执行某些操作,则需要执行三件事:

  1. 检测用户中止连接。这意味着您必须尝试定期刷新给用户,如上所述。清除所有输出缓冲区、echo、flush。

    a.如果 ignore_connection_aborted 为 true,则需要在每次刷新后手动测试 connection_aborted()

    b.如果 ignore_connection_aborted 为 false,则调用 flush 将导致关闭过程开始。 然后,您必须特别小心,不要在关闭函数中导致刷新,否则 PHP 将立即停止执行该函数并继续执行下一个关闭函数。< /p>

将它们放在一起

将所有这些放在一起,让我们做一个检测用户点击“停止”并执行操作的示例。

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_abortregister_shutdown_function__destruct 问题的人。

I'm a bit late to this party, but I just had this problem and got to the bottom of it. There are multiple things going on here -- a few of them mentioned here:
PHP doesn't detect connection abort at all

The gist of it: In order for connection_aborted() to work, PHP needs to attempt to send data to the client.

Output Buffers

As noted, PHP will not detect the connection is dead until it tries to actually send data to the client. This is not as simple as doing an echo, because echo sends the data to any output buffers that may exist, and PHP will not attempt a real send until those buffers are full enough. I will not go into the details of output buffering, but it's worth mentioning that there can be multiple nested buffers.

At any rate, if you'd like to test connection_abort(), you must first end all buffers:

while (ob_get_level()){ ob_end_clean(); }

Now, anytime you want to test if the connection is aborted, you must attempt to send data to the client:

echo "Something.";
flush();

// returns expected value...
// ... but only if ignore_user_abort is false!
connection_aborted(); 

Ignore User Abort

This is a very important setting that determines what PHP will do when the above flush() is called, and the user has aborted the connection (eg: hit the STOP button in their browser).

If true, the script will run along merrily. flush() will do essentially nothing.

If false, as is the default setting, execution will immediately stop in the following manner:

  • If PHP is not already shutting down, it will begin its shutdown
    process.

  • If PHP is already shutting down, it will exit whatever shutdown
    function it is in and move on to the next.

Destructors

If you'd like to do stuff when the user aborts the connection, you need to do three things:

  1. Detect the user aborted the connection. This means you have to attempt to flush to the user periodically, as described further above. Clear all output buffers, echo, flush.

    a. If ignore_connection_aborted is true, you need to manually test connection_aborted() after each flush.

    b. If ignore_connection_aborted is false, a call to flush will cause the shutdown process to begin. You must then be especially careful not to cause flush from within your shutdown functions, or else PHP will immediate cease execution of that function and move on to the next shutdown function.

Putting it all together

Putting this all together, lets make an example that detects the user hitting "STOP" and does stuff.

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;

The comments explain everything. You can fiddle with ignore_user_abort and look at the logs to see how this changes things.

I hope this helps anyone having trouble with connection_abort, register_shutdown_function, and __destruct.

呆° 2024-11-23 00:31:19

尝试在 flush(); 之前使用 ob_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
?>

Google Chrome 之类的操作,此代码存在问题;它不能很好地支持流媒体。

Try using ob_flush(); just before flush(); and some browsers just won't update the page before some data is added.

Try doing something like

<? 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
?>

Google Chrome has issues with this code, actually; it doesn't support streaming very nicely.

夏九 2024-11-23 00:31:19

尝试:

    ignore_user_abort(true);

    echo "Testing connection handling";

    while (1) {
            if (connection_status() != CONNECTION_NORMAL)
                    break;
            sleep(1);
            echo "test";
            flush();
    }

Try:

    ignore_user_abort(true);

    echo "Testing connection handling";

    while (1) {
            if (connection_status() != CONNECTION_NORMAL)
                    break;
            sleep(1);
            echo "test";
            flush();
    }
风吹短裙飘 2024-11-23 00:31:19

缓冲似乎会导致问题,具体取决于您的服务器设置。

我尝试使用 ob_end_clean 禁用缓冲区,但这还不够,我必须发送一些数据才能使缓冲区完全刷新。这是最终为我工作的最终代码。

set_time_limit(0); // run the delay as long as the user stays connected
ignore_user_abort(false);
ob_end_clean();
echo "\n";
while ($delay-- > 0 && !connection_aborted())
{
    echo str_repeat("\r", 1000) . "<!--sleep-->\n";
    flush();
    sleep(1);
}
ob_start();

Buffering seems to cause issues depending on your server settings.

I tried disabling the buffer with ob_end_clean but that wasn't enough, I had to send some data to cause the buffer to fully flush out. Here is the final code that ended up working for me.

set_time_limit(0); // run the delay as long as the user stays connected
ignore_user_abort(false);
ob_end_clean();
echo "\n";
while ($delay-- > 0 && !connection_aborted())
{
    echo str_repeat("\r", 1000) . "<!--sleep-->\n";
    flush();
    sleep(1);
}
ob_start();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文