When a script ends, all buffered output is flushed (this is not a bug: http://bugs.php.net/bug.php?id=42334&thanks=4). What happens when the script throws an error (and thus ends) in the middle of an output buffer? The script spits out everything in the buffer before printing the error!
Here is the simplest solution I have been able to find. Put it at the beginning of the error handling function to clear all buffered data and print only the error:
$handlers = ob_list_handlers();
while ( ! empty($handlers) ) {
ob_end_clean();
$handlers = ob_list_handlers();
}
ob_start
(PHP 4, PHP 5)
ob_start — 出力のバッファリングを有効にする
説明
bool ob_start ( [callback $output_callback [, int $chunk_size [, bool $erase]]] )この関数は出力のバッファリングをオンにします。 出力のバッファリングを有効にすると、 (ヘッダ以外の) スクリプトからの出力は実際には行われず、 代わりに内部バッファに保存されます。
この内部バッファの内容は、ob_get_contents() を用いて文字列変数にコピーされます。 内部バッファの内容を出力するには ob_end_flush() を使用します。 ob_end_clean() は、バッファの内容を出力せずに消去します。
オプションの引数 output_callback 関数を指定することが可能です。この関数は、パラメータとして文字列をとり、 文字列を返します。このコールバック関数は、 ob_end_flush() がコールされた際、 またはリクエストの最後にブラウザに出力をフラッシュする際にコールされます。 output_callback がコールされた際に、 この関数は出力バッファの内容をパラメータとして受け取ります。このコールバック関数は、 新規の出力バッファを実際に出力される結果として返す必要があり、 この結果はブラウザに送信されます。 output_callback がコール可能な関数ではない場合は FALSE を返します。 コールバック関数が 2 つの引数をとる場合、 2 番目のパラメータは以下のビットフィールド PHP_OUTPUT_HANDLER_START、 PHP_OUTPUT_HANDLER_CONT および PHP_OUTPUT_HANDLER_END を含みます。 output_callback が FALSE を返すと、元の入力がそのままブラウザに送信されます。
注意: PHP 4.0.4 において、Web ページの圧縮をサポートする圧縮 gz エンコード されたデータの Web ブラウザへの送信を容易にするために ob_gzhandler() がサポートされています。 ob_gzhandler() は、ブラウザが受け入れる content encoding の型を調べ、それに基づいて出力を返します。
注意: PHP 4.3.2 未満では、この関数は渡された output_callback を実行することができなくても FALSE を返しませんでした。
web サーバによっては (例: Apache)、コールバック関数からコールされた際に、 スクリプトの実行ディレクトリを変更するものがあります。 コールバック関数の内部で chdir(dirname($_SERVER['SCRIPT_FILENAME'])) などと指定することで、これをもとに戻すことが可能です。
オプションのパラメータ chunk_size が渡された場合、 出力が chunk_size バイトを超えるたびに、 最初の改行の際にコールバック関数がコールされます。 output_callback パラメータに NULL 値を渡すと、 これをバイパスすることができます。
オプションのパラメータ erase に FALSE を指定すると、 スクリプトが終了するまでバッファは削除されません (PHP 4.3.0 以降で対応)。
出力バッファはスタッカブルであり、このため、他の ob_start() がアクティブの間に ob_start() をコールすることが可能です。この場合、 ob_end_flush() を適切な回数コールするようにしてください。 複数の出力コールバック関数がアクティブの場合、 ネストした順番で逐次連続的に出力がフィルタ処理されます。
ob_end_clean()、 ob_end_flush()、ob_clean()、 ob_flush() および ob_start() をコールバック関数の内部からコールすることはできません。 実際にコールした際の挙動は未定義です。バッファの内容を消去したい際には、 コールバック関数から "" (空文字列) を返してください。 同じく、print_r($expression, true) や highlight_file($filename, true) のような 出力バッファリング関数も、 コールバック関数の内部からコールすることはできません。
例 1644. ユーザ定義のコールバック関数の例
<?php
function callback($buffer)
{
// apples を全て oranges に置換する
return (ereg_replace("apples", "oranges", $buffer));
}
ob_start("callback");
?>
<html>
<body>
<p>It's like comparing apples to oranges.</p>
</body>
</html>
<?php
ob_end_flush();
?>
出力は次のようになります。
<html>
<body>
<p>It's like comparing oranges to oranges.</p>
</body>
</html>
ob_get_contents()、 ob_end_flush()、 ob_end_clean()、 ob_implicit_flush()、 ob_gzhandler()、ob_iconv_handler()、 mb_output_handler() および ob_tidyhandler() も参照ください。
ob_start
21-Aug-2007 05:17
28-Jul-2007 01:51
ob_start() seems not compliant with command-line PHP.
just calling the function ahead of your unix-php script and nothing
happens. The STDOUT stream is not buffered.
30-Mar-2007 06:01
Way to make all stdout and stderr write to a log
from *inside* a php script.
You simply need to make sure to call elog() every
once in awhile to get output.
It's a nice way to "daemonize" a script w.r.t. its logging.
// This allows us to capture all stdout and stderr (and error_log() calls)
// to this logfile...
// The "collected output" will be flushed anytime "elog()" is used...
ini_set("error_log", "/var/log/script.log");
ob_start();
function elog($str)
{
// get anything written to stdout or stderr that did *NOT* use elog()
// and write it now...
$writeme = ob_get_contents();
if ($writeme)
{
error_log($writeme);
ob_end_clean();
ob_start();
}
// now write message this method was called with
error_log($str);
}
16-Mar-2007 07:05
Hi, I use those functions for stripping unnecessary chars in my output code...because I have JavaScript placed in outpout code so I don't remove \n\r\t but just replace them with single space (it could cause errors in scripts)
Function stripBufferSkipTextareaTags skips tags Textarea. It's needed to don't loose \n\r when user edit some content...
sorry for my english ;)
<?php
function stripBufferSkipTextareaTags($buffer){
$poz_current = 0;
$poz_end = strlen($buffer)-1;
$result = "";
while ($poz_current < $poz_end){
$t_poz_start = stripos($buffer, "<textarea", $poz_current);
if ($t_poz_start === false){
$buffer_part_2strip = substr($buffer, $poz_current);
$temp = stripBuffer($buffer_part_2strip);
$result .= $temp;
$poz_current = $poz_end;
}
else{
$buffer_part_2strip = substr($buffer, $poz_current, $t_poz_start-$poz_current);
$temp = stripBuffer($buffer_part_2strip);
$result .= $temp;
$t_poz_end = stripos($buffer, "</textarea>", $t_poz_start);
$temp = substr($buffer, $t_poz_start, $t_poz_end-$t_poz_start);
$result .= $temp;
$poz_current = $t_poz_end;
}
}
return $result;
}
function stripBuffer($buffer){
// change new lines and tabs to single spaces
$buffer = str_replace(array("\r\n", "\r", "\n", "\t"), ' ', $buffer);
// multispaces to single...
$buffer = ereg_replace(" {2,}", ' ',$buffer);
// remove single spaces between tags
$buffer = str_replace("> <", "><", $buffer);
// remove single spaces around
$buffer = str_replace(" ", " ", $buffer);
$buffer = str_replace(" ", " ", $buffer);
return $buffer;
}
ob_start("stripBufferSkipTextareaTags");
?>
13-Mar-2007 11:55
This function's behaviour has been changed in php 5.2.0:
<?
global $AP;
$AP = new ap;
ob_start("ob_end");
function ob_end() {
global $AP;
$r = $AP->test();
return $r;
}
class ap {
function test() {
return "debug";
}
}
?>
In older versions it shows: "debug".
But latest php version causes error: PHP Fatal error: Call to a member function test() on a non-object.
And this is not a bug: http://bugs.php.net/bug.php?id=40104
26-Dec-2006 08:02
I use this to strip unnecessary characters from HTML output:
<?php
function sanitize_output($buffer)
{
$search = array(
'/\>[^\S ]+/s', //strip whitespaces after tags, except space
'/[^\S ]+\</s', //strip whitespaces before tags, except space
'/(\s)+/s' // shorten multiple whitespace sequences
);
$replace = array(
'>',
'<',
'\\1'
);
$buffer = preg_replace($search, $replace, $buffer);
return $buffer;
}
ob_start("sanitize_output");
?>
22-Nov-2006 02:20
Just for simplicity's sake (and because I had to rewrite it for use at http://www.VideoSift.com anyway), here's a very simplified, pre-PHP5 version. Just add one call to dump_css_cache() for each of your CSS files.
<?php
ob_start('ob_gzhandler');
header('Content-Type: text/css; charset: UTF-8');
header('Cache-Control: must-revalidate');
$expire_offset = 0; // set to a reaonable interval, say 3600 (1 hr)
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expire_offset) . ' GMT');
function css_compress($buffer) {
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);// remove comments
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' '), '', $buffer);// remove tabs, spaces, newlines, etc.
$buffer = str_replace('{ ', '{', $buffer);// remove unnecessary spaces.
$buffer = str_replace(' }', '}', $buffer);
$buffer = str_replace('; ', ';', $buffer);
$buffer = str_replace(', ', ',', $buffer);
$buffer = str_replace(' {', '{', $buffer);
$buffer = str_replace('} ', '}', $buffer);
$buffer = str_replace(': ', ':', $buffer);
$buffer = str_replace(' ,', ',', $buffer);
$buffer = str_replace(' ;', ';', $buffer);
return $buffer;
}
function dump_css_cache($filename) {
$cwd = getcwd() . DIRECTORY_SEPARATOR;
$stat = stat($filename);
$current_cache = $cwd . '.' . $filename . '.' . $stat['size'] . '-' . $stat['mtime'] . '.cache';
// the cache exists - just dump it
if (is_file($current_cache)) {
include($current_cache);
return;
}
// remove any old, lingering caches for this file
if ($dead_files = glob($cwd . '.' . $filename . '.*.cache', GLOB_NOESCAPE))
foreach ($dead_files as $dead_file)
unlink($dead_file);
if (!function_exists('file_put_contents')) {
function file_put_contents($filename, $contents) {
$handle = fopen($filename, 'w');
fwrite($handle, $contents);
fclose($handle);
}
}
$cache_contents = css_compress(file_get_contents($filename));
file_put_contents($current_cache, $cache_contents);
echo $cache_contents;
}
dump_css_cache('_general.css');
?>
22-Nov-2006 02:10
In extension to the compress() function posted below, here's a nifty little class that improves the idea a bit. Basically, running that compress() function for all your CSS for every single page load is clearly far less than optimal, especially since the styles will change only infrequently at the very worst.
With this class you can simply specify an array of your CSS file names and call dump_style(). The contents of each file are saved in compress()'d form in a cache file that is only recreated when the corresponding source CSS changes.
It's intended for PHP5, but will work identically if you just un-OOP everything and possibly define file_put_contents.
Enjoy!
<?php
$CSS_FILES = array(
'_general.css'
);
$css_cache = new CSSCache($CSS_FILES);
$css_cache->dump_style();
//
// class CSSCache
//
class CSSCache {
private $filenames = array();
private $cwd;
public function __construct($i_filename_arr) {
if (!is_array($i_filename_arr))
$i_filename_arr = array($i_filename_arr);
$this->filenames = $i_filename_arr;
$this->cwd = getcwd() . DIRECTORY_SEPARATOR;
if ($this->style_changed())
$expire = -72000;
else
$expire = 3200;
header('Content-Type: text/css; charset: UTF-8');
header('Cache-Control: must-revalidate');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expire) . ' GMT');
}
public function dump_style() {
ob_start('ob_gzhandler');
foreach ($this->filenames as $filename)
$this->dump_cache_contents($filename);
ob_end_flush();
}
private function get_cache_name($filename, $wildcard = FALSE) {
$stat = stat($filename);
return $this->cwd . '.' . $filename . '.' .
($wildcard ? '*' : ($stat['size'] . '-' . $stat['mtime'])) . '.cache';
}
private function style_changed() {
foreach ($this->filenames as $filename)
if (!is_file($this->get_cache_name($filename)))
return TRUE;
return FALSE;
}
private function compress($buffer) {
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' '), '', $buffer);
$buffer = str_replace('{ ', '{', $buffer);
$buffer = str_replace(' }', '}', $buffer);
$buffer = str_replace('; ', ';', $buffer);
$buffer = str_replace(', ', ',', $buffer);
$buffer = str_replace(' {', '{', $buffer);
$buffer = str_replace('} ', '}', $buffer);
$buffer = str_replace(': ', ':', $buffer);
$buffer = str_replace(' ,', ',', $buffer);
$buffer = str_replace(' ;', ';', $buffer);
return $buffer;
}
private function dump_cache_contents($filename) {
$current_cache = $this->get_cache_name($filename);
// the cache exists - just dump it
if (is_file($current_cache)) {
include($current_cache);
return;
}
// remove any old, lingering caches for this file
if ($dead_files = glob($this->get_cache_name($filename, TRUE), GLOB_NOESCAPE))
foreach ($dead_files as $dead_file)
unlink($dead_file);
$compressed = $this->compress(file_get_contents($filename));
file_put_contents($current_cache, $compressed);
echo $compressed;
}
}
?>
I'm sure some of you more brilliant minds could pare this down some more, but using the method found at fiftyfoureleven.com for compressing, I got my 10090-byte stylesheet down to 3536 bytes and then again down to 2713 bytes, by stripping unecessary characters from the stylesheet. 2 ob_start calls and the CSS file is now 73% smaller. YMMV.
<?php
ob_start("ob_gzhandler");
ob_start("compress");
header("Content-type: text/css; charset: UTF-8");
header("Cache-Control: must-revalidate");
$off = 0; # Set to a reaonable value later, say 3600 (1 hr);
$exp = "Expires: " . gmdate("D, d M Y H:i:s", time() + $off) . " GMT";
header($exp);
function compress($buffer) {
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer); // remove comments
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' ', ' ', ' '), '', $buffer); // remove tabs, spaces, newlines, etc.
$buffer = str_replace('{ ', '{', $buffer); // remove unnecessary spaces.
$buffer = str_replace(' }', '}', $buffer);
$buffer = str_replace('; ', ';', $buffer);
$buffer = str_replace(', ', ',', $buffer);
$buffer = str_replace(' {', '{', $buffer);
$buffer = str_replace('} ', '}', $buffer);
$buffer = str_replace(': ', ':', $buffer);
$buffer = str_replace(' ,', ',', $buffer);
$buffer = str_replace(' ;', ';', $buffer);
return $buffer;
}
require_once('screen.css');
require_once('layout.css');
require_once('custom.php');
require_once('titles.css');
require_once('bus.css');
?>
08-Nov-2006 08:35
PHP Suggests: Some web servers (e.g. Apache) change the working directory of a script when calling the callback function. You can change it back by doing, for example, the following in the callback function: chdir(dirname($_SERVER['SCRIPT_FILENAME']))
The solution provided by PHP, does not function as intended when running PHP as a CGI (on CGI mode (CGI-BUILD (--enable-cgi) and/or CLI)). In such a case, PHP is executed as a CGI-BIN and the web server daemon (e.g. Apache) sees SCRIPT_FILENAME as being the PHPCGI processor, and won't look deeper to find what file the PHPCGI processor is actually running/parsing; therefore the path returned by SCRIPT_FILENAME is wrong (most of the time, containing/ending with "cgi-system/php.cgi").
As SCRIPT_FILENAME is the safest way to proceed, but turns to be wrong in this exact situation; PATH_TRANSLATED is the next safe solution one would turn towards since it is populated with a different mechanism.
It would be correct to develop in the direction if the script filename itself is contained in the SCRIPT_FILENAME path value, then the SCRIPT_FILENAME content is reported correctly. If it is not, using PATH_TRANSLATED is the next logical choice we can use. The best reference in this case would be PHP_SELF as it is populated by PHP itself. Using SCRIPT_NAME as a reference would be an error as it is affected by the same problem (reports cgi-system and/or php.cgi as well).
The following is the revised code and should work on both the non-CGI and the CGI PHP processor types.
<?php
chdir(dirname((strstr($_SERVER["SCRIPT_FILENAME"], $_SERVER["PHP_SELF"])
? $_SERVER["SCRIPT_FILENAME"] : $_SERVER["PATH_TRANSLATED"])));
?>
Or the decomposed code as follows:
<?php
if (strstr($_SERVER["SCRIPT_FILENAME"], $_SERVER["PHP_SELF"])) {
$reference = $_SERVER["SCRIPT_FILENAME"];
} else {
$reference = $_SERVER["PATH_TRANSLATED"];
}
chdir(dirname($reference);
?>
This has been tested on Apache 1 & 2, PHP 4 & 5 and IIS 5.1
02-Oct-2006 04:04
simple code to make phpsession $_GET nice for Valid XHTML 1.0 Transitional :)
function callback($buffer)
{
$buffer = str_replace("&PHPSESSID", "&PHPSESSID", $buffer);
return $buffer;
}
ob_start("callback");
session_start();
30-May-2006 11:09
Hello firends
ob_start() opens a buffer in which all output is stored. So every time you do an echo, the output of that is added to the buffer. When the script finishes running, or you call ob_flush(), that stored output is sent to the browser (and gzipped first if you use ob_gzhandler, which means it downloads faster).
The most common reason to use ob_start is as a way to collect data that would otherwise be sent to the browser.
These are two usages of ob_start():
1-Well, you have more control over the output. Trivial example: say you want to show the user an error message, but the script has already sent some HTML to the browser. It'll look ugly, with a half-rendered page and then an error message. Using the output buffering functions, you can simply delete the buffer and sebuffer and send only the error message, which means it looks all nice and neat buffer and send
2-The reason output buffering was invented was to create a seamless transfer, from: php engine -> apache -> operating system -> web user
If you make sure each of those use the same buffer size, the system will use less writes, use less system resources and be able to handle more traffic.
With Regards, Hossein
01-Feb-2006 07:14
One of the notes below mentions that ob_end_flush() is called automatically at the end of the script if you called ob_start without an ob_end.
Because I couldn't find any other way to do it, I tried to use this fact to have some stuff run at the end of every script. It was a maintenance nightmare, so I'm putting a link here to the good way to do it, since it's nigh impossible to find with google.
http://php.net/register_shutdown_function
25-Jan-2006 01:51
Found that variables in class instances we're not being set after the call to ob_start().
Call ob_start after the variables are set however and it works but that didn't seem to solve the goal of a self contained templating class.
The fix was to assign the class by reference with '&new'
Here is a simplified working example:
<?php
class Buffer {
var $template = ' - template set in class constructor';
function Buffer() {
$this->startBuffer();
}
function startBuffer() {
ob_start(array(&$this, 'doFlush'));
}
function doFlush($buffer) {
/* simple string concat to show use of a
template string and the buffer output */
return $buffer . $this->template;
}
}
/* template does not get set:
$buffer1 = new Buffer();
$buffer1->template = ' - template set in instance';
echo 'some buffer content';
*/
/* this works as expected */
$buffer2 = &new Buffer();
$buffer2->template = ' - template set in instance';
echo 'some buffer content';
09-Jan-2006 02:57
When you rely on URL rewriting to pass the PHP session ID you should be careful with ob_get_contents(), as this might disable URL rewriting completely.
Example:
ob_start();
session_start();
echo '<a href=".">self link</a>';
$data = ob_get_contents();
ob_end_clean();
echo $data;
In the example above, URL rewriting will never occur. In fact, rewriting would occur if you ended the buffering envelope using ob_end_flush(). It seems to me that rewriting occurs in the very same buffering envelope where the session gets started, not at the final output stage.
If you need a scenario like the one above, using an "inner envelope" will help:
ob_start();
ob_start(); // add the inner buffering envelope
session_start();
echo '<a href=".">self link</a>';
ob_end_flush(); // closing the inner envelope will activate URL rewriting
$data = ob_get_contents();
ob_end_clean();
echo $data;
In case you're interested or believe like me that this is rather a design flaw instead of a feature, please visit bug #35933 (http://bugs.php.net/bug.php?id=35933) and comment on it.
17-Oct-2005 07:07
If you're trying to use ob_start() in some PHP5 classes (probably works on PHP4 classes), this is the good way :
<?
class HTMLPage
{
//----------------------------------------------------------------- PUBLIC
//----------------------------------------------------- M�thodes publiques
public static function ConvertIntoSGML( $source )
// Mode d'emploi :
//convertit une string en une SGML valide
//
// Renvoie :
//la chaine trait�e
//
// Algorithme :
//analyse char par char de la chaine. Si un caract�re est de nombre ASCII > 127,
//conversion en son code SGML.
{
$newString = '';
for( $i = 0; $i < strlen( $source ) ; $i++ ) {
$o = ord( $source{ $i } );
$newString .= ( ( $o > 127 ) ? '&#'.$o.';': $source{ $i } );
}
return $newString;
}
public function FlushSite( $source )
{
return $this->ConvertIntoSGML ( $source );
}
//-------------------------------------------- Constructeurs - destructeur
function __construct()
// Mode d'emploi (constructeur) :
//initialise la buffurisation
//
// Contrat :
//
{
ob_start( array ( & $this , 'FlushSite' ) );
} //---- Fin du constructeur
//------------------------------------------------------ M�thodes Magiques
//------------------------------------------------------------------ PRIVE
}
// Example :
$webdesign = new HTMLPage ( );
echo 'H�llo world'; // Will produce the source 'héllo world'
?>
Without the & before $this, you'll loose your content because ob_start() will call the flushsite() function from a clone of the object and not the caller object himself.
Note : call_back function must be public because ob_start() is in an extern scope from your class :)
I hope this will help you!
16-Sep-2005 02:29
The following should be added: "If outbut buffering is still active when the script ends, PHP outputs it automatically. In effect, every script ends with ob_end_flush()."
29-Aug-2005 12:50
If you want to run code in the middle of a string that you made, but you want to wait the printing...
(so if you want to allow php in bb-code style, and you want to execute it in order, and print everything in order...)
phpRun($code) {
ob_start();
exec($code);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
$str = str_replace("]\n", "]", $str);
$match = array('#\[php\](.*?)\[\/php\]#se');
$replace = array( phpRun( stripslashes('$1') ) );
$str= preg_replace($match, $replace, $str);
echo $str;
28-Aug-2005 11:29
Another way to make your code harder to copy, is to remove all line-breaks and tabs you have in it.
You can use this function to remove those.
You can choose to place "ob_start();" at the start of your main PHP-file, and "ob_end_clean();" at the end of it.
This is not the best solution though. If you are running Apache you might want to do something like this to a ".htaccess"-file:
php_value auto_prepend_file "/home/www/load_before.php"
php_value auto_append_file "/home/www/load_after.php"
(you should replace those files with files that actually exists).
In the "load_before.php" you can place the "ob_start();", and in the "load_after.php" you can do something like this:
<?
$html = strtr(ob_get_contents(), array("\t" => "", "\n" => "", "\r" => ""));
ob_end_clean();
echo $html;
?>
This will clean your HTML of all kind of linebreaks (both \n and \r) and tabulators (\t). This will save your users some bandwidth, but it will also make your HTML, JavaScripts and more very difficult to read or copy. That way, making it harder for people to steal your code if you do not want them to.
This isnt in the spirit of OpenSource, but anyway you should be aware that this is possible.
Be aware that if you use PHP-files, to simulate pictures, all linebreaks will also be remove, making them corrupt. A solution to this, could be to check the headers (Content-Type), and if isnt set, or it is "text/html", you can go ahead and remove the linebreaks and tabs.
At my site, with more than 50.000 pictures and about 60 people online, I couldnt see any difference in the loadtime.
But still be aware, that your output will not be sent, before the script is finished, which will make your page slower to load that way also, since it cannot send any output while loading, but will have to wait until the load is finished.
10-Aug-2005 04:05
When using a callback with ob_start(), functions like ob_get_contents() don't make use of it, use ob_end_flush() instead.
nb: not tested with every ob_* functions, just ob_get_contents() and ob_end_flush()
I usually create my pages in four parts - variable initialisation, import header (using the variables just declared to configure), main body (mostly non-PHP), import footer. I wondered about making the main body examinable by another PHP script if the main page was included into it. I found I could control output of the main body by ending the header with an unclosed function which finishes at the start of the footer, thus enclosing the main body. Output buffering can then be used to read this into a variable. As a demonstration of how this can be used to control the order of output look at this example:
<?php
$output = "";
// Callback to process buffered output
function capture($buffer)
{
$GLOBALS['output'] .= $buffer;
return "C ";
}
// Calls the printE() function with output capture
function captureE()
{
ob_start("capture");
printE();
ob_end_flush();
}
?>
A
<?php
// Output 'E' (the main body in the example scenario)
function printE()
{ // (End header after this line) ?>
E
<?php // (Start footer with this line)
}
?>
B
<?php captureE(); ?>
D
<?php print $output; ?>
F
<?php printE(); ?>
G
The output is A B C D E F E G.
For the application I mentioned above there are two points to note:
- The page when executed alone must output its main body but the inspection script should suppress this, perhaps by means of a variable set before the page is included and then checked for in the footer output lines.
- Because the main body is now inside a function it has a different namespace, thus changes may be required to prevent code breaking (e.g. use of globals, handling of functions defined within the main body).
08-Jul-2005 09:46
These are handy. First one has been mentioned before.
ob_start( array( 'lib_class', 'parse_output' ) );
ob_start( array( $this, 'parse_output' ) );
Note: $this is NOT a reference. Anything the callback saves or logs disappears in the clone ob_start works with.
It does enable the callback to work with the attributes of $this, like $this->ar_tpl_value or whatever your style is.
The manual says:
"If the optional parameter chunk_size is passed, the callback function is called on every first newline after chunk_size bytes of output. The output_callback parameter may be bypassed by passing a NULL value."
This doesn't work with my 4.3.11. Might be the Zend optimizer though. Daren't turn it off to go see.
25-May-2005 04:08
This function dynamically changes title of HTML page:
function change_title($new_title) {
$output = ob_get_contents();
ob_end_clean();
$output = preg_replace("/<title>(.*?)<\/title>/", "<title>$new_title</title>", $output);
echo $output;
}
Example:
ob_start();
// ... some output
change_title('NEW TITLE!');
24-May-2005 11:06
This code demonstrates the affect of providing a value to the chunk_size parameter. A value of 1 or 0 will be ignored by php. Here's the Code:
<?
function callback($buffer)
{
return "TRAPPED:".$buffer."<br/>";
}
ob_start("callback",2);
echo "long string, so callback";
echo "X";
echo " - no callback, less than 2 chars";
?>newlines,
but
no
callback
!<?
// PHP block
?>PHP block initiates callback.<?
echo "One more callback at EOF...";
?>
The code above outputs:
TRAPPED:long string, so callback
TRAPPED:X - no callback, less than 2 chars
TRAPPED:newlines, but no callback !
TRAPPED:PHP block initiates callback.
TRAPPED:One more callback at EOF...
TRAPPED:
10-May-2005 06:10
If you're trying to include a php file inside a loop by require_once (in example, a dinamic email template) and change the value of some variables (in example, url to unsuscribe, different for each user), you should use
<?php
// ... some code
$usermail = array("email1", "email2", ...);
for($i = 0; $i < $MAX; $i++)
{
$usermail_unsuscribe = $usermail[$i];
ob_start();
include("email_template.php");
ob_clean();
}
?>
Otherwise $usermail_unsuscribe will get only "email1" value.
09-May-2005 04:17
I don't claim to understand this--I would have expected the exact opposite--but it seems that
ob_start() ... ob_end_flush()
can massively improve perfomance, by at least a factor of 10 (admittedly a small number of samples).
I tried this after discovering that I could move a large (100ms) bottleneck in one of my scripts into
echo "<!-- about 40 characters of junk -->";
which clearly shouldn't have taken long to run.
My unfounded theory is that without buffering, the interaction between PHP4.3.4 and Apache is not optimized, whereas with buffering, PHP delivers the entire page at once, which Apache handles better.
I should add that this is under https.
02-Mar-2005 06:50
You can use PHP to generate a static HTML page. Useful if you have a complex script that, for performance reasons, you do not want site visitors to run repeatedly on demand. A "cron" job can execute the PHP script to create the HTML page. For example:
<?php // CREATE index.html
ob_start();
/* PERFORM COMLEX QUERY, ECHO RESULTS, ETC. */
$page = ob_get_contents();
ob_end_clean();
$cwd = getcwd();
$file = "$cwd" .'/'. "index.html";
@chmod($file,0755);
$fw = fopen($file, "w");
fputs($fw,$page, strlen($page));
fclose($fw);
die();
?>
14-Feb-2005 01:09
I use this function for deleting not needed characters within the html code before sending the whole stuff to the browser.
function callback($buffer){
$buffer = str_replace("\n", "", $buffer);
$buffer = str_replace("\t", "", $buffer);
$buffer = str_replace(chr(13), "", $buffer);
$buffer = ereg_replace("<!\-\- [\/\ a-zA-Z]* \-\->", "", $buffer);
return $buffer;
}
First str_replace will delete any newlines, second any tabs and the third any carriage return. Finally the regular expression will delete any html-comment which consists of /, space, a-z or A-Z.
Using this saves about 1kb on every pageload.
02-Feb-2005 11:59
I've noticed a bug with MSIE for non cached contents if your page is less than 4096 octets : you have to refresh the page each time to view its content !
Here is the solution to prevent this stupid behaviour of MSIE : just insert this code at the top of your scripts :
function ob_callback($buffer)
{
return $buffer . str_repeat(' ', max(0, 4097 - strlen($buffer)));
}
ob_start('ob_callback');
28-Dec-2004 11:39
nl2br() is not a callable function, to make it work define a new one like this:
function mynl2br($str){
return nl2br($str);
}
example:
ob_start("nl2br");
echo "under pressure\npushing down on me";
$str_error=ob_get_flush();
doesn't work, use ob_start("mynl2br") instead.
PHP 4.3.9
14-Nov-2004 10:19
My callback is stored in a function class, and using ob_start ('Class::callback') wasn't working. Not wanting to instantiate the class (no need, it's a function class) I tried this and it worked a charm:
ob_start (array (Class, 'callback'));
PHP 4.3.4
29-Oct-2004 05:49
I wanted to do things a very particular way with output buffering and shutdown functions; using register_shutdown_function instead of the built in callback feature of this function. However, one should note that this won't work, because the contents of the buffer are no longer in scope when PHP is calling the shutdown functions. This would have been easy to see EXCEPT that PHP graciously flushes any unsent buffers at the end of the script, or when calling exit. So:
<?php
ob_start();
echo 'hi';
exit;
?>
Prints "hi". In a nutshell, if you want it to have a shutdown function that handles an output buffer, just specify it in ob_start() and let PHP automatically call it at the end of the script.
18-Mar-2004 12:20
If ob_start does not seem to be working for you, note that with Apache 2 the flush() function causes PHP to send headers regardless of whether ob_start had been called before flush.
ob_start();
echo 'test';
flush();
will cause Apache 2 to send whatever headers may be stacked up - which means you can't use a header(location:xxx) after the flush. To fix, remove the flush(). Spent several hours discovering this. Apache 1.x didn't work this way.
11-Mar-2004 02:10
If you're using object-orientated code in PHP you may, like me, want to use a call-back function that is inside an object (i.e. a class function). In this case you send ob_start a two-element array as its single argument. The first element is the name of the object (without the $ at the start), and the second is the function to call. So to use a function 'indent' in an object called '$template' you would use <?php ob_start(array('template', 'indent')); ?>.
24-Nov-2003 02:03
Note that the current working directory changes in the callback procedure (Changed from htdocs\ to windows\system32\ on my system).
You have to use absolut paths if you want to open files on your local system.
22-Nov-2003 10:18
Output Buffering even works in nested scopes or might be applied in recursive structures... thought this might save someone a little time guessing and testing :)
<pre><?php
ob_start(); // start output buffer 1
echo "a"; // fill ob1
ob_start(); // start output buffer 2
echo "b"; // fill ob2
$s1 = ob_get_contents(); // read ob2 ("b")
ob_end_flush(); // flush ob2 to ob1
echo "c"; // continue filling ob1
$s2 = ob_get_contents(); // read ob1 ("a" . "b" . "c")
ob_end_flush(); // flush ob1 to browser
// echoes "b" followed by "abc", as supposed to:
echo "<HR>$s1<HR>$s2<HR>";
?></pre>
... at least works on Apache 1.3.28
Nandor =)
18-Jan-2003 02:58
IE 55. sp2 and IE6 as on the date of adding this note have problems with content type gzip and caching http headers. The pages are never cached. I think this combination of http headers can also crash the browser.
see http://support.microsoft.com/default.aspx?scid=kb;en-us;321722
If you're using Apache (1.3x or 2.0), you might consider adding automatic compression capability to your delivered pages.
I assume you all know how to build compression classes and use them in your programs, but none has yet to offer the speed and robustness of a binary-compiled module. Furthermore, such modules also log the "compressable" hit in the web log file, thus allowing your favorite web anaysing program to show you reports of bandwidth saved.
Having said that, you might consider the following two modules for Apache:
1) Apache 1.3x: use mod_gzip, available from:
http://sourceforge.net/projects/mod-gzip/
2) Apache 2.x: use mod_gz, see here:
http://www.mail-archive.com/dev@httpd.apache.org/msg00734.html
3) Apache 1.3x: you may also want to use mod_defalte, from:
ftp://ftp.lexa.ru/pub/apache-rus/contrib/
Hope it helps.
08-Aug-2002 07:36
good article on output buffering on devshed:
http://www.devshed.com/c/a/PHP/Output-Buffering-With-PHP