Зачем использовать буферизацию вывода в PHP? - PullRequest
27 голосов
/ 27 января 2010

Я прочитал довольно много материала в Интернете, где разные авторы предлагают использовать буферизацию вывода. Самое смешное, что большинство авторов аргументируют его использование только потому, что оно позволяет смешивать заголовки ответов с реальным содержимым. Честно говоря, я думаю, что ответственные веб-приложения не должны смешивать выходные заголовки и контент, и веб-разработчики должны искать возможные логические недостатки в своих сценариях, которые приводят к отправке заголовков после генерации вывода. Это мой первый аргумент против ob_* API буферизации вывода. Даже для такого небольшого удобства, которое вы получаете - смешивая заголовки с выводом, - это недостаточно веская причина для его использования, если только вам не нужно быстро взламывать сценарии, что обычно не является целью или способом серьезного веб-приложения.

Кроме того, я думаю, что большинство людей, имеющих дело с API буферизации вывода, не думают о том факте, что даже если явно не включена явная буферизация вывода, PHP в сочетании с веб-сервером, к которому он подключен, все еще выполняет некоторые внутренние функции. буферизация в любом случае. Это легко проверить - сделайте эхо какой-нибудь короткой строки, поспите 10 секунд и сделайте еще одно эхо. Запросите ваш скрипт в браузере и наблюдайте за паузой пустой страницы в течение 10 секунд, после чего появятся обе строки. Прежде чем кто-то скажет, что это артефакт рендеринга, а не трафик, отслеживание фактического трафика между клиентом и сервером показывает, что сервер сгенерировал заголовок Content-Length с подходящим значением для всего вывода - предполагая, что вывод не был отправлен постепенно с каждым вызовом echo, но накапливается в некотором буфере и затем отправляется при завершении скрипта. Это одна из моих проблем с явной буферизацией вывода - зачем нам две разные реализации буфера вывода друг на друга? Может ли это быть из-за того, что внутренняя (недоступная) буферизация вывода PHP / Web-сервера зависит от условий, которые разработчик PHP не может контролировать, и, таким образом, на самом деле не используется?

В любом случае, я, например, начинаю думать, что следует избегать явной буферизации вывода (серии функций ob_*) и полагаться на неявную, помогая ей с хорошей функцией flush, когда это необходимо. Может быть, если бы была какая-то гарантия от веб-сервера на фактическую отправку вывода клиенту при каждом вызове echo / print, тогда было бы полезно установить явную буферизацию - ведь никто не хочет отправлять ответ клиенту с какими-то 100 байтовые куски. Но альтернатива с двумя буферами выглядит несколько бесполезным уровнем абстракции.

Так, в конечном счете, серьезным веб-приложениям нужна буферизация вывода?

Ответы [ 11 ]

12 голосов
/ 05 июля 2013

Да

Серьезным веб-приложениям нужна буферизация вывода в одной конкретной ситуации:

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

В этом сценарии вы можете позвонить ob_start() непосредственно перед вручением контроль над этим кодом, возиться с тем, что написано (в идеале с обратный вызов, или путем проверки содержимого буфера, если необходимо), и затем звоните ob_flush().

В конечном счете, PHP-функции ob_ - это механизм для захвата того, что какой-то другой бит кода делает в буфере , с которым может связываться .

Если вам не нужно проверять или изменять то, что записывается в буфер, ничего не получается с помощью ob_start().

Вполне вероятно, что ваше "серьезное приложение" на самом деле является своего рода фреймворком.


В любом случае, у вас уже есть выходная буферизация

Вам не нужно ob_start(), чтобы использовал буферизацию вывода. Ваш веб-сервер уже делает буферизацию вашего вывода.

Использование ob_start() не дает вам лучшей буферизации вывода - это может фактически увеличить использование памяти вашего приложения и задержку за счет «накопления» данных, которые веб-сервер в противном случае уже отправил бы клиенту .


Может быть ob_start() ...

... для удобства при промывке

В некоторых случаях вам может потребоваться контроль над , когда веб-сервер очищает свой буфер, основываясь на некоторых критериях, которые ваше приложение знает лучше всего. В большинстве случаев вы знаете, что вы только что закончили писать логический «модуль», который может использовать клиент, и вы говорите веб-серверу сбрасывать now и не ждать выходного буфера заполнить. Для этого просто необходимо вывести ваш вывод в обычном режиме и пунктуировать его с помощью flush().

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

... если вы несете ответственность за определенные заголовки

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

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

Например, было бы полезно установить заголовок Content-Length, только если ваше приложение знает длину тела ответа до того, как оно вычислит тело ответа.


Нет панацеи от плохой практики

Вы не должны ob_start() избегать дисциплин:

  • открытие , с использованием и быстрое закрытие ресурсов, таких как память, потоки и соединения с базой данных
  • сначала заголовки, а затем тело
  • все вычисления и обработка ошибок выполняются до начала ответа

Если вы сделаете это, они вызовут технический долг, который однажды заставит вас плакать.

9 голосов
/ 27 января 2010

Хорошо, вот настоящая причина: выход не запускается, пока все не будет сделано. Представьте себе приложение, которое открывает соединение SQL и не закрывает его перед началом вывода. Происходит следующее: ваш скрипт получает соединение, начинает выводить, ждет, пока клиент получит все, что ему нужно, и в конце закрывает соединение. Woot, соединение 2s, где достаточно 0.3s.

Теперь, если вы буферизуете, ваш скрипт подключается, помещает все в буфер, автоматически отключается в конце, а затем начинает отправку сгенерированного контента клиенту.

5 голосов
/ 27 января 2010

Наиболее очевидные варианты использования:

  1. Выходной фильтр (например, ob_gzhandler или любое количество фильтров, которые вы можете создать самостоятельно); Я сделал это с API, которые поддерживают только вывод (а не возвращаемые значения), где я хотел выполнить последующий анализ с помощью библиотеки, такой как phpQuery .
  2. Обслуживание (а не переписывание) кода, написанного со всеми обсуждаемыми вами проблемами; это включает в себя такие вещи, как отправка заголовков после начала вывода (кредит Дон Дикинсон) или подавление определенного вывода, который уже был создан.
  3. Пораженный выход (благодарность Тому и Лэнгдону); обратите внимание, что ваши тесты могут быть неудачными, потому что они конфликтуют с внутренним буфером PHP / Apache по умолчанию, но это возможно , для этого просто требуется сброс определенной суммы перед тем, как PHP отправит что-либо - PHP все равно сохранит хотя соединение открыто.
5 голосов
/ 27 января 2010

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

3 голосов
/ 04 августа 2012

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

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

  1. специальный перевод терминов (замена коротких текстов)
  2. обфускация HTML, CSS и Javascript (не спрашивайте меня, почему)

Чтобы включить выходную фильтрацию, вызовите ob_start("callback"), где callback - это название функции фильтрации. Для получения более подробной информации см. Руководство по PHP для ob_start: http://php.net/manual/en/function.ob-start.php

3 голосов
/ 27 января 2010

Буферизация вывода критически важна для IIS, которая не имеет собственной внутренней буферизации. Когда выходная буферизация отключена, PHP-скрипты работают намного медленнее, чем в Apache. Включите его, и они будут работать во много раз быстрее.

3 голосов
/ 27 января 2010

Я использую буферизацию вывода по одной причине ... это позволяет мне отправлять заголовок "location" после того, как я начал обработку запроса.

2 голосов
/ 27 января 2010

Я использую буферизацию вывода, чтобы избежать генерации HTML путем конкатенации строк, когда мне нужно знать результат операции рендеринга для создания вывода до Я использую рендеринг.

1 голос
/ 27 января 2010

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

0 голосов
/ 16 ноября 2010

Используйте буферизацию вывода для кэширования данных в файле, для других подобных запросов, если вы выполняете много транзакций и обработки базы данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...