PHP вообще не обнаруживает разрыв соединения

Я прочитал и глубоко понял это: http://www.php.net/manual/en/features.connection-handling.php http://www.php.net/manual/en/function.register-shutdown-function.php

Однако я тестировал как PHP 5.1.6, так и 5.3, и все работает НЕ так, как там описано. Что я наблюдаю:

  • connection_status() всегда возвращает true, даже после того, как клиент закрыл соединение.
  • выполнение сценария продолжается после того, как клиент закрыл соединение, даже если ignore_user_abort равен 0
  • функция, зарегистрированная с помощью register_shutdown_function(), не запускается, пока сценарий не завершится. Сценарий НЕ прерывается (и, следовательно, функция не вызывается), когда клиент разрывает соединение.

Таким образом, в основном PHP просто не обнаруживает отключение клиента ВООБЩЕ.

Обратите внимание, что это НЕ так, как если бы ignore_user_abort был установлен в 1: если бы это было так, то connection_status() вернул бы 1, даже если скрипт продолжал бы работать, а функция выключения не вызывалась бы до конца. Это не относится к делу.

ini_get("ignore_user_abort") возвращает 0, как и ожидалось.

Это ошибка в PHP или это может быть связано с какой-то настройкой Apache?

Как заставить PHP работать, как описано в вышеупомянутой документации?

Тестовый сценарий:

<?php

function myShutdown() {
    error_log("myShutdown ".connection_status()." ".ini_get("ignore_user_abort"));
}

register_shutdown_function(myShutdown);

echo "Hi!";
error_log(" *** test/test *** ");
for ($i=0; $i<10; $i++) {
    sleep(1);
    error_log(".");
    echo ".";
}
?>

Шаги для воспроизведения: - перейдите по URL-адресу сценария - прервите соединение на клиенте до истечения 10 секунд (например, нажмите кнопку «Стоп» в браузере).

Ожидаемое/желаемое поведение: журналы должны отображать менее 10 точек и в конце «myShutdown 1 0» (если вы смотрите журнал в режиме реального времени, myShutDown должен появиться сразу после отключения клиента)

Наблюдаемое/текущее поведение: журналы всегда показывают ровно 10 точек и в конце «myShutdown 0 0» (если вы смотрите это в реальном времени, это продолжается в течение 10 секунд независимо от того, когда клиент отключается).


person matteo    schedule 13.04.2013    source источник
comment
Почему вы используете версию PHP, которая больше не поддерживается?   -  person Baba    schedule 13.04.2013
comment
Вы используете php как модуль apache?   -  person hek2mgl    schedule 13.04.2013
comment
@ hek2mgl да, как модуль Apache   -  person matteo    schedule 14.04.2013
comment
@Baba Я вижу одно и то же как в версии 5.3, которая определенно поддерживается, так и в более ранней версии 5.1. Почему я не обновляю 5.1, в основном потому, что я на CentOS 5, которую я не могу обновить до CentOS6, а там php 5.1 не может быть обновлен до 5.3/4, но и не может быть установлен вместе с ним (полностью f ***ed дизайнерское решение в упаковке RHEL, которое наследует CentOS), поэтому я застрял на этой версии.   -  person matteo    schedule 14.04.2013
comment
@Baba извините, я заметил, что вы прокомментировали до того, как я отредактировал: позже я тоже тестировал PHP 5.3, и он показывает точно такое же поведение.   -  person matteo    schedule 14.04.2013


Ответы (1)


Во-первых, мне также не удалось заставить его работать, используя базовую установку LAMP Ubuntu 12.04 (php5.3). Но у меня есть некоторая информация, и я надеюсь, что она будет полезна. Любые комментарии или правки приветствуются! :)


Я вижу две проблемы с вашим кодом. Первая — синтаксическая ошибка. Вам не хватает одинарных кавычек вокруг myShutdown при вызове register_shutdown_function(). Измените строку на:

register_shutdown_function('myShutdown');

Вторая проблема, которую я вижу, это отсутствие вызова flush() после echos. В документации говорится:

PHP не обнаружит, что пользователь прервал соединение, пока не будет предпринята попытка отправить информацию клиенту. Простое использование инструкции echo не гарантирует, что информация будет отправлена, см. flush().

Но даже flush() не поможет в любом случае. Из документации flush():

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

Некоторые серверы, особенно на Win32, по-прежнему будут буферизовать вывод вашего скрипта до тех пор, пока он не завершится перед передачей результатов в браузер.

Модули сервера для Apache, такие как mod_gzip, могут выполнять собственную буферизацию, из-за которой метод flush() не приведет к немедленной отправке данных клиенту.

Даже браузер может буферизовать свой ввод перед его отображением. Netscape, например, буферизует текст до тех пор, пока не получит конец строки или начало тега, и не будет отображать таблицы, пока не будет виден тег самой внешней таблицы.

Некоторые версии Microsoft Internet Explorer начнут отображать страницу только после того, как получат 256 байт вывода, поэтому вам может потребоваться отправить дополнительные пробелы перед очисткой, чтобы эти браузеры отображали страницу.

В комментариях той страницы есть совет установить несколько заголовков и конфигов апача:

apache_setenv('no-gzip', 1); 
ini_set('zlib.output_compression', 0); 
ini_set('implicit_flush', 1); 

однако даже это не сработало для меня. Я исследовал это с помощью wiresharek, хотя веб-сервер отправляет контент («Привет») через 0,0037 секунды, веб-браузер буферизует страницу.

person hek2mgl    schedule 13.04.2013
comment
Вы должны удалить часть об отсутствующих одинарных кавычках. rehister_shutdown_function() принимает вызываемый объект, а функция является вызываемым. В противном случае я бы получил ошибку, кстати, это не так - person matteo; 14.04.2013
comment
Обратите внимание, что вы получите: Notice: Use of undefined constant myShutdown - assumed 'myShutdown' in .... Вы должны повысить свой error_reporting уровень - person hek2mgl; 14.04.2013
comment
Я понимаю! буферизация вывода должна быть проблемой. Я предполагаю, что apache буферизует его, даже если gzip отключен, и независимо от неявного сброса PHP, возможно, поэтому даже ваш предпринятый обходной путь не работает. - person matteo; 14.04.2013
comment
Либо веб-серверы буферизируют либо браузер. Я не уверен в данный момент. Для этого придется использовать wireshark. Но обратите внимание, что я буду AFK некоторое время (вероятно, завтра) - person hek2mgl; 14.04.2013
comment
Я проверил это, и в моем случае это браузер, который буферизует вывод. Веб-сервер отправляет контент («Привет») через 0,0037 секунды. - person hek2mgl; 14.04.2013
comment
Буферизация браузера не может мешать тесту, если смотреть error_log - person matteo; 14.04.2013
comment
Это сработало для меня!!! в .htaccess: SetEnv no-gzip dont-vary - и даже без implicit_flush при условии, что блоки вывода достаточно велики (например, много ..... вместо одного . в приведенном выше примере) - person matteo; 14.04.2013
comment
прохладно! Вы заставили id работать! Я не был уверен, есть ли шанс... Сейчас возвращаюсь домой с вечеринки. Завтра протестирую! :) - person hek2mgl; 14.04.2013
comment
Стоит отметить, что если ignore_user_abort=true, и вы вызываете flush из функции выключения, эта функция немедленно прекратит выполнение, и PHP перейдет к следующей функции выключения. См.: stackoverflow.com/ вопросы/6381270/ - person JS_Riddler; 09.11.2019