Предотвращение буферизации вывода с помощью PHP и Apache

У меня есть PHP-скрипт, который отправляет большое количество записей, и я хочу очистить каждую запись, как только она будет доступна: клиент может обрабатывать каждую запись по мере ее поступления, ей не нужно ждать весь ответ. Я понимаю, что для всего переноса требуется немного больше, потому что его нужно отправлять по нескольким пакетам, но он все же позволяет клиенту начать работу раньше.

Я пробовал все функции flush() и ob_flush() но ничто не помогает получить данные, фактически отправленные по строке до завершения страницы. Я подтвердил, что это не веб-браузер, потому что я тестировал его с помощью telnet.

Единственное решение, которое сработало для меня, – установить директиву output_buffering в php.ini на «Off». Я не хотел делать это для всего сервера, только для этого конкретного ресурса. Обычно вы можете использовать ini_set из PHP-скрипта, но по какой-либо причине php не позволяет output_buffering таким образом (см. Руководство по php ).

Хорошо получается, что если вы используете Apache, вы можете установить некоторые директивы php ini (включая output_buffering ) из конфигурации вашего сервера, включая файл .htaccess. Поэтому я использовал следующее в файле .htaccess, чтобы отключить output_buffering только для этого одного файла:

 <Files "q.php"> php_value output_buffering Off </Files> 

И тогда в моей конфигурации статического сервера мне просто нужны AllowOverride Options=php_value (или более крупный молот, например AllowOverride All ), чтобы это было разрешено в файле .htaccess.

Вы не упоминаете, какой веб-сервер вы используете, но я собираюсь выйти на конечность здесь и догадаться Apache2. Я поразил почти то же, что вы описываете. Я пытался заставить свой скрипт cgi передавать информацию, как только она была готова, а не буферизировать все это. Работал в локте и т. Д., Но буферизовался в браузере (почти в любом браузере), который был, по крайней мере, безумным. Я просмотрел точные шаги, которые вы описали. Решение в моем случае состояло в том, чтобы изменить конфигурационный файл с sites-enabled/terrifico.com в Apache2 (строка, о которой идет речь, начинается с

SetEnvIfNoCase

(Вы можете игнорировать материал выше и ниже этой строки, я просто показываю его для справки о том, где я его разместил.)

 <VirtualHost *:80> ServerAdmin webmaster@localhost ServerName test.terrifico.com ServerAlias test.terrifico.com SetEnvIfNoCase Request_URI \.cgi$ no-gzip dont-vary DocumentRoot /var/www/test.terrifico.com 

От взгляда на сетевой трафик, идущий туда и обратно, мне наконец-то стало ясно, что браузер рекламирует, что он принял дефляцию ни за что (это был текст). Например, это разница между браузером и завитком. Основной бит был

Accept-Encoding: GZIP, выкачать, SDCH

Было немного о chunking , но это не повлияло на эту конкретную проблему. Таким образом, браузер запрашивал mod_deflate чтобы mod_deflate удар, который победил мои тщательно извергающие байты, когда я получил их в своем скрипте cgi. Вы можете изменить его в браузере, но было бы разумнее изменить его на сервере один раз для работы.

Возможно, это помогает.

Чтобы отключить буферизацию вывода во время выполнения в PHP без изменения php.ini или с файлом .htaccess , просто используйте ob_end_flush() или ob_end_clean() в начале скрипта. Например:

Это должно выводиться без буферизации:

 <?php ob_end_clean(); for ($i = 0; $i < 5; $i++) { echo "$i\n"; flush(); usleep(0.5e6); } 

Это выводит с буферизацией (все за раз), если output_buffering , независимо от вызова flush() :

 <?php for ($i = 0; $i < 5; $i++) { echo "$i\n"; flush(); usleep(0.5e6); } 

Несмотря на свое имя, ob_implicit_flush вызывает flush() , а не ob_flush() , неявно после каждого выхода. Это может быть удобно в этом случае после закрытия выходного буфера в начале:

 <?php ob_end_clean(); // disable output buffer ob_implicit_flush(); // call flush() automatically after every output for ($i = 0; $i < 5; $i++) { echo "$i\n"; usleep(0.5e6); } 

Это исправляет сторону PHP. Возможно, что-то еще происходит с mod_deflate или аналогичным (см. Ответ Теда Коллинза), и я заметил, что Firefox должен иметь по крайней мере 1024 байта, прежде чем он начнет выводить что-нибудь вообще.