Закрытие соединения с клиентом в register_shutdown_function

SiMM

Новичок
Закрытие соединения с клиентом в register_shutdown_function

Минимальный скрипт:
PHP:
<?
  function sd(){
    echo 'test';
    flush();
    while(1){}
  }
  set_time_limit(60);
  register_shutdown_function('sd');
?>
При загрузке скрипта имеем ожидание браузером его вылета по таймауту. Через 60 секунд происходит честный вывод в браузер строки "test Fatal error: Maximum execution time of 60 seconds exceeded in c:\usr\www\test.php on line 5", что подтверждает мою догадку о том, что PHP не закрывает соединение при срабатываении shutdown-функции (вообще не понятно, почему происходит вывод на экран - если я правильно понял ман, в нём сказано, что это невозможно). Такое поведение как-то лечится? Хотелось бы принудительно прекратить ожидание браузера.
 

SiMM

Новичок
Space, не понял юмора.
Приведённый скрипт не несёт смысловой нагрузки, он лишь демонстрирует вопрос. Выкладывать же кучу не относящегося к вопросу кода необходимости, думаю, нет.
 

Space

Новичок
я прошу сказать мне, что ты хотел этим скриптом добится?
может тебе вовсе нужны:
sleep();
exit;

не надо кода - просто поясни что хочешь сделать.
 

SiMM

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

Space

Новичок
сделай
error_reporting(0);
но мне кажется это не решение.. тем более что "выдать... всё пользователю" может не уложится в 60 секунд.. может проще поставить на крон те приложения? и зачем такая лишняя нагрузка на сервер (по твоим словам следует что она пустая).
задача все равно не ясна..
 

SiMM

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

Space

Новичок
у тебя прописано:
header ("Content-Length: xxxx");
?
вот подумай - если все скачано и никаких признаков жизни нет.. но брозер продолжает работать...
 

SiMM

Новичок
Space, посмотри пожалуйста на отклик при запросе этой странички (GET /talk/showthread.php?s=&threadid=50374 HTTP/1.1). Ты видишь в отклике Content-Length? Выводы сам сделаешь? Убедительная просьба - если нечего сказать по существу (интересующий меня вопрос указан в топике), то лучше не вмешиваться.
 

Space

Новичок
ты мудришь.
"..перекачкой информации с ftp-сервера.." - ты качаешь файл, у него 100% есть размер.
ищи ошибку в коде а не пытайся ее замазать.
 

SiMM

Новичок
Space, при чём тут код? Выше представлен кусок (ты его хотя бы запускать пробовал, чтобы понять, что доставляет дискомфорт?), что я делаю в месте, которое я заменил пустым циклом - сугубо моё интимное дело и код там ошибок не содержит. То, что у файла, скачиваемого по ftp есть размер - вообще к делу не относится - я же не вывожу его клиенту. header('Content-Length: 0'); (или 4) ничего в этом плане не меняет - соединение не закрывается.
ищи ошибку в коде а не пытайся ее замазать.
Я привёл код выше (не будем рассуждать по поводу его реальной ценности - это просто иллюстрация), покажи мне в нём ошибку. Если тебя сильно смущает while (1){} замени его на for ($i=70;$i--; )sleep(1); - сути проблемы это не изменит.
 

Profic

just Profic (PHP5 BetaTeam)
SiMM
а попробуй
$fp = fopen ('php://output', 'wb');
fclose ($fp);
бред конечн, но вдруг заработает :)
а вообще если есть возможность, то этот длинный код запустить фоном. Например так
exec ('php file.php 1>/dev/null 2>/dev/null &');
но не уверен, что оно в аком виде будет работать.

А насчет такого поведения PHP, то где-то в чейнж логе видел упоминание о том, что теперь shutdown_function выполняется до обрыва соединения
 

SiMM

Новичок
Profic, не помогло :(

По поводу exec есть вопросы:
1. Думаю, не на каждом хостинге это может быть доступно (слова "если недоступно, меняй хостинг", не принимаются)?
2. Путь к php не надо прописывать? Не хотелось бы возиться с возможной неработоспособностью скрипта при переносе на хостинг, а также различиями под Win и *nix'ы (с последними просто незнаком вообще, отчего, отчасти, и комплексую применительно exec'а).

Что-же касается чейнж лога - очень жаль :( Неужели нет никакой возможности закрыть соединение? Понимаю, задача нестандартная и её решение несколько "натянутое" - но хотелось бы раз написать и забыть и чтобы работало как под Win, при обкатке дома, так и под *nix без особой настройки ;)

Спасибо за попытку помочь :)
 

nightik

PHP5 BetaTeam
Кажется такое поведение shutdown_function характерно под Win, под *nix вроде должно работать как следует, хотя утверждать со 100% увереностью я бы не стал. Просто нет возможности проверить.
 

csa

Guest
Если не ошибаюсь, тебе может помочь Header('Location: куда-нибудь') с выставлением (ini_set) опции игнорирования разрыва соединения клиентом (на вскидку не помню имени)
 

SiMM

Новичок
csa, хм... т.е. сделать два скрипта? Первый, чего-то там долго делающий, просто выдаёт Location на другой, просто выводящий странички, и занимается своим делом? Хм... я как-то об этом не подумал :) Надо попробовать.
 

csa

Guest
можно передавать параметр в тот же скрипт

собственно, тебе все равно желательно сделать редирект, иначе при обновлении страницы будет дубль
 

SiMM

Новичок
Автор оригинала: csa
можно передавать параметр в тот же скрипт
Это уже детали :) - мне нужен был минимально работающий пример или идея - дальше уже сам :)
На всякий случай, подытожив, приведу код:
PHP:
  function sd(){
    while(1){}
  }
  if (!isset($_GET['ok'])){ // требуется обработка данных
    header('Location: ?ok=1');
    register_shutdown_function('sd');
  }
  else { // обычный вывод странички без обработки данных
    echo "Ok=$_GET[ok]";
  }
Остался один вопросец - я так понял, отсылаемые заголовки кэшируются PHP или Apache'м? При использовании, казалось бы, эквивалентного кода (хотелось попробовать обойтись без регистрации shutdown'а, чтобы упростить код):
PHP:
  function sd(){
    while(1){}
  }
  if (!isset($_GET['ok'])){ // требуется обработка данных
    header('Location: ?ok=1');
    sd();
  }
  else { // обычный вывод странички
    echo "Ok=$_GET[ok]";
  }
"переброска" сработала только после таймаута. Но это уже философский вопрос - спасибо большое за идею, проблема решена :)
 

csa

Guest
а register_shutdown_function тебе уже не нужен
попробуй сделать flush() после header
 

SiMM

Новичок
Походу, рано радовался - посмотрел сейчас на всё более пристально (что и когда уходит в запросе и что и как возвращается) - тогда, видимо, на радостях, поспешил:
уходит запрос
приходит отклик
браузер, не смотря на location продолжает чего-то ждать, даже если кинуть ему 512 байт данных с flush'ем
по проходу времени совершается 2й запрос

Вобщем, похоже поначалу у меня с кэшированием в браузере было что-то не так - срабатывало :(
 
Сверху