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


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

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

Author: brianmearns, 2013-02-22

3 answers

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

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

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

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

 4
Author: brianmearns, 2013-02-22 16:18:24

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

Сетенвифноказ

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

<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

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

Принять кодировку: gzip, выкачать, sdch

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

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

 3
Author: Ted Collins, 2013-06-21 20:28:31

Чтобы отключить буферизацию вывода во время выполнения в 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 байт, прежде чем он начнет выводить что-либо вообще.

 2
Author: Pedro Gimeno, 2015-02-22 18:37:33