Я пытаюсь продолжить 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 framework, но его не работает на моем реальном сервере. Он ждет 3 секунды, а затем выводя You cant see me..
тоже.
Пожалуйста, помогите мне.
Проект размещается в общих хостах LINUX / WINDOWS / WAMP-SERVER.
После некоторых исследований я получил его работу, Иногда это может быть полезно для некоторых других.
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();
Во-первых, не используйте пробел после Connection
и до :
он должен быть Header: value
не Header : value
. Во-вторых, Connection: close
не заставляют браузер перестать получать текущий ответ и отображать пустую страницу. Здесь http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html глава 14.10 в нем говорится: 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
Итак, как вы можете попробовать, если ваш код работает:
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('Ymd 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-машине.
Из-за этой прекрасной возможности, которую опубликовал Red, я написал небольшой класс утилиты, который предоставляет очередь, в которую вы можете добавить Closures для последующего исполнения:
<?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();