文字

ob_start

(PHP 4, PHP 5)

ob_start打开输出控制缓冲

说明

bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。

内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。 想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。

Warning

当有正在调用的回调函数时,一些网络服务器(例如Apache)会改变一个脚本的工作目录。 你可以在回调函数中再把它改回来,例如 chdir(dirname($_SERVER['SCRIPT_FILENAME']))

输出缓冲区是可堆叠的,这即意谓着,当有一个 ob_start() 是活跃的时, 你可以调用另一个 ob_start() 。 只要确保又正确调用了 ob_end_flush() 恰当的次数即可。 如果有多重输出回调函数是活跃的,输出内容会一直按嵌套的顺序依次通过它们而被过滤。

参数

output_callback

可选参数 output_callback 函数可以被指定。 此函数把一个字符串当作参数并返回一个字符串。 当输出缓冲区被( ob_flush() , ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器的时候该函数将会被调用。 当调用 output_callback 时,它将收到输出缓冲区的内容作为参数 并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器。 如果这个 output_callback 不是一个可以调用的函数,此函数 会返回 FALSE

如果回调函数有两个参数,第二个参数会由一个位域补充,该位域由 PHP_OUTPUT_HANDLER_START , PHP_OUTPUT_HANDLER_CONT PHP_OUTPUT_HANDLER_END 组成。

如果 output_callback 返回 FALSE ,其原来的输入 内容被直接送到浏览器。

这个参数 output_callback 可以通过直接给一个 NULL 值而避开。

ob_end_clean() , ob_end_flush() , ob_clean() , ob_flush() and ob_start() 不能从一个回调函数中调用。 如果从回调函数中调用了它们,产生的行为是不明确的。 如果想要删除缓冲区的内容,从回调函数中返回一个"" (空字符串)。 更不能从一个回调函数中使用像print_r($expression, true)highlight_file($filename, true) 一样的输出缓冲函数。

Note:

在PHP 4.0.4中, ob_gzhandler() 被引入是为了简化把gz编码过 数据发送到支持压缩网页的浏览器。 ob_gzhandler() 会判定浏览器可以接受哪种类型的编码内容,并返回相应 的输出。

chunk_size

如果可选参数 chunk_size 被赋值了,在任何一个能引起缓冲区的长度等于 或超过 chunk_size 的输出操作后,缓冲区都会被刷送。 默认值 0 意味着函数仅在最后被调用,其余的特殊值可以将 chunk_size 从 1 设定到 4096。

erase

如果可选参数 erase 被赋成 FALSE ,直到脚本执行完成缓冲区才被删除。 这使得,如果调用了冲刷和清洗(清除)函数,会抛出一个“notice”,并返回 FALSE 值。

返回值

成功时返回 TRUE , 或者在失败时返回 FALSE

更新日志

版本 说明
4.3.2 在传递的 output_callback 不能被执行时,此函数 被改成返回 FALSE
4.2.0 添加了 erase 参数。

范例

Example #1 用户自定义回调函数的例子

<?php

function  callback ( $buffer )
{
  
// replace all the apples with oranges
  
return ( str_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_clean() - 清空(擦除)缓冲区并关闭输出缓冲
  • ob_end_flush() - 冲刷出(送出)输出缓冲区内容并关闭缓冲
  • ob_implicit_flush() - 打开/关闭绝对刷送
  • ob_gzhandler() - 在ob_start中使用的用来压缩输出缓冲区中内容的回调函数。ob_start callback function to gzip output buffer
  • ob_iconv_handler() - 以输出缓冲处理程序转换字符编码
  • mb_output_handler() - 在输出缓冲中转换字符编码的回调函数
  • ob_tidyhandler() - ob_start callback function to repair the buffer

用户评论:

[#1] bty-adminf2 at trebly dot net [2015-07-28 17:27:03]

This concerns the default values used for ob_start().

Note that I could verify that ob_start() and ob_start(null,0) are not always equivalent.

It seems that with ob_start() the output buffer is open with current default parameters of your configuration if they have been explicitly defined.

So, if you have set $chunk_size to any value previously and send data larger than $chunk_size the data will be automatically flushed by blocks of $chunk_size.

If you explicitely define $chunk_size=0, later, when you will use any function as $my_ob_dataoutput=ob_get_clean(); you  will get back the whole content of your output (quite unlimited with $chunk_size=0).

I discover this because my var $my_ob_dataoutput was truncated. Using "ob_get_status (true)" function, I could verify that an error (in a lower level or by default out of my control) was setting previously the $chunk_size of ob at 4096.
I changed "ob_start()" for "ob_start(null,0)" and everything became OK.

[#2] brad at wpsites dot net [2014-09-14 12:06:09]

When creating a shortcode i've seen code which uses the callback function however its not always needed as this example demonstrates:
<?php
function wpsites_comment_form_shortcode() {
    
ob_start();
    
comment_form();
    
$cform ob_get_contents();
    
ob_end_clean();
    return 
$cform;
 }
?>

[#3] David Spector [2013-10-03 20:23:12]

Function ob_start can capture all normal output, assuming no errors occur. Naturally, the programmer will want to know whether errors can also be caught (that is, captured and logged). The answer is that warnings can be caught along with normal output, but that fatal errors and exceptions break the normal buffering and can only be caught separately. Finally, parsing errors cannot be caught at all, because parsing is done in a first pass before execution begins. Of course, even parsing errors can be caught by running PHP in a separate process and capturing the output of that process.

Here is an example that illustrates all these additional details (see also the comment by Philip at set_error_handler function). Those errors that break buffering can be illustrated by uncommenting their lines.

<?php
ob_start
(); // Start output buffering
function exception_handler($exc)
    {
    
// Catch thrown exceptions break output buffering
    
exit("Uncaught exception: ".$exc->getMessage());
    } 
// exception_handler
set_exception_handler('exception_handler');

function 
shutdown_function()
    {
    
// Catch fatal errors that break output buffering
    
$error=error_get_last();
    if (
$error["type"]==E_ERROR)
        echo 
"Uncaught fatal error: ".$error["message"];
    } 
// shutdown_function
register_shutdown_function("shutdown_function");

// Demonstrate normal output
echo 'This is normal output, caught okay';

// Demonstrate a warning
$DivisionByZeroWarningCaughtOkay=3/0;

// Demonstrate a fatal error
//UndefinedFunctionFatalErrorCanBeCaughtSeparately();

// Demonstrate a parsing error
//$x=FatalParse@ErrorCannotBeCapturedAtAll

// Complete capturing normal output
$Out=ob_get_contents();
ob_end_clean();

// Demonstrate a thrown exception
//throw new Exception('Exception Can Be Caught Separately');

echo "All captured output:<p>".$Out;
exit(
0);
?>

[#4] jhlavon [2013-06-19 12:47:07]

When used in constructor part of class it have to be prefixed by "self::" or by classname, else PHP fails to create buffer.

function __construct ()
{

$bo = ob_start ("self::callback_ob") ;
...
}

[#5] me at haravikk dot com [2012-06-19 19:26:41]

I think it's worth noting that while you can't call any output functions such as echo or print from within a callback function, you can still send headers (presumably including cookies, haven't checked). Of course this only works in the first callback, like so:

<?php

function myCallback($buffer$flags) {
    if (
$flags PHP_OUTPUT_HANDLER_START) {
        
header('Server: LastMinuteHeaderServer');
    }
}

ob_start('myCallback');
echo 
"Hello World!";
ob_end_flush();

?>


Not the most inspiring example, but in this case the code is able to sneak a last-minute header in before the headers part of the response is sent. This can be handy if you want to avoid replacing header values that are uncertain.

For example if your code may return an image, but you don't want to set content type until you're sure that the image can be sent successfully, you can use the callback to leave the decision right until the very last moment, by which point you're hopefully sure of what's being sent in the HTTP body.

[#6] mchojrin at gmail dot com [2012-05-30 14:23:45]

Just a word of warning to those like myself who are upgrading from 5.3. I have a piece of code that used to work:

<?php
if ( !ob_start( !DEBUGMODE 'ob_gzhandler' '' ) ) {
    
ob_start();
}
?>


Which is not working anymore (I get an error like: Warning: ob_start(): function '' not found or invalid function name).

It's easily fixed though, just changed the '' to a null, like this:

<?php
if ( !ob_start( !DEBUGMODE 'ob_gzhandler' null ) ) {
    
ob_start();
}        
?>


Which preserves the code intention but works :)

[#7] Filip Dalge [2012-03-22 12:24:41]

When a fatal error is thrown, PHP will output the current buffer of Output-Control without postprocessing before printing the error message. If you are working with several output control levels, this might not result in the desired behavior.

You can use an output callback handler to handle this and discard the output.

Therefore, use ob_start("ob_error_handler") in connection with the following:

function ob_error_handler($str) {
$error = error_get_last();
if ($error && $error["type"] == E_USER_ERROR || $error["type"] == E_ERROR) {
return ini_get("error_prepend_string").
  "\nFatal error: $error[message] in $error[file] on line $error[line]\n".
  ini_get("error_append_string");
}
return $str;
}

[#8] loksly at gmail dot com [2011-10-24 05:30:04]

It's easy to add a function that caches the whole page you're going to generate so resources are saved. I wrote it in a file called "cacheable.php", and I included in each file that I want to be cached. It cannot manage files with ob_start inside.

Here is the code:
file "cacheable.php":
----------------------------------
<?php
    
global $cachefile;

    
$cachefilename urlencode($_SERVER['REQUEST_URI'])."-".md5($_SERVER['REQUEST_URI']);

    
$cachefile sys_get_temp_dir ().DIRECTORY_SEPARATOR.$cachefilename.'.cache';

    
// How long to keep cache file?
    
$cachetime 60*5;    //5min

    // Is cache file still fresh? If so, serve it.    
    
if (file_exists($cachefile) && time() - $cachetime filemtime($cachefile)) {
      
readfile($cachefile);
      exit;
    }

    function 
cacheOutputtoFile()
    {
        global 
$cachefile;
        
$webpage ob_get_contents();
        if (
$fp fopen($cachefile'w+'))
        {
            if (
flock($fpLOCK_EX))
            { 
// do an exclusive lock
                
ftruncate($fp0); // truncate file
                //compress unnecesary spaces
                
$busca = array('/\>[^\S ]+/s','/[^\S ]+\</s','/(\s)+/s'); 
                
$reemplaza = array('>','<','\\1'); 
                
$webpage preg_replace($busca$reemplaza$webpage);
                
                
fwrite($fp$webpage);
                
fclose($fp);
            }
        }
        echo 
$webpage;
    }

    
ob_start();
    
register_shutdown_function("cacheOutputtoFile");

//note you must use register_shutdown_function instead of using "cacheOutputtoFile" as parameter of ob_start as socket is already closed when callback function is called, then the client not get the echo $webpage output.
?>


----------------------------------
usage:

<?php
require_once(realpath(dirname(__FILE__).DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR."cacheable.php");
?>

----------------------------------

[#9] Anonymous [2011-02-08 07:32:09]

In case you're in for some readable representation for flags, this is a variant as a private class member:

<?php
class foo {
    private function 
getFlagsReadable($flags) {
        
$flagNames = array('PHP_OUTPUT_HANDLER_START''PHP_OUTPUT_HANDLER_CONT''PHP_OUTPUT_HANDLER_END');
        
$readable '';
        foreach(
$flagNames as $flagName)
            if (
$flags constant($flagName) )
                
$readable .= (strlen($readable) ? ' | ' '') . $flagName
            
;        
        return 
$readable;
    }
}
?>

[#10] Chris [2010-11-24 16:35:51]

Careful with while using functions that change headers of a page; that change will not be undone when ending output buffering.

If you for instance have a class that generates an image and sets the appropriate headers, they will still be in place after the end of ob.

For instance:
<?php
  ob_start
();
  
myClass::renderPng(); //header("Content-Type: image/png"); in here
  
$pngString ob_get_contents();
  
ob_end_clean();
?>


will put the image bytes into $pngString, and set the content type to image/png. Though the image will not be sent to the client, the png header is still in place; if you do html output here, the browser will most likely display "image error, cannot be viewed", at least firefox does.

You need to set the correct image type (text/html) manually in this case.

[#11] Bitwise [2009-10-21 12:41:55]

There is no start flag problem. One just has to notice that the second parameter is not a mode but consists of bitwise-OR'ed flags.

<?php
function ob_handler($string$flags) {
        static 
$input = array();
        if ( 
$flags PHP_OUTPUT_HANDLER_START )
                
$flags_sent[] = "PHP_OUTPUT_HANDLER_START";
        if ( 
$flags PHP_OUTPUT_HANDLER_CONT )
                
$flags_sent[] = "PHP_OUTPUT_HANDLER_CONT";
        if ( 
$flags PHP_OUTPUT_HANDLER_END )
                
$flags_sent[] = "PHP_OUTPUT_HANDLER_END";
        
$input[] = implode(' | '$flags_sent) . " ($flags): $string<br />";
        
$output  "$string<br />";
        if ( 
$flags PHP_OUTPUT_HANDLER_END ) {
                
$output .= '<br />';
                foreach(
$input as $k => $v$output .= "$k$v";
        }
        return 
$output;
}

ob_start('ob_handler');

echo 
'flush';
ob_flush();

echo 
'flush 2';
ob_flush();

echo 
'clean';
ob_clean();

echo 
'flush 3';
ob_flush();

echo 
'end flush';
ob_end_flush();
?>


flush
flush 2
flush 3
end flush

0: PHP_OUTPUT_HANDLER_START | PHP_OUTPUT_HANDLER_CONT (3): flush
1: PHP_OUTPUT_HANDLER_CONT (2): flush 2
2: PHP_OUTPUT_HANDLER_CONT (2): clean
3: PHP_OUTPUT_HANDLER_CONT (2): flush 3
4: PHP_OUTPUT_HANDLER_END (4): end flush

[#12] clancy hood at gmail dot com [2009-09-19 17:31:33]

With ob callback: note that the second parameter sent to your method won't help you differentiate between flush calls and calls to ob_clean, but the buffer contents is sent in both cases, so you end up parsing data that isn't going to be used. Also, note that the constant PHP_OUTPUT_HANDLER_START is never actually sent, rather the integer "3" turns up on first flush:
<?php

    
function ob_handler($string$flag){
        static 
$input = array();
        
$done false;
        switch(
$flag){
            case 
PHP_OUTPUT_HANDLER_START:    
                
$flag_sent "PHP_OUTPUT_HANDLER_START ($flag)";
                break;  
            case 
PHP_OUTPUT_HANDLER_CONT:
                
$flag_sent "PHP_OUTPUT_HANDLER_CONT ($flag)";
                break;
            case 
PHP_OUTPUT_HANDLER_END:
                
$done true
                
$flag_sent "PHP_OUTPUT_HANDLER_END ($flag)";
                break; 
            default:
                
$flag_sent "Flag is not a constant ($flag)";
        }
        
$input[] = "$flag_sent$string<br />";
        
$output "$string<br />"
        if(!
$done) return $output;
        
// print_r($input, 1) causes an error and var_export just doesn't work
        
$output .= '<br />';
        foreach(
$input as $k=>$v$output .= "$k$v";
        return 
$output;
    }

    
ob_start('ob_handler');

    echo 
'flush';
    
ob_flush();

    echo 
'flush 2';
    
ob_flush();

    echo 
'clean';
    
ob_clean();

    echo 
'flush 3';
    
ob_flush();

    echo 
'end flush';
    
ob_end_flush();
?>

?
flush
flush 2
flush 3
end flush

0: Flag is not a constant (3): flush
1: PHP_OUTPUT_HANDLER_CONT (2): flush 2
2: PHP_OUTPUT_HANDLER_CONT (2): clean
3: PHP_OUTPUT_HANDLER_CONT (2): flush 3
4: PHP_OUTPUT_HANDLER_END (4): end flush

I suppose the START flag problem *may* be a bug but I'm not able to upgrade before reporting since I must have the same version as my server (I'm on PHP 5.2.6). If anyone has 5.2.11 or other stable version feel free to test/report as you see fit.

[#13] mariusads at helpedia dot com [2009-07-02 15:02:53]

Make sure the editor you use does not add the UTF8/UTF16 BOM at the start of the scripts if you want to use ob_start("ob_gzhandler"); 

If those three characters are present, browsers like Firefox won't be able to decode the pages and will report:

Content Encoding Error

The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression.

The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression.

Google Chrome will simply report "Error 2 (net::ERR_FAILED): Unknown error."

With the ob_start command commented out, the page is successfully load and the browser will usually detect the BOM and not show it on the page, so everything's hard to debug.

[#14] robin [2009-04-17 06:53:09]

When using brame buffer from time to time, you can see this warning
Warning: session_start() [function.session-start]: Cannot send session cache limiter - headers already sent
One of the many reasons may be in bad encoding of document. My experience, the document had header of utf-8 encoding but in fact it was win1252. After reconding to the utf-8 warning has disapeard.

[#15] cj at ceejayoz dot com [2008-12-29 08:45:36]

Note that since PHP 5.1.x, all objects have their destructors called before the output buffer callback function executes. Thus, globalised objects will not be available as expected in the function.

This is stated to be intended behaviour, per http://bugs.php.net/bug.php?id=40104

[#16] codextasy at gmail dot com [2008-09-19 01:26:42]

There is a difference between the documentation and real callback functions invocation.
Manual says: "The function will be called when ob_end_flush() is called, or when the output buffer is flushed to the browser at the end of the request."

Actually, the callback function, once set by ob_start(), will be called regardless.
Here are the functions that invoke callback function immediately:
ob_clean
ob_end_clean
ob_end_flush
ob_flush
ob_get_clean

BUT only two of them return the result returned by the callback (ob_end_flush, ob_flush), other functions discard it.

At the end of the request, even if none of the functions listed above is called, the callback will be called anyway, and its result will be returned to the browser (well, at least this is corresponding to the manual).

There is one more trick:
If you set callback function with chunk_size > 1, callback function will be called each time output buffer is equal or exceeds chunk_size and its result will be output to the browser, even if you call any of ob_clean(), ob_end_clean(), ob_get_clean() later, so be aware of this fact.

[#17] fordiman at gmail dot com [2008-06-27 09:35:43]

Here's a nifty function I use daily.  Essentially: include a PHP file - but render its output to a variable, rather than to the buffer.  It's also set up to load the script with a variable set, and automagically loads globals into the script's namespace, making it an effective templating scheme.  It also has error handling, so that you're not flying blind when using output buffering.

<?php
$GLOBALS['BufferedErrors']=Array();
function errorParse($errno, $errstr, $errfile, $errline, $errcontext) {
$errorTypes = Array(
E_ERROR => 'Fatal Error',
E_WARNING => 'Warning',
E_PARSE => 'Parse Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Fatal Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compilation Error',
E_COMPILE_WARNING => 'Compilation Warning',
E_USER_ERROR => 'Triggered Error',
E_USER_WARNING => 'Triggered Warning',
E_USER_NOTICE => 'Triggered Notice',
E_STRICT => 'Deprecation Notice',
E_RECOVERABLE_ERROR => 'Catchable Fatal Error'
);
$ret=(object)Array(
'number'=>$errno,
'message'=>$errstr,
'file'=>$errfile,
'line'=>$errline,
'context'=>$errcontext,
'type'=>$errorTypes[$errno]
);
$GLOBALS['BufferedErrors'][]=$ret;
return false;
}
function parse($fileToInclude, $argumentsToFile=false) {
$bufferedErrorStack = $GLOBALS['BufferedErrors'];
set_error_handler('errorParse', error_reporting());
$GLOBALS['BufferedErrors']=Array();

if (!file_exists($fileToInclude))
return '';
if ($argumentsToFile === false)
$argumentsToFile = Array();
$argumentsToFile = array_merge($GLOBALS, $argumentsToFile);
foreach ($argumentsToFile as $variableName => $variableValue)
$$variableName = $variableValue;
ob_start();
include($fileToInclude);
$ret = ob_get_contents();
ob_end_clean();

restore_error_handler();
$errors = $GLOBALS['BufferedErrors'];
$GLOBALS['BufferedErrors'] = $bufferedErrorStack;
if (count($errors)>0) {
$ret.='<ul class="error">';
foreach ($errors as $error)
$ret.= 
'<li>'.
'<b>'.$error->type.'</b>: '.
$error->message.
'<blockquote>'.
'<i>file</i>: '.$error->file.'<br />'.
'<i>line</i>: '.$error->line.
'</blockquote>'.
'</li>';
$ret.='</ul>';
}
return $ret;
}

[#18] coldshine at gmail dot com [2008-04-07 11:52:39]

Referring to dan at roteloftet dot com's comment:

RFC 2616 (HTTP) specifies a "transparent" Content-Encoding, "identity" (?? 3.5), that nicely suits what you tried to do with the (invalid) "None". So this equally working, and it's also RFC-compliant:

<?php
header
('Content-Encoding: identity'true);
?>

[#19] dan at roteloftet dot com [2008-03-09 02:19:23]

Some web hosting servers (mine do, at least) have in their php.ini the following setting:
output_handler = ob_gzhandler

This proved problematic for php-scripts which returns an image or a binary file in general, since there is no way to determine the content length of the compressed file.

Since I spent a lot of time scouring the net searching for a work-around (.htaccess-modifications were out of the picture for various reasons), I found this to work nicely to cancel out the ob_gzhandler specified in the php.ini:

<?php
while (ob_get_level())
        
ob_end_clean();
header("Content-Encoding: None"true);
?>


Put this at the top of the script before anything else is written to the page, and the script result will not be compressed.

[#20] dale3h [2008-01-10 07:06:52]

Here's a simple way, using register_shutdown_function, to easily maintain your global output filter:

File: outputfilter.inc.php
<?php
  initOutputFilter
();

  function 
initOutputFilter() {
    
ob_start('cbOutputFilter');
    
register_shutdown_function('ob_end_flush');
  }

  function 
cbOutputFilter($output) {
    
$search  = array(
      
'user@domain.com',
      
'user2@domain.com',
    );
    
$replace = array(
      
'<img src="/images/nospam001.jpg" border="0" alt="" />',
      
'<img src="/images/nospam002.jpg" border="0" alt="" />',
    );

    return 
str_replace($search$replace$output);
  }
?>


Just include it in your global file, or in each file you want to filter.

This one in particular replaces any occurrence of "user@domain.com" or "user2@domain.com" with an image (that contains each email respectively), preventing spam bots from picking it up.

[#21] Eamon Straughn eamon at gizzle dot co dot uk [2007-12-27 11:06:30]

In regards to below. The best thing to do is create an error handler that catches all non-fatal errors prior and during your ob_start.

works for me all the time. and if there is an error you can format the errors in that script it executed in. From my experience i'm quite sure that would be the most logical choice. wouldn't it? Plus your using objects...Please do think before you can code.

Always plan else you'll be working blind. Anyways ob_start wouldn't exit;

Just plan ahead and anticipate. It's just like driving a car.. Happy new year.

[#22] Anonymous [2007-10-22 06:40:50]

As a follow up to my previous post :
(output seems to go to standard ouptput in command  line mode even with the use of output buffering (ob_start) )

Setting implicit_flush to false seems to do the trick :

   ini_set('implicit_flush',false); // (avoids output even with    ob_start, in command line mode)
   ob_start();
   include (realpath(dirname(__FILE__))."/".$template);
   $ret_str.=ob_get_contents();
   ob_end_clean();
   ini_set('implicit_flush',true);

(See :
Chapter 43. Using PHP from the command line
)

[#23] Francois Hill [2007-10-22 05:42:12]

Following clement dot ayme at st dot com 's remark :

In my experience it seems that the output IS buffered, but ALSO sent to the standard output !

[#24] Charlie Farrow [2007-10-18 09:49:59]

Under certain freak conditions, when an error ocours perfoming an action on an object that cannot be done (either because the object does not exist or the method does not exist) inside of an ob_start() the script will exit and print everything the current function generates before the error, but nothing else, including no error message.

I am at a loss to why no error message appears and am trying to get a working example for the developers that is simpler than my whole program!

So if you are using ob_start() and you get no output, check your objects.... you have made a mistake on them somewhere. The only trouble is you will not know where as there is no error!!

[#25] Asher Haig (ahaig at ridiculouspower dot com) [2007-08-20 13:17:45]

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();
}

[#26] tracey AT archive DOT org [2007-03-29 13:01:58]

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);
}

[#27] joebezucha at tlen dot pl [2007-03-16 02:05:16]

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 &nbsp;
    
$buffer str_replace(" &nbsp;""&nbsp;"$buffer);
    
$buffer str_replace("&nbsp; ""&nbsp;"$buffer);
    return 
$buffer;
}

ob_start("stripBufferSkipTextareaTags");

?>

[#28] denis at SPAM_WELCOME dot i39 dot ru [2007-03-13 06:55:32]

This function's behaviour  has been changed in php 5.2.0:

<?php
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

[#29] Tobias Goldkamp [2006-12-25 15:02:55]

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");

?>

[#30] lucky760 at yahoo dot com [2006-11-21 09:20:36]

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');

?>

[#31] lucky760 at yahoo dot com [2006-11-21 09:10:21]

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($filenameTRUE), 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;
  }
}

?>

[#32] [2006-11-10 10:34:25]

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');

?>

[#33] feedback at realitymedias dot com [2006-11-07 15:35:34]

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

[#34] butch at enterpol dot pl [2006-10-02 00:04:50]

simple code to make phpsession $_GET nice for Valid XHTML 1.0 Transitional :)

function callback($buffer)
{
  $buffer = str_replace("&PHPSESSID", "&amp;PHPSESSID", $buffer);
  return $buffer;
}

ob_start("callback");

session_start();

[#35] net_navard at yahoo dot com [2006-05-30 07:09:12]

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

[#36] simon [2006-01-24 08:51:34]

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) {

return $buffer . $this->template; 
}
}


$buffer2 = &new Buffer();
$buffer2->template = ' - template set in instance';
echo 'some buffer content';

[#37] ernest at vogelsinger dot at [2006-01-08 09:57:42]

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.

[#38] cyrille.berliat[no spam]free.fr [2005-10-16 15:07:22]

If you're trying to use ob_start() in some PHP5 classes (probably works on PHP4 classes), this is the good way :

<?php

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&#233;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!

[#39] php at bucksvsbytes dot com [2005-09-15 22:29:06]

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()."

[#40] admin at bobfrank dot org [2005-08-28 08:50:39]

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;

[#41] geoffrey at nevra dot net [2005-08-09 12:05:36]

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()

[#42] [2005-08-05 07:54:27]

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).

[#43] oersoep at gmail dot com [2005-07-08 05:46:49]

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.

[#44] Aleksey [2005-05-25 00:08:10]

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!');

[#45] jds1509 at NOSPAMrit dot edu [2005-05-23 19:06:03]

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:

<?php

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

// PHP block

?>
PHP block initiates callback. <?php

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:

[#46] rafa dot chacon at factorydea dot com [2005-05-10 02:10:42]

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.

[#47] JM [2005-05-08 12:17:00]

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.

[#48] Ray Paseur (Paseur ... ImagineDB.com) [2005-03-01 13:50:03]

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();

   
$page ob_get_contents();
   
ob_end_clean();
   
$cwd getcwd();
   
$file "$cwd.'/'"index.html";
   @
chmod($file,0755);
   
$fw fopen($file"w");
   
fputs($fw,$pagestrlen($page));
   
fclose($fw);
   die();
?>

[#49] eddie [2005-02-13 08:09:20]

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.

[#50] FB [2005-02-02 06:59:43]

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');

[#51] aaron at offtone.com [2004-11-13 17:19:11]

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

[#52] dev at kiwicore dot org [2004-10-28 13:49:58]

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.

[#53] jkloss at hotmail dot com [2004-03-17 07:20:05]

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.

[#54] mjr [2004-03-10 09:10:32]

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')); ?> .

[#55] ed.oohay (a) suamhcs_rodnan [2003-11-21 17:18:00]

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 =)

[#56] venky_athome at yahoo dot com [2003-01-17 09:58:57]

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

[#57] [2002-12-05 12:02:55]

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.

[#58] cliff at NOSPAMtravelguides dot com [2002-08-07 15:36:05]

good article on output buffering on devshed:

http://www.devshed.com/c/a/PHP/Output-Buffering-With-PHP

上一篇: 下一篇: