Php очистить вывод в браузере

Обновлено: 05.07.2024

Буферизация вывода в PHP это довольно полезная штука, если уметь ею пользоваться. Скажите сколько раз вы видели ошибки типа:

Warning: Cannot modify header information - headers already sent by (output started at . )

Возможности при буферизации

Используя буферизацию вывода мы можем:

  • Посылать cookie из любого места в скрипте.
  • Стартовать сессию в любой момент.
  • Сжимать данные, перед отправкой их пользователю.

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

Что происходит при буферизации?

Как включить буферизацию вывода?

Первый способ это если сервак ваш, или у вас просто есть доступ к файлу php.ini ( как я писал выше ) ищем в нём директиву output_buffering и присваиваем ей значение On. Это включит буферизацию для всех скриптов.

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

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

Итак, открыть такой контейнер - буфер можно лишь одной функцией ob_start(), а вот закрыть этот самый буфер можно двумя функциями: ob_end_flush() и ob_end_clean()

Закрывает буфер и отправляет данные.

Закрывает буфер без отправки данных.

Все содержимое, которое выводиться в тот момент, когда буфер открыт попадает в буфер и никуда не отсылается. Например:

Этот скрипт выведет результат:

Разберемся что произошло. Мы создали три буфера. Строки 1-3 - это первый. Он закрыт функцией ob_end_flush() - его вывод мы можем наблюдать в результате.

Второй буфер - это строки 5-7. Он закрыт с помощью функции ob_end_clean() - поэтому его вывод пропал безследно.

И третий буфер - это строки 9-10. Он у нас не закрыт никак! - Шозанах? - спросите вы . Всё просто - по окончанию сценария, PHP автоматом закрывает все буферы так, как будто они были закрыты функцией ob_end_flush().

Повторное использование буферов

Функции ob_end_flush() и ob_end_clean() дополняются функциями: ob_flush() и ob_clean() - они делают то же самое, но не закрывают буфер вывода. Верхний пример с их применением может выглядеть так:

Этот скрипт выведет результат:

Смотрим сверху вниз. На этот раз содержимое выводится так же строка 1-3, но буфер не закрывается, затем буфер просто чистится (строа 5) без вывода содержимого, оставаясь открытым, и наконец буфер автоматом закрывается, а содержимое выводится, так как подошёл конец скрипта. Такой подход даст 60% прирост производительности, по сравнению с предыдущим, за счёт того, что мы избегаем лишних открытий/закрытий буферов.

Стек буферов

Если рассматривать буферы вывода как контейнеры, то вполне можно понять тот факт, что один контейнер - буфер, можно поместить внутрь другого, создав некий стек буферов, который в свою очередь подчиняется определённым правилам. Стек буферов приблизительно можно представить так:

Стек буферов вывода

Подпись "уровень" - это фактический уровень вложенности буфера. Когда в коде следует инструкция очистить буфер и вывести его содержимое ( например вызов функции ob_end_flush() ), то содержимое буфера выводится внутрь его родительского контейнера. Если родительского контейнера нет, то содержимое выводится в основной поток, т.е. (условно) на экран:

Обратите внимание: очерёдность вывода сохраняется. Теперь проведём такой эксперимент - "зарэжэм"основной буфер, т.е. тот который находиться на самом верху стека:

Теперь мы с вами ничего не увидим, потому что вложенные буферы извлекли своё содержимое в буфер верхнего уровня, а буфер верхнего уровня у нас закрылся функцией ob_end_clean() - которая накрыла весь вывод медным тазом.

Главное помнить, что буферы вывода работают в стеке, и всё будет ок.

Чтение буферов вывода

Буфер вывода - штука двунаправленная, т.е. в него можно как писать (ударение на втором слоге! ), так и читать из него. Читать можно в переменную. Сделать это позволяет функция ob_get_contents()

Эта функция не принимает параметров, и возвращает всё содержимое последнего открытого буфера:

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

Другие ob - функции

У механизма буферизации вывода есть ещё несколько полезных функций:

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

Не принимает никаких параметров, возвращает массив, содержащий дескрипторы вывода, которые работают на данный момент. Если буферизация включена - вернёт массив, в котором будет дескриптор по-умолчанию, если используется gzip сжатие буфера (об этом позже) то получим дескриптор "ob_gzhandler", а если используется замена URL то получим "URL-Rewriter".

Отправка выходных данных

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

При работе этого скрипта данные к клиенту будут уходить частями, с интервалом в 1 сек. И клиент в реальном времене будет видеть, как у него появляются новые данные.

В IE есть некий механизм "оптимизации" который отображает страницу только после получения первых 256 байт информации! Независимо от применения вами функции flush(). Так что имейте ввиду с "ишаком", как всегда могут быть проблемы, если ваш скрипт отправляет клиенту меньше 256 символов ( или примерно 128 символов кирилицы в юникоде ) .

Функция ob_implicit_flush() - избавляет вас от вызова каждый раз функции flush():

Этот скрипт даст тот же эффект, что и скрипт выше.

Сжатие выходных данных

Для того чтобы сжать буфер вывода нужно передать функции ob_start() параметр - имя функции - компрессора, например ob_gzhandler.При этом будет выполнена проверка поддержки сжатия и если такая поддержка есть, то данные будут сжиматься:

Результат обведён красным:

Сжатие буфера вывода

А вот результат без сжатия вывода:

Без сжатия вывода

Сжатие работает только применительно к HTML - коду, к картинкам и прочему это не относится. Учитывайте это. А ещё PHP позволяет сжимать только один буфер вывода, так как содержимое должно сжиматься всё и сразу. Имейте это ввиду, если вы используете стек буферов вывода.

Переписывание URL

Скажу сразу что к ЧПУ ссылкам и к mod_rewrite данные возможности не имеют никакого отношения, но тем не менее позволяют вытворятьинтересные штуки.

output_add_rewrite_var( 'name', 'value' )

Эта функция принимает два параметра, первый это имя переменной, а второй это значение этой переменной. Эта пара ключ-значение добавиться ко всем URL, формам и атрибутам SRC - элементов FRAME, в виде GET - переменных строки запроса. К формам добавятся скрытые поля с соответствующими значениями. В общем проще показать на примере:

Выдаст такой HTML - код:

Заметьте атрибуты action тега form - остались не тронуты! Пример с тегами FRAME:

Выдаст такой HTML - код:

Функцию output_add_rewrite_var() можно вызывать несколько раз с разными параметрами, в этом случае пары будут добавляться согласно формату кодирования данных: "application/x-www-form-urlencoded" разделяясь знаками амперсанда а в формы будут добавляться новые скрытые поля. Вот так будет выглядеть пример:

Выдаст такой HTML - код:

А вот если в конце примера вызвать функцию output_reset_rewrite_vars() - то она отменит все изменения произведённые вызовами функций output_add_rewrite_var(. ) т.е. все ссылки вернуться к исходному состоянию.

Ну, вот надеюсь я доступно объяснил что такое буферизация вывода в PHP и с чем её едят.

Не могу понять как делать вывод на экран структуры STUDENT на заполнение и вывод данных на экран
Добрый вечер. Вот задание и мой код программы на C++ Не выходит нормально реализовать 3е.

Синий экран при удалении аваста и очистка места
В общем, сначала я был обладателем касперского, после этого решил перейти на аваст, при установке.


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

-Aleks-, можно сделать скрипт который загружает на сервер файлы, и при вызове очередной, предыдущая сама сотрется с экрана.. (можно в БД грузить и выгружать) -Aleks-, Эмм. Никак. Но хотя, может кто-то и придумает эдакий велик, который будет полностью перезагружать страницу Ведь когда текст уже выведен в браузер, то это значит, что PHP уже отработал свое. Дальше дело js и других клиентских языков. Добавлено через 2 минуты
конечно хтмл-страничку разом поменять я не знаю (возможно как ты говоришь и нет решения).
я имею ввиду отображение статьи на страничке.. (я так понял) $fileReset = fopen("file_with_XML.txt", "w+");
Вот и всё. Коротко и то, что нужно.

Очистка консоли, очистка строки, установка курсора
Здравствуйте! Подскажите как можно после очистки консоли поставить курсор в самый вверх, чтоб то.

Очистка графика + очистка формы
Есть две программы: Прямо на форме (через Canvas) рисую правильный многоугольник, но после.


Побуквенный вывод слова через цикл + ожидания нажатия клавиши (после нажатия очистка и установка курсора на )
строку) Masm32 (com -программа) вообщем столкнулся с проблемой. к примеру ввожу ' LSD49 $'.


Сгенерировать и вывести на экран массив с 10 случайных чисел в диапазоне от -100 до 100. Цикличный сдвиг массива влево на 1 и вывод на экран
Помогите написать программку в Паскале, что сгенерирует и выведет на экран массив с 10 случайных.

Создание сценария,обеспечивающего вывод на экран содержимого окна "Экран"
Прошу помощи в создании сценария так как попросту не знаю VBScript. =( Может кто имел дело с таким.

Что такое буферизация вывода PHP? главное изображение

Буферизация вывода — одна из тех концепций в PHP, с которой достаточно часто сталкиваются разработчики, но обычно никто даже не задумывается, как она устроена. Мы перевели текст What is PHP Output Buffering? разработчика Лары Шенк, в котором она детально объясняет эту концепцию.

Разве эта концепция вообще важна?

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

В эту кроличью нору с буферизацией в PHP я попал из-за вопроса, который мне пришел на почту про плагин Timber — я люблю использовать его при разработке сайтов на WordPress. У меня спросили: «Что делают header.php и footer.php в плагине Timber?». Быстрый ответ на этот вопрос: «Они ничего не делают», потому что они нужны для работы определенных плагинов в WordPress — однако я этого не знал, потому что мне никогда не приходилось сталкиваться со взаимодействием с этими файлами во время разработки. Мне стало интересно, и я начал исследовать, из чего состоят эти файлы. Оказалось, что в основном в них находятся функции буферизации вывода PHP ( ob_start, ob_get_contents). В этой статье мы не будем подробно разбирать устройство Timber, но поговорим про буферизацию вывода в PHP.

Что такое буферизация вывода?

Давайте представим диалог разработчика с языком программирования PHP (если бы с ним можно было просто поговорить):

PHP: «Хорошо, я сохраню его в буфере, пока ты не скажешь мне, когда отправлять его в браузер».

Вы: «Спасибо, PHP! Не волнуйся, ты по-прежнему актуален, и другие языки тебя не заменят!».

Функции

Существует несколько основных функций для работы буферизации вывода:

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

ob_get_contents() собирает все данные, которые находятся в буфере после выполнения команды ob_start. Обычно с помощью этой функции данные из буфера присваивают переменной.

ob_clean() удаляет все из буфера. Обратите внимание, что она ничего не выводит — просто очищает буфер.

ob_flush() выводит содержимое из буфера. Обратите внимание, что она не очищает буфер.

ob_end_clean() очищает буфер и отключает буферизацию вывода.

ob_end_flush() выводит содержимое из буфера и завершает буферизацию вывода. Буфер не стирается.

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

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

Вполне возможно, что вы никогда не будете использовать буферизацию вывода, особенно если вы не являетесь разработчиком плагинов WordPress, или не пишете собственные приложения PHP. Для тех, кто не делает ни того, ни другого, я дам несколько примеров, когда эта концепция может пригодиться:

Вы когда-нибудь видели такую ошибку?

Warning: Cannot modify header information — headers already sent by (output started at /some/file.php:12) in /some/file.php on line 23

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

Вывод

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

В этой статье я хочу рассказать о том, как реализован слой «буферизации вывода» в PHP, как работает и как с ним взаимодействовать из PHP. В этом слое нет ничего сложного, но многие разработчики либо совсем не понимают, как с ним обращаться, либо не имеют полной ясности. Всё, о чём я буду писать, относится к PHP версии 5.4 и выше. Именно начиная с неё изменились многие вещи, связанные с буфером вывода (БВ). По сути, этот функционал был полностью переписан, поэтому совместимость с версией 5.3 сохранилась лишь частично.

Второй важный момент: слой БВ является не единственным слоем, в котором буферизуются выводимые данные.

И третье: в зависимости от SAPI, который вы используете (веб или cli), слой БВ может вести себя по-разному.

Ниже представлена схема, которая поможет понять всё вышесказанное:


Здесь мы видим, что для управления выводимыми данными в PHP используется три логических слоя буферизации. Два из них принадлежат тому самому «буферу вывода», а третий — SAPI. Когда поток вывода покидает область PHP, чтобы попасть на нижний уровень архитектуры, «по пути» могут возникнуть новые буферы: буфер терминала, буфер FastCGI, буфер веб-сервера, буфер операционной системы, буферы стеков TCP/IP. Не забывайте об этом. Хотя в рамках данной статьи мы будем говорить только о PHP, в стеке на пути данных к нижнему слою и пользователю встретится ещё немало программных средств.

Важное замечание относительно CLI SAPI: он отключает в PHP любой буфер вывода по умолчанию, присвоив в ini параметру output_buffering значение 0. Так что, пока вы в CLI не пропишете вручную функции ob_(), по умолчанию все выводимые данные будут напрямую попадать в слой SAPI. Более того, в CLI для параметра implicit_flush жёстко указано значение 1. Суть этого параметра разработчики вечно понимают неправильно, хотя код говорит совершенно недвусмысленно: когда implicit_flush имеет значение 1, буфер слоя SAPI сбрасывается при каждой записи. То есть каждый раз, когда вы записываете данные для вывода с помощью CLI SAPI, они немедленно отправляются на нижний уровень, где записываются в виде stdout, а потом сбрасываются.

  • output_buffering
  • implicit_flush
  • output_handler

По умолчанию в php.ini, идущем в составе поставки PHP, output_buffering присвоено значение «4096» (байт). Если вы не используете php.ini (или запускаете PHP с ключом –n), то значением по умолчанию будет «0», то есть отключено. Если захардкодить значение «On», то будет назначен стандартный размер буфера вывода (16 КБ).

Как вы уже, наверное, догадались, использование буфера для вывода в веб-окружении благотворно влияет на производительность. Начальных 4 КБ вполне достаточно, ведь это означает, что вы можете записать до 4096 ASCII-символов, пока PHP не начнёт взаимодействовать с нижерасположенным слоем SAPI. В условиях веба отправка данных побайтно, напротив, производительность не улучшает. Гораздо лучше, если сервер отправляет весь контент скопом или большими частями. Чем реже уровни обмениваются данными, тем лучше с точки зрения производительности. Поэтому обязательно используйте буфер вывода. PHP отправит его содержимое в конце запроса и вам для этого ничего не придётся делать.

В предыдущей главе я упоминал об implicit_flush в контексте CLI. В случае с любым другим SAPI implicit_flush изначально отключён. Это хорошо, поскольку вряд ли вы будете приветствовать сброс SAPI сразу же после записи в него. Для протокола FastCGI сброс можно сравнить с завершением и отправкой пакета после каждой записи. Однако лучше сначала полностью заполнить буфер FastCGI, а уже потом слать пакеты. Если вам нужно вручную сбросить буфер SAPI, используйте для этого PHP-функцию flush(). Для сброса после каждой записи, как уже говорилось выше, можно использовать параметр implicit_flush в php.ini. Как вариант — однократный вызов PHP-функции ob_implicit_flush().

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

Давайте разберём на примерах, как это работает, и что вы можете сделать. Имейте в виду, что если вы хотите использовать стандартный PHP-слой буферизации, то не сможете воспользоваться CLI, поскольку он отключается как слой.

Ниже приведён пример работы со стандартным PHP-слоем с помощью внутреннего веб-сервера SAPI:

Мы запустили PHP со стандартным буфером вывода на 32 байта, после чего сразу же записали в него 31 байт, пока не включилась задержка исполнения. Экран чёрный, пока ничего не отправлено. Затем действие sleep() заканчивается, и мы записываем ещё один байт, тем самым полностью заполняя буфер. После этого он сразу же сбрасывает себя в буфер слоя SAPI, а тот сбрасывает себя в вывод, поскольку implicit_flush имеет значение 1. На экране появляется строка aaaaaaaaaab, после чего опять начинает действовать sleep(). По его завершении пустой 31-байтный буфер заполняется одним-единственным байтом, после чего PHP завершается и сбрасывает буфер. На экране появляется с.

Так выглядит работа стандартного PHP-буфера без вызова каких-либо ob-функций. Не забудьте, что это именно стандартный буфер, то есть он уже имеется в наличии (только нельзя использовать CLI).

Теперь с помощью ob_start() можно запускать пользовательские буферы, причем столько, сколько нужно, пока память не закончится. Каждый буфер будет помещаться за предыдущим и немедленно сбрасываться в следующий, что постепенно приведёт к переполнению.

Как я уже говорил, начиная с версии 5.4 механизм буферизации вывода был полностью переписан. До этого код был очень неаккуратным, многие вещи сделать было непросто, часто возникали баги. Подробнее об этом можно почитать по ссылке. Новая кодовая база получилась гораздо чище, лучше организована, появились новые возможности. Правда, совместимость с версией 5.3 обеспечивается лишь отчасти.

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

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

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

К неочевидным подводным камням можно отнести то, что некоторые функции PHP используют внутренний БВ для самих себя, заполняя его, а затем сбрасывая или возвращая. При этом следующий буфер ставится в стек. К подобным функциям относятся print_r(), highlight_file() и SoapServer::handle(). Не следует использовать их изнутри callback-а БВ – это может привести к непредсказуемым последствиям.

Слой вывода можно сравнить со своеобразной сетью, которая улавливает любые возможные «утечки» вывода из PHP и сохраняет их в буфере заданного размера. Когда буфер заполняется, он сбрасывается (записывается) в нижний уровень, если таковой есть. Как минимум в самый нижний из имеющихся — в буфер SAPI. Пользователи могут управлять количеством буферов, их размером и операциями, которые могут быть разрешены в каждом слое буфера (очистка, сброс или удаление). Это очень гибкий инструмент, позволяющий, например, создателям библиотек и фреймворков полностью контролировать поток вывода, направляя его в глобальный буфер и обрабатывая там. При этом PHP сам регулирует порядок отправки заголовков и потока вывода.


Что вы не узнаете из документации, момент с дырой в безопасности + советы о том, как ускорить отклик сервера.

Буферизация вывода позволяет вам сохранять выходные данные PHP (в основном генерируемые echo, var_dump, print_r) в памяти (т.е. в буфере) вместо немедленной передачи в браузер или терминал. Что полезно для самых разных задач:

Захватить вывод и записать в переменную: (Таким образом можно создавать кэш)

Буфер так же может быть очищен в любое время не выключая его, используя функцию ob_clean() (очищает буфер) или ob_flush() (отправляет буфер на выход):

В буфер также отправляются выходные данные, записанные в выход php://output, буферизацию можно избежать, записав в php://stdout (или STDOUT), который доступен только в CLI, т.е. при запуске скриптов из командной строки.

Гнездование (вложенные буферы)

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

Поэтому важно отключить буферизацию, даже если происходит исключение:

Размер буфера (chunk_size)

Буферизация также может улучшить производительность сервера, когда PHP не будет отправлять каждое echo в браузер, а вместо этого будет отправлять большие куски данных, например, по 4 кб. Просто вызовите в начале скрипта:

Когда размер буфера превышает 4096 байт, PHP автоматически выполняет flush, т.е. буфер очищается и отправляется. То же самое может быть достигнуто установкой директивы output_buffering, которая игнорируется в CLI.

Будьте осторожны , если вы начнете буферизацию без указания размера (то есть просто ob_start()), это приведет к тому, что страница будет отправляться не непрерывно, а один раз в конце скрипта, поэтому сервер будет реагировать очень медленно!

Дыра в безопасности

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

Пользовательские обработчики

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

Также ob_clean() и ob_end_clean() вызывают обработчик, но отбрасывают вывод. Обработчик может определить, какая функция вызывается, и ответить на нее с помощью второго параметра $phase, который является битовой маской (начиная с PHP 5.4)

  • PHP_OUTPUT_HANDLER_START когда буфер активирован
  • PHP_OUTPUT_HANDLER_FINAL когда буфер отключен
  • PHP_OUTPUT_HANDLER_FLUSH при вызове ob_flush() (но не ob_end_flush() или ob_get_flush())
  • PHP_OUTPUT_HANDLER_CLEAN при вызове ob_clean(), ob_end_clean() или ob_get_clean()
  • PHP_OUTPUT_HANDLER_WRITE при автоматическом flush

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

Читайте также: