Continue php script after connection close

后端 未结 4 1115
南旧
南旧 2020-12-30 19:09

I am trying to continue a PHP Script after the page/connection is closed.

Users will POLL the script in every 1 hour, I want to return some json output and want to c

相关标签:
4条回答
  • 2020-12-30 19:17

    After some research i got it work, Sometime it may be useful to some others.

    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();   
    } 
    

    You can use it like

    $outputContent = 'Contentent Goes Here...';
    
    closeOutput( $outputContent );
    
    sleep(5);
    
    //do some background works ...
    
    exit();
    
    0 讨论(0)
  • 2020-12-30 19:27

    If you are using PHP-FPM, a cleaner and more versatile solution would be to simply execute fastcgi_finish_request();

    From PHP.net's documentation

    This function flushes all response data to the client and finishes the request. This allows for time consuming tasks to be performed without leaving the connection to the client open.

    This is how Symfony handles its onTerminate.

    0 讨论(0)
  • 2020-12-30 19:34

    First, don't use space after Connection and before : it should be Header: value not Header : value. Second, Connection: close don't force browser to stop getting current response and display blank page. Here http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html chapter 14.10 it states: Connection: 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

    So how can you try if your code works:

    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;
    

    Open this php file in browser and after 2-3 seconds close tab (even if you don't see anything on screen), wait a little longer and check if file is created. It's working on my linux machine.

    0 讨论(0)
  • 2020-12-30 19:36

    Because of this cool possibility, which Red posted, I've written a small utility class which provides a queue where you can add Closures for later execution:

    <?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();
    }
    }
    

    This is how you add new tasks to queue:

    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
    ));
    

    And this is how you trigger execution of all previous added tasks:

    ContinueUtility::closeConnection('Ready.');
    ContinueUtility::executeAndClearTasks();
    
    0 讨论(0)
提交回复
热议问题