Using an output callback and ob_gzhandler together
If you've ever tried to use an output callback and ob_gzhandler together, you might've run into problems. Just try it yourself with the below code snippet:
function mycallback($buffer) {
$buffer = str_replace ('Dennis Pallett', 'John Doe', $buffer);
return $buffer;
}
ob_start('mycallback');
ob_start('ob_gzhandler');
echo 'My name is Dennis Pallett';
?>
The above code should replace 'Dennis Pallett' with 'Joe Doe', except it doesn't because of the ob_gzhandler. Remove ob_gzhandler, and it will replace the names.
To fix this, you must decode the buffer in your callback, then make your changes, and encode the buffer again (this last step is extremely important, or you'll get garbage). The only problem though is that PHP doesn't have a gzdecode function, only a gzencode function. Thankfully, this is extremely easy to write:
if (!function_exists('gzdecode')) {
function gzdecode ($data) {
// Check if data is GZIP'ed
if (strlen($data) < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
return false;
}
// Remove first 10 bytes
$data = substr($data, 10);
// Return regular data
return gzinflate($data);
}
}
All this function does is first check whether the data is actually gzip encoded, then removes the first 10 bytes, and finally uses the gzinflate function to do the actual decoding. I've used this function many times myself, and I've never had any problems with it.
The callback code now looks like this:
// Define the gzdecode function
if (!function_exists('gzdecode')) {
function gzdecode ($data) {
// Check if data is GZIP'ed
if (strlen($data) < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) {
return false;
}
// Remove first 10 bytes
$data = substr($data, 10);
// Return regular data
return gzinflate($data);
}
}
function mycallback($buffer) {
// GZipped buffer?
$gzbuffer = gzdecode($buffer);
if ($gzbuffer !== false) {
$buffer = $gzbuffer;
}
$buffer = str_replace ('Dennis Pallett', 'John Doe', $buffer);
// Return normal or GZipped buffer
return ($gzbuffer !== false) ? gzencode($buffer) : $buffer;
}
ob_start('mycallback');
ob_start('ob_gzhandler');
echo 'My name is Dennis Pallett';
?>
If you now run the above code it will correctly replace the names, with and without ob_gzhandler enabled. I've used the above code in many of my scripts (most notable my PHP components), and it's worked perfectly.
April 11th, 2006 at 1:26 pm
Yes you might do that, though this is not a logical thing to do. What actualy happends is:
1.) The outputbuffer ‘My name is Dennis Pallett’ is zipped by the ob_gzhandler.
2.) The zipped buffer is send down to the next callback ‘mycallback’
3.) Function ‘mycallback’ unzipps the buffer
4.) Function ‘mycallback’ replaces ‘Dennis Pallett’
5.) Function ‘mycallback’ rezipps the buffer
The callbacks are called as ‘last in, first out’. You could simply change the order of the callbacks to get the expected result.