Зачем использовать буферизацию вывода в PHP?
Я прочитал довольно много материалов в Интернете, где разные авторы предлагают использовать буферизацию вывода. Самое забавное, что большинство авторов аргументируют его использование только потому, что он позволяет смешивать заголовки ответов с фактическим содержимым. Честно говоря, я думаю, что ответственные веб-приложения не должны смешивать вывод заголовков и контента, а веб-разработчикам следует искать возможные логические недостатки в своих сценариях, которые приводят к отправке заголовков после вывода. Это мой первый аргумент против API-буферизации вывода ob_*
. Даже для этого небольшого удобства вы получаете - микширование заголовков с выходом - это не очень хорошая причина для его использования, если не нужно быстро взламывать скрипты, что обычно не является целью и способом серьезного веб-приложения.
Кроме того, я думаю, что большинство людей, работающих с API буферизации вывода, не думают о том, что даже без явной буферизации вывода, PHP в сочетании с веб-сервером подключен, все еще делает некоторые внутренние буферизация. Легко проверить - сделать эхо короткой строки, спать на 10 секунд и сделать еще одно эхо. Запросите script с помощью браузера и посмотрите паузу на пустой странице в течение 10 секунд, после чего появятся обе строки. Прежде чем кто-то скажет, что это артефакт рендеринга, а не трафик, отслеживающий фактический трафик между клиентом и сервером, показывает, что сервер сгенерировал заголовок Content-Length
с соответствующим значением для всего вывода - что вывод не был отправлен постепенно с каждым вызовом echo
, но накапливается в некотором буфере, а затем отправляется на завершение script. Это одна из моих проблем с явной буферизацией вывода - почему нам нужны две разные реализации буфера вывода друг над другом? Может быть, потому, что внутренняя (недоступная) буферизация вывода на PHP/Web-сервере зависит от условий, которые разработчик PHP не может контролировать и, следовательно, не может использоваться?
В любом случае, для начала, подумайте, что нужно избегать явной буферизации вывода (серии функций ob_*
) и полагаться на неявный, помогая ему с хорошей функцией flush
, когда это необходимо. Возможно, если бы на веб-сервере была какая-то гарантия для отправки вывода клиенту с каждым вызовом echo/print, тогда было бы полезно настроить явную буферизацию - ведь никто не хочет отправлять ответ клиенту с 100 байтовые куски. Но альтернатива с двумя буферами кажется несколько бесполезным слоем абстракции.
Итак, в конечном счете, для серьезных веб-приложений нужна буферизация вывода?
Ответы
Ответ 1
Да
Серьезные веб-приложения нуждаются в буферизации вывода в одной конкретной ситуации:
Ваше приложение хочет контролировать то, что выдается сторонним кода, но нет API для контроля того, что этот код испускает.
В этом случае вы можете вызвать ob_start()
непосредственно перед передачей контроль над этим кодом, беспорядок с тем, что написано (в идеале с обратный вызов, или путем проверки содержимого буфера, если вы должны), и затем вызов ob_flush()
.
В конечном счете, ob_functions PHP - это механизм для захвата того, что какой-то другой бит кода делает в буфер, с которым вы можете столкнуться с.
Если вам не нужно проверять или изменять то, что записано в буфер, ничего не получается при использовании ob_start()
.
Скорее всего, ваше "серьезное приложение" на самом деле является какой-то структурой.
У вас уже есть буферизация вывода, в любом случае
Вам не нужно ob_start()
, чтобы использовать буферизацию вывода. Ваш веб-сервер уже блокирует ваш вывод.
Использование ob_start()
не улучшит буферизацию вывода - на самом деле это может увеличить использование и задержку использования памяти приложения за счет данных "накопления", которые в противном случае веб-сервер уже отправил бы клиенту.
Может быть ob_start()
...
... для удобства при промывке
В некоторых случаях вам может потребоваться контроль, когда веб-сервер очищает свой буфер, основываясь на некоторых критериях, которые ваше приложение знает лучше всего. В большинстве случаев вы знаете, что вы только что закончили писать логический "блок", который клиент может использовать, и вы говорите, что веб-сервер теперь скрывается и не дожидаться, когда буфер вывода будет заполнен. Для этого просто необходимо выпустить свой вывод как обычно и выделить его flush()
.
Реже, вы захотите убрать данные с веб-сервера, пока у вас не будет достаточно данных для отправки. Не нужно прерывать клиента с половиной новостей, особенно если остальная часть новостей займет некоторое время, чтобы стать доступной. Простой ob_start
, заключенный позже ob_end_flush()
, действительно может быть самым простым и подходящим делом.
... если вы несете ответственность за определенные заголовки
Если ваше приложение принимает на себя ответственность за расчет заголовков, которые могут быть определены только после того, как полный ответ будет доступен, тогда это может быть приемлемым.
Однако даже здесь, если вы не можете сделать ничего лучше, чем получить заголовок, проверив полный выходной буфер, вы можете также позволить веб-серверу (если он будет). Код веб-сервера написан, протестирован и скомпилирован - вы вряд ли улучшаете его.
Например, было бы полезно установить заголовок Content-Length
, если ваше приложение знает длину тела ответа после того, как оно вычислит тело ответа.
Нет панацеи от плохой практики
Вы не должны ob_start()
избегать дисциплин:
- открытие, с помощью и быстро закрытия ресурсов, таких как память, потоки и подключения к базе данных
- сначала излучающие заголовки, а второй -
- выполнив все вычисления и обработку ошибок, перед началом ответа
Если вы сделаете это, они вызовут технический долг, который заставит вас плакать один день.
Ответ 2
Хорошо, вот настоящая причина: выход не запускается, пока все не будет выполнено.
Представьте приложение, которое открывает соединение SQL и не закрывает его перед началом вывода. Случается, что ваш script получает соединение, начинает выводить, ждет, когда клиент получит все, что ему нужно, в конце, закрывает соединение. Woot, соединение 2s, где будет достаточно 0,3 с.
Теперь, если вы буферизируете, ваш script подключается, помещает все в буфер, автоматически отключается в конце, а затем начинает отправлять сгенерированный контент клиенту.
Ответ 3
Если вы хотите выводить отчет на экран, но также отправлять его по электронной почте, буферизация вывода позволяет вам не повторять обработку для вывода вашего отчета дважды.
Ответ 4
Наиболее очевидными случаями использования являются:
- Выходной фильтр (например,
ob_gzhandler
или любое количество фильтров, которые вы можете создать самостоятельно); Я сделал это с API-интерфейсами, которые поддерживают только выходные данные (а не возвращаемые значения), где я хотел выполнить последующий синтаксический анализ с помощью библиотеки phpQuery.
- Обслуживание (а не переписывание) кода, написанного со всеми проблемами, которые вы обсуждаете; это включает в себя такие вещи, как отправка заголовков после начала работы (кредит Дон Дикинсон) или подавление определенного результата, который уже был сгенерирован.
- Разбитый выход (кредит здесь для Тома и Лэнгдона); обратите внимание, что ваши тесты могут быть неудачными, поскольку они конфликтуют с внутренним буфером PHP/Apache по умолчанию, но это можно сделать, просто требуется, чтобы определенная сумма была очищена до того, как PHP отправит что-нибудь. PHP все равно будет поддерживать соединение открытым.
Ответ 5
Я использую буферизацию вывода по одной причине... он позволяет мне отправить заголовок "location" после того, как я начал обрабатывать запрос.
Ответ 6
Буферизация вывода имеет решающее значение для IIS, которая не делает внутренней внутренней буферизации. Когда выходная буферизация отключена, скрипты PHP работают намного медленнее, чем на Apache. Включите его, и они работают много раз быстрее.
Ответ 7
Это старый вопрос, но никто не сказал, что важной особенностью буферизации outbut является фильтрация. Перед отправкой его клиенту можно предварительно обработать буфер.
Это очень мощная концепция и открывает множество интригующих возможностей. В проекте я использовал два фильтра одновременно:
- специальный перевод терминов (замена коротких текстов)
- обфускация HTML, CSS и Javascript (не спрашивайте меня почему)
Чтобы включить вызов фильтрации вывода ob_start("callback")
, где callback
- имя функции фильтрации. Для получения дополнительной информации см. Руководство PHP для ob_start
: http://php.net/manual/en/function.ob-start.php
Ответ 8
Я использую буферизацию вывода, чтобы избежать генерации HTML путем конкатенации строк, когда мне нужно знать результат операции рендеринга для создания некоторого вывода, прежде чем использовать рендеринг.
Ответ 9
Мы использовали его в тот же день для страниц с чрезвычайно длинными таблицами, заполненными данными из базы данных. Вы очищаете буфер каждые x строк, чтобы пользователь знал, что страница действительно работает. Затем кто-то услышал об удобстве использования, и на страницах, подобных этому, были найдены поисковые запросы и поисковые запросы.
Ответ 10
Это полезно, если вы пытаетесь отобразить индикатор выполнения во время страницы, на выполнение которой требуется некоторое время. Поскольку PHP-код не является многопоточным, вы не можете этого сделать, если обработка зависает, выполняя 1 функцию.
Ответ 11
Используйте буферизацию вывода для кэширования данных в файле, для других подобных запросов, если вы выполняете много транзакций и обработки базы данных.