PHP: закрыть выходной поток

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

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


person Tiddo    schedule 23.01.2012    source источник
comment
Зачем вам это нужно? Можете ли вы опубликовать немного своего кода, чтобы помочь нам понять немного больше?   -  person Jonathan M    schedule 24.01.2012
comment
+1 к тому, что сказал Джонатан. Расскажите нам немного больше о том, почему вы хотите это сделать.   -  person Radu Murzea    schedule 24.01.2012


Ответы (3)


ОБНОВЛЕНИЕ

В этом случае можно справиться с помощью буферизации вывода и соответствующих заголовков HTTP.

Из раздела 14.10 спецификации HTTP/1.1:

HTTP/1.1 определяет опцию «закрыть» соединение для отправителя, чтобы сигнализировать, что соединение будет закрыто после завершения ответа.

Таким образом, если мы передаем HTTP-заголовок Content-Length в дополнение к Connection: close, браузер знает, что нужно закрыть соединение после получения ответа указанной длины:

  1. Буферизируйте ВСЕ выходные данные сценария, чтобы сохранить возможность отправки заголовков.
  2. Получив полные выходные данные, отправьте клиенту соответствующие заголовки.
  3. Продолжайте обработку... но не пытайтесь отправить выходные данные, иначе вы получите ошибки из-за того, что заголовки были отправлены.

Кроме того, будьте осторожны, так как вы можете столкнуться с ограничениями времени выполнения скрипта в SAPI веб-сервера, если вы выполняете слишком много обработки. Наконец, вы должны указать PHP игнорировать «пользовательское прерывание» в этом конкретном скрипте, используя ignore_user_abort(), потому что браузер закроет соединение в результате того, что вы делаете, и вы хотите, чтобы PHP продолжил обработку.

<?php
ignore_user_abort();
ob_start();

// do stuff, generate output

// get size of the content
$length = ob_get_length();

// tell client to close the connection after $length bytes received
header('Connection: close');
header("Content-Length: $length");

// flush all output
ob_end_flush();
ob_flush();
flush();

// close session if you have one ...
// continue your processing tasks ...
?>

Вы можете изучить раздел руководства по PHP, посвященный Обработка соединенийдокументы.

В качестве альтернативы, почему бы не запустить буферизацию вывода? Затем вы можете захватить все выходные данные, которые будут отправлены, а затем решить, действительно ли вы хотите что-то с ними делать.

<?php

echo 'before output buffering';
ob_start();
echo 'after output buffering';
$output = ob_get_contents();

// script's only output to this point will be 'before output buffering'

// I changed my mind, send the output ...
ob_end_flush();
?>
person rdlowrey    schedule 23.01.2012
comment
Буферизация вывода по-прежнему сохраняет поток вывода открытым для браузера. Итак, предположим, мне нужно выполнить некоторую постобработку, которая занимает несколько секунд или около того, браузер будет ждать, пока эта постобработка не будет выполнена. Особенно с AJAX это сильно замедлит веб-приложение. Поэтому я хочу иметь возможность закрыть соединение с браузером, чтобы браузер мог обработать вывод, а затем продолжить постобработку. - person Tiddo; 24.01.2012
comment
@Tiddo Я обновил свой ответ (как я считаю) правильным решением. - person rdlowrey; 24.01.2012
comment
Это довольно хорошее решение! Я собираюсь попробовать это через минуту. Редактировать: он отлично работает! Большое спасибо! - person Tiddo; 24.01.2012
comment
@Tiddo, еще кое-что - я добавил, что вы должны вызывать ignore_user_abort() в верхней части вашего скрипта. Это могло быть очевидно из ссылки на руководство по PHP в моем исходном ответе, но я хотел указать это явно. Так что, просто убедитесь, что вы делаете это :) - person rdlowrey; 24.01.2012
comment
Хорошо, большое спасибо! В моем сценарии я также добавил буферизацию вывода для всего после отправки исходного вывода, поэтому, если какой-либо вывод будет отправлен случайно, я могу просто отбросить его, и сценарий не рухнет. - person Tiddo; 24.01.2012

fclose(STDOUT): http://php.net/manual/en/features.commandline.io-streams.php

person Marc B    schedule 23.01.2012

У меня недостаточно репутации, чтобы комментировать, но я хочу поделиться тем, что в ответе @rdlowrey gzip может быть проблемой.

Если у вас включен gzip, заголовок Transfer-encoding всегда имеет значение chunked, даже если вы попытаетесь изменить его с помощью header("Transfer-encoding: none");, поэтому он не будет не отправлять заголовок Content-Length.

Способ, которым я мог решить эту проблему, заключался в следующем:

<?
@ini_set('zlib.output_compression', 'Off');
@ini_set('output_buffering', 'Off');
@ini_set('output_handler', '');
@apache_setenv('no-gzip', 1);
?>

И тогда решение:

<?
ignore_user_abort();
ob_start();

// do stuff, generate output

// get size of the content
$length = ob_get_length();

// tell client to close the connection after $length bytes received
header('Connection: close');
header("Content-Length: $length");

// flush all output
ob_end_flush();
flush();

// close session if you have one ...
// continue your processing tasks ...
?>
person oscargilfc    schedule 03.06.2015