Зачем использовать буферизацию вывода в PHP?

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

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

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

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

да

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

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

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

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

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

Вполне вероятно, что ваше «серьезное применение» на самом деле является какой-то структурой.


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

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

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


Возможно, ob_start()

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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