3

我试图在页面/连接关闭后继续 PHP 脚本。

用户将每 1 小时轮询一次脚本,我想返回一些 json 输出并希望在后台继续脚本。我正在使用共享主机,但无法使用 cron 作业。

这是我尝试过的。

ob_start();

ignore_user_abort();

echo "JSON_OUTPUT GOES HERE";

$ob_length = ob_get_length();

header("Content-Type : text/plain",TRUE);
header("Content-Length : $ob_length",TRUE);
header("Connection : Close",TRUE);

flush();
ob_flush();
ob_end_flush();

sleep(3);

echo "You cant see me..";

exit();

我正在使用 Codeigniter 框架,但它不适用于我的实时服务器。它等待 3 秒,然后You cant see me..也输出。

请帮我。

笔记

项目托管在 LINUX/WINDOWS/WAMP-SERVER 共享主机中。

4

4 回答 4

11

经过一些研究,我得到了它的工作,有时它可能对其他人有用。

function closeOutput($stringToOutput){   
        set_time_limit(0);
        ignore_user_abort(true);
        header("Connection: close\r\n");
        header("Content-Encoding: none\r\n");  
        ob_start();          
        echo $stringToOutput;   
        $size = ob_get_length();   
        header("Content-Length: $size",TRUE);  
        ob_end_flush();
        ob_flush();
        flush();   
} 

你可以像这样使用它

$outputContent = 'Contentent Goes Here...';

closeOutput( $outputContent );

sleep(5);

//do some background works ...

exit();
于 2013-04-30T09:10:11.763 回答
2

首先,不要在不应该​​的Connection前后使用空格。其次,不要强制浏览器停止获取当前响应并显示空白页。这里http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html第 14.10 章指出::Header: valueHeader : valueConnection: closeConnection: close in either the request or the response header fields indicates that the connection SHOULD NOT be considered 'persistent' (section 8.1) after the current request/response is complete

那么,如果您的代码有效,您如何尝试:

ignore_user_abort();
header("Content-Type: text/plain; charset=UTF-8");

// just to try show following echo immediately, working depends on server configuration
while (@ob_end_flush()); 

echo date('Y-m-d H:i:s'), PHP_EOL;

echo "JSON_OUTPUT GOES HERE", PHP_EOL;

sleep(10); // 10 seconds so you can close browser tab before

// this file should be created after 10 seconds, even after you closed browser tab
// also check if permissions to write to __DIR__ are set for apache.
file_put_contents(__DIR__ . '/tmp.txt', "Text after 10 sec");

exit;

在浏览器中打开这个 php 文件,并在 2-3 秒后关闭选项卡(即使您在屏幕上看不到任何内容),稍等片刻并检查文件是否已创建。它在我的 linux 机器上运行。

于 2013-04-30T08:28:03.467 回答
2

由于 Red 发布的这种很酷的可能性,我编写了一个小型实用程序类,它提供了一个队列,您可以在其中添加闭包以供以后执行:

<?php

namespace Company\Project\Utilities;

/**
 * Class ContinueUtility
 *
 * @package Company\Project\Utilities
 */
class ContinueUtility {
/**
 * Stored tasks
 * @var array
 */
static protected $tasks = array();

/** Constant for new line in HTTP Header */
const HEADER_NEW_LINE = "\r\n";

/**
 * Add task (closure/function) to queue, with set arguments
 *
 * @param \Closure $task
 * @param array $arguments
 * @return void
 */
public static function addTask(\Closure $task, array $arguments = array()) {
    self::$tasks[] = array(
        'closure' => $task,
        'arguments' => $arguments
    );
}

/**
 * Returns TRUE if tasks has been set, otherwise FALSE
 *
 * @return boolean
 */
public static function hasTasks() {
    return !empty(self::$tasks);
}

/**
 * Clear all previous set tasks
 *
 * @return void
 */
protected static function clearTasks() {
    self::$tasks = array();
}

/**
 * Execute all previous set tasks
 *
 * @return void
 */
protected static function executeTasks() {
    foreach (self::$tasks as $task) {
        call_user_func_array($task['closure'], $task['arguments']);
    }
}

/**
 * Execute and clear all previous set tasks
 *
 * @return void
 */
public static function executeAndClearTasks() {
    self::executeTasks();
    self::clearTasks();
}

/**
 * Closes the HTTP connection to client immediately and outputs given string.
 *
 * @param string $instantOutput
 * @return void
 */
public static function closeConnection($instantOutput = '') {
    set_time_limit(0);
    ignore_user_abort(TRUE);
    header('Connection: close' . self::HEADER_NEW_LINE);
    header('Content-Encoding: none' . self::HEADER_NEW_LINE);
    ob_start();
    echo $instantOutput;
    $size = ob_get_length();
    header('Content-Length: ' . $size, TRUE);
    ob_end_flush();
    ob_flush();
    flush();
}
}

这是将新任务添加到队列的方式:

use Company\Project\Utilities\ContinueUtility;

$a = 4;
$b = 5;
ContinueUtility::addTask(function($a, $b){
    sleep(5);
    $c = a + b;
    file_put_contents(__DIR__ . '/whatever.log', $a . '+' . $b . '=' . $c);
}, array(
    $a, $b
));

这就是您触发执行所有先前添加的任务的方式:

ContinueUtility::closeConnection('Ready.');
ContinueUtility::executeAndClearTasks();
于 2013-11-11T16:32:54.757 回答
0

如果您使用的是 PHP-FPM,一个更清洁、更通用的解决方案是简单地执行fastcgi_finish_request();

来自 PHP.net 的文档

此函数将所有响应数据刷新到客户端并完成请求。这允许在不打开与客户端的连接的情况下执行耗时的任务。

这就是 Symfony 处理其onTerminate.

于 2019-07-08T15:42:47.700 回答