ASPit - Totally ASP JSit - Totally JavaScript
Search PHPit

Use this textbox to search for articles on PHPit. Seperate keywords with a space.

Advertisements

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:

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

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.

One Response to “Using an output callback and ob_gzhandler together”

  1. Arnold Daniels Says:

    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.

Leave a Reply