ob_gzhandler
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
ob_gzhandler — 出力バッファを gzip 圧縮するための ob_start コールバック関数
説明
ob_gzhandler() は ob_start()
用のコールバック関数として使用されることを意図したもので、
圧縮されたページをサポートしている web ブラウザに対して
gz エンコードされたデータを送信することを容易にします。
ob_gzhandler() は
実際に圧縮されたデータを送信する前にブラウザがサポートする content
encoding の種類("gzip"、"deflate" またはなし)を調べ、それに基づいて
出力を返します。すべてのブラウザがサポートされています。
というのも、ブラウザは、
自分が圧縮されたページをサポートするかどうかを表す
適切なヘッダを送信することになっているからです。
圧縮されたページをブラウザがサポートしていない場合、
この関数は false
を返します。
パラメータ
data
-
flags
-
戻り値
例
例1 ob_gzhandler() の例
<?php
ob_start("ob_gzhandler");
?>
<html>
<body>
<p>このページは圧縮されます。</p>
</body>
</html>
注意
注意:
ob_gzhandler() は、zlib 拡張モジュールが必要です。
注意:
ob_gzhandler() と zlib.output_compression の両方を使用することはできません。 また、 zlib.output_compression を使用すると、それは ob_gzhandler() よりも優先されることに注意してください。
User Contributed Notes 29 notes
I've just spent 5 hours fighting a bug in my application and outcome is:
<?php
// do not use
ob_start("ob_gzhandler");
// along with
header('HTTP/1.1 304 Not Modified');
// or in the end use
ob_end_clean();
?>
W3C Standart requires response body to be empty if 304 header set. With compression on it will at least contain a gzip stream header even if your output is completely empty!
This affects firefox (current ver.3.6.3) in a very subtle way: one of the requests after the one that gets 304 with not empty body gets it response prepended with contents of that body. In my case it was a css file and styles was not rendered at all, which made problem show up.
The simplest way for gzip compression is:
<?php
if(!ob_start("ob_gzhandler")) ob_start();
?>
ob_start("ob_gzhandler") returns FALSE if browser doesn't support gzip, so then is called normal ob_start();
About the previous note from Davey:
ob_start(array('ob_gzhandler',9));
Does not work. The output size isn?t affected at all, stays the same.
ob_gzhandler compression level use zlib.output_compression_level, which is -1 per default, level 6.
To change the compression level on the fly, simply use ini_set:
<?php
ini_set('zlib.output_compression_level', 1);
ob_start('ob_gzhandler');
echo 'This is compressed at level 1';
?>
Please be aware that, as other users have already mentioned, the compression will fail if there are characters before the php start tag "< ?".
This is also the case when saving files in UTF-8 format with editors such as Ultraedit. Make sure you save the file using the defined option UTF-8 NO BOM or delete the BOM otherwise the two UTF BOM characters will be written at the start of the file.
I just spent 30 minutes wondering why my browser wasn't getting the gzipped version :-O
If you're using Google Web Access your pages will be delivered to the browser uncompressed unless you tell GWA to ignore the specific site.
Obvious, really.
All versions of MSIE have a bug where they don't cache gzipd contents. If your application depends on caching, this is something to keep in mind. In the end, I did:
<?php
// These are so benificial, they default to true.
if (!isset($use_page_cache))
$use_page_cache = 1;
if (!isset($use_compression))
$use_compression = 1;
// Add browsers here as we must detect them. Opera is an oddball, if we don't detect
// it specifically, it will turn up as MSIE
$browser="other";
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$agent = $_SERVER['HTTP_USER_AGENT'];
if (eregi("opera",$agent)){
$browser="opera";
}elseif(eregi("msie",$agent)){
$browser="msie";
}
}
if ($use_compression && !( $use_page_cache && $browser == "msie")) {
// Turn on compression, makes quite a difference in bandwith usage.
// However, MSIE (all versions) have a bug with compression and caching. So for MSIE
// it's either compression or caching. We choose caching.
ob_start('ob_gzhandler');
}
session_cache_limiter("must-revalidate");
session_start();
// ... put stuff in $content ...
if ($use_page_cache) {
// MD5 is slow, however with a fast server (PIII or better) we should be OK
$hash = md5($content);
$headers = getallheaders();
if (isset($headers['If-None-Match']) && ereg($hash, $headers['If-None-Match']))
{
header('HTTP/1.1 304 Not Modified');
exit;
}
header("ETag: \"$hash\"");
}
print $content;
?>
In the set_error_handler notes, there is a technique described for capturing all errors (even parse errors) before they are displayed the user, using a special error handler and output handler. If this output handler detects a fatal error in the output buffer, it's captured and dealt with before it can be displayed to the user. If no error was detected, then output buffer is displayed verbatim (i.e. without being compressed).
If you are using this method, you can still take advantage of ob_gzhandler's compression. However, you MUST specify a mode argument (I'm using 4.2.2 on RedHat9). The mode value affects which headers are automatically added (Content-Encoding, etc). A value of '5' worked for me. '0' or discarding the argument produces a blank screen under Mozilla.
<?php
function my_output_handler(&$buffer) {
// Detect errors in the output
if(ereg("(error</b>:)(.+) in <b>(.+)</b> on line <b>(.+)</b>", $buffer, $regs)) {
my_error_handler(E_ERROR, $regs[2], $regs[3], $regs[4]);
// ...
// ... Insert your error handling here ...
// ...
return 'An internal error occurred.';
} else {
// The page rendered without any errors, so compress
// and output.
return ob_gzhandler($buffer, 5);
}
}
?>
if you call ob_end_clean() after ob_start("ob_gzhandler"), the "Content-Encoding: gzip" header will still get sent (assuming the browser supports the encoding). if you don't call ob_start() again with the ob_gzhandler callback function, the output will not be compressed, but the header will say it is. this causes mozilla (as of build 2002032808) to display a blank page.
Someone previously mentioned that MSIE doesn't cache gzipped content. This is false. He misread the source of information. In fact, IE will cache gzipped content no matter what. Here is the quote from the mailing list:
The reason the COMPRESSED responses are, in fact,
always getting cached no matter what "Vary:" field name
is present is just as I suspected... it is because MSIE
decides it MUST cache responses that arrive with
"Content-Encoding: gzip" because it MUST have a
disk ( cache ) file to work with in order to do the
decompression.
Source: http://lists.over.net/pipermail/mod_gzip/2002-December/006826.html
Hi,
if you are using apache, I think you are much better of using apache's compression, as this will also compress static text files (css, js, html, etc) and it will keep your php-code clean.
See http://httpd.apache.org/docs/2.0/mod/mod_deflate.html for an excellent how to.
And for other webservers, I'm pretty sure they have equivalents.
Cheers, Ronald
So why doesn't the web server just do this by default?
This works for me if I do:
ini_set('zlib.output_compression_level', 3); ob_start("ob_gzhandler");
or even just:
ob_start("ob_gzhandler");
I did the level 3 compression, I think the default was 6 and I didn't want to put too much load on the server. For a 895k file (my largest) the compression levels were:
1 = 189k
3 = 178k
4 = 163k
6 = 156k (I believe 6 is the default if you leave out the ini_set)
9 = 155k
I use http://www.whatsmyip.org/mod_gzip_test/ to check the sizes.
FYI: This works with dynamic files in Movable Type 3.x (I'm testing it in 3.2) I've got the above command in the first line of my mtview.php file.
More info here: http://www.whatsmyip.org/forum/viewtopic.php?t=43
On several sites I've read the some browsers don't like compressed CSS.
Problem revelate by xn@bnw.com
cause real problem when you have a global call
to ob_gzhandler , for example in an include configuration
file , and you want send a non gzip content , for any reason ...
To resolve it ,
put this after your global call to ob_gzhandler
include("conf_that_call_ob_gzhandler.php");
-->ob_end_clean();
-->header("Content-Encoding: none");
If you try to overload the header before the the ob_gzhandler call
or during the buffering output , it will not work .
( probably erase at the call of ob_gzhandler and not allow during
buffering ).
Hope this help
I have a very well tested module in my framework (ie: running in production environments for a couple of years) called 'fastpage', which takes care of both memcaching and gzipping for frequently requested content. It memcaches one version of each unique page both gzipped and non-gzipped, and returns the appropriate version based upon the browser's Accept-encoding header.
Use is:
$page_spec = array($dependent,$variables,$go,$here);
if(!fastpage_display('content_id',$page_spec)) {
... make page here ...
fastpage_displayed('content_id',$page_spec);
}
Unfortunately, on IE8 beta (in IE8 mode, or in IE7 emulation mode), the fastpage-enabled content was not being un-gzipped. IE would request the content, a 200 status and correct byte count would appear in the server logfile, and IE would show no errors. However, CSS would not be applied and Javascript would not execute.
The only fix I can find right now is to disable gzip entirely for this browser. User agent string is: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727
The same output works fine in firefox (windows, linux, mac), and in safari.
Versions are: zlib 1.2.3-r1 / php 5.2.6-r7 / lighttpd 1.4.20
So much for the 'this has been fixed in IE7' comment, above...
If you want to suppress errors (such as for cases when headers are already sent) and you don't want to write or patch your error_handler, you need to create an intermediate callback:
function ob_gz_handler_no_errors($buffer)
{
@ob_gzhandler($buffer);
}
ob_start('ob_gzhandler_no_errors');
Here are some precisions :
- the "mode" arg accepts a bit field composed of PHP_OUTPUT_HANDLER_START, PHP_OUTPUT_HANDLER_CONT and PHP_OUTPUT_HANDLER_END. See http://www.php.net/manual/fr/ref.zlib.php#56216 for an example. The value that jazfresh recommends below (5) is the good one, because 5 == PHP_OUTPUT_HANDLER_START | PHP_OUTPUT_HANDLER_END.
- when called INSIDE an output buffering handler, ob_gzhandler DOES NOT return false when the browser doesn't support compressed pages. It rather returns the original string.
When writing scripts for distribution, I would usually "null" out the following deprecated superglobals so that users who uses the script will not be able to use them.
<?php
$HTTP_GET_VARS = null;
$HTTP_POST_VARS = null;
$HTTP_COOKIE_VARS = null;
$HTTP_POST_FILES = null;
$HTTP_SERVER_VARS = null;
?>
However, when using ob_start('ob_gzhandler'), one of those superglobals somehow disable this function.
Found out that it was $HTTP_SERVER_VARS that's causing the problems.
<?php
ob_start('ob_gzhandler');
$HTTP_GET_VARS = null;
$HTTP_POST_VARS = null;
$HTTP_COOKIE_VARS = null;
$HTTP_POST_FILES = null;
/**
* Causing the trouble
*/
//$HTTP_SERVER_VARS = null;
?>
I don't know why it does that, but I just want to point that out.
According to the manual, if ob_gzhander detects that the browser is unable to support deflate or gzip it returns FALSE. Does this mean that if you use ob_start("ob_gzhandler"), any browsers that do not support gzip/deflate will receive a blank page?
I've been having problems with IE6 displaying gzipped pages and adding a test:
<?php
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler");
else ob_start();
?>
solved the problem. Surely if ob_gzhandler detects a lack of support for deflation it should return the input unchanged and not FALSE?
if you want to send an output to the browser (which accepts gzip), and you haven't set the buffering callback ob_start("ob_gzhandler"), you may use the gzencode() function.
header("Content-Encoding: gzip");
echo gzencode("some output", 9, FORCE_GZIP);