Обновить вывод длинного процесса на странице PHP

У меня есть «длинный» скрипт, который я хочу выполнить на странице PHP, и я хочу, чтобы его вывод «обновлялся», как только скрипт что-то выводит. Я прочитал множество решений, таких как questions 4706525, 9182094, 8882383, Руководство по сбросу PHP, но в моем случае оно не работает должным образом!

Мой тестовый скрипт:

#!/bin/bash

echo "This is a test script"
echo "Sleeping"
sleep 30
echo "Done"

Исполняемое разрешение установлено для www-data.

Моя PHP-страница:

<?php
@apache_setenv('no-gzip', 1);
@ini_set('zlib.output_compression', 0);
@ini_set('implicit_flush', 1);
@ini_set('output_buffering', 0);
@apache_setenv('output_buffering', 0);

echo "Here<br>";
flush();
$cmd = "../test.sh";
$pipes = array();
$descriptors = array(
             0 => array("pipe", "r"),
             1 => array("pipe", "w"),
             2 => array("pipe", "w"),
             );
echo "Starting process<br>";
flush();
$process = proc_open($cmd, $descriptors, $pipes, realpath('./'), array());
echo "<pre>";
if (is_resource($process)) {
    while ($s = fgets($pipes[1])) {
        print $s;
        flush();
    }
} else {
    print "Cannot create process\n";
}
echo "</pre>";
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
?>

NB. Мой тестовый скрипт, test.sh, находится в каталоге над страницей PHP, таким образом, ../test.sh. Не то чтобы это что-то меняет. Но это не опечатка.

В моем php.ini они есть (хотя я не слишком стремился менять этот сервер в целом, но я хотел проверить, была ли проблема в этом):

zlib.output_compression = Off
output_buffering = Off

Я использую ЛАМПП.

Если я запускаю страницу PHP в терминале,

$ php test.php

Работает нормально: сразу получаю "Это тестовый скрипт" и "Спящий", а через некоторое время "Готово".

Если я загружаю страницу в свой браузер, она не работает: она ожидает завершения работы test.sh, прежде чем что-либо выводить.

Отредактировано: если я добавлю в цикл echo str_pad('',4096)."\n", тогда все заработает. Однако это исправление предполагает, что по непонятной мне причине для буферизации вывода по-прежнему установлено значение по умолчанию (4096), а не отключено, как я пытался настроить.

while ($s = fgets($pipes[1])) {
  print $s;
  echo str_pad('',4096)."\n";    
  flush();
}

Кроме того, это решение не идеально, потому что на самом деле оно добавляет пробелы к выходным данным.

Я ищу решение, которое

  • обновляет вывод страницы PHP
  • не изменяет php.ini
  • не изменяет вывод

Спасибо!


person user1381    schedule 28.08.2015    source источник
comment
Поведение, которого вы пытаетесь добиться, зависит от многих факторов. Я предполагаю, что вы прочитали страницу php.net/flush. Какой браузер вы используете? Попробуйте добавить новые строки после print $s в цикле, чтобы это было print $s . "\n", посмотрите, поможет ли это.   -  person marekful    schedule 28.08.2015
comment
Есть одно предостережение, о котором вы должны знать. Параметр output_buffering PHP имеет два возможных значения. Один указывает, включен ли он, а второй указывает максимальный размер буфера. Если для output_buffering установлено значение 1, вы не сможете увидеть, как ваш контент или индикатор загрузки браузера вращаются, пока не завершится выполнение кода PHP. Это связано с тем, что если output_buffering равен 1, это означает, что мы включили его, но не указали максимальный размер, поэтому в этом случае буферы PHP могут хранить данные до числа, указанного в параметре memory_limit. Источник: sitepoint.com   -  person sanderbee    schedule 28.08.2015
comment
@marekful нет, это ничего не меняет (и, честно говоря, я не думаю, почему это должно быть, поскольку это только изменяет отображение).   -  person user1381    schedule 28.08.2015
comment
@sanderbee хорошо, что в моем случае output_buffering отключен. Так что нет размера буфера. Так что это тоже не решает мою проблему, если только я неправильно понимаю вашу точку зрения.   -  person user1381    schedule 28.08.2015
comment
Независимо от буферизации или сжатия на стороне сервера браузер/агент может использовать некоторую стратегию буферизации. Я предложил новую строку, основываясь на документации по сбросу PHP, в которой упоминается, что это может помочь (хотя это несколько устарело, поскольку относится к Netscape). Кроме того, посмотрите на первую запись в разделе «Заметки, внесенные пользователями». Он утверждает, что у него есть проверенное решение, которое построчно отправляет контент в браузер, который отображает его построчно по мере отправки. В этом решении для достижения желаемого результата отправляется 4 КБ новых строк. Может, тебе стоит попробовать. (str_pad('',4096)."\n";)   -  person marekful    schedule 28.08.2015
comment
@marekful действительно добавляет str_pad('',4096).\n; работает!!! Но это странно, потому что 4096 — это значение по умолчанию для output_buffering. И я изменил свой php.ini на output_buffering = 0. Я добавил print output_buffering = . ini_get('output_buffering'); а выводит output_buffering=0. Это же как не использовать?! Кроме того, использование str_pad(...) не является идеальным решением, потому что тогда, если вы запустите страницу через php test.php, вы действительно увидите много пробелов.   -  person user1381    schedule 28.08.2015
comment
Потенциально между выводом PHP и сетевым сокетом есть много вещей. Указание apache не сжимать вывод не обходит mod_deflate (и, следовательно, любую буферизацию), он просто говорит ему не сжимать. И вышеизложенное не будет работать ни с чем, кроме как с клиентом, подключающимся напрямую к apache, работающему под управлением mod_php - вы не сказали, была ли здесь такая архитектура.   -  person symcbean    schedule 28.08.2015
comment
@symcbean нет, клиент не должен находиться на том же хосте, что и тот, на котором работает mod_php   -  person user1381    schedule 28.08.2015