php关于ob_start('ob_gzhandler')启用GZIP压缩的bug
则ob_clean()后面的输出将不显示,这是个bug,
可以用ob_end_clean();ob_start("ob_gzhandler"); 代替ob_clean();
否则后面输出内容将是空。
<?php
error_reporting(E_ALL);
ob_start("ob_gzhandler");
echo "content";
ob_clean();
echo "more content";
?>
上面的代码期望输出more content实际上什么内容也不会输出。
下面就正常了
<?php
error_reporting(E_ALL);
ob_start("ob_gzhandler");
echo "content";
ob_end_clean();
ob_start("ob_gzhandler");
echo "more content";
?>
下面自定义一个回调函数再测试
<?php
function my_ob_gzhandler($buffer,$mod){
header("Content-Encoding: gzip");
return gzencode($buffer, 9, FORCE_GZIP);
}
error_reporting(E_ALL);
ob_start("my_ob_gzhandler");
echo "content";
ob_clean();
echo "more content";
?>
上面是正常的,但使用ob_end_clean代替ob_clean后又会导致后面的输出不会显示。
因此即使是下面的代码依然会在使用ob_clean或者ob_end_clean后会导致输出为空。
<?php
if (ini_get('zlib.output_compression')) {
if (ini_get('zlib.output_compression_level') != 9) {
ini_set('zlib.output_compression_level', '9');
}
ob_start();
} else {
if (strstr($_SERVER['HTTP_ACCEPT_ENCODING'], "gzip")) {
ob_start("ob_gzhandler");
} else {
ob_start();
}
}
?>
最稳定的启用页面压缩的方法应该类似下面
<?php
if(extension_loaded('zlib')) {
ini_set('zlib.output_compression', 'On');
ini_set('zlib.output_compression_level', '3');
}
?>
但如果一定要使用ob_gzhandler来启用页面压缩就要注意本文的第一句话了。
事实上,下面的代码只是浏览器不显示
error_reporting(E_ALL);
ob_start("ob_gzhandler");
echo "content";
ob_clean();
echo "more content";
但如果测试一下
telnet localhost 80
GET /test.php HTTP/1.0
<Enter>
<Enter>
将会返回如下信息
HTTP/1.1 200 OK
Date: Fri, 20 Feb 2009 15:40:17 GMT
Server: Apache/2.2.6 (Win32) PHP/5.2.5
X-Powered-By: PHP/5.2.5
Vary: Accept-Encoding
Content-Length: 12
Connection: close
Content-Type: text/html
more content
失去了跟主机的连接。
可以看出more content已经输出
但为何浏览器不显示呢?
下面引用http://bugs.php.net/bug.php?id=34071
Can't reproduce. Please try with PHP CLI and tell what SAPI you're using.
It works with PHP CLI. SAPI: cgi-fcgi
Still can't reproduce, even with FCGI. Does this work for you: <?php function foo($data) { return $data; } ob_start('foo'); echo 'Should NOT be shown'; ob_clean(); echo 'Should be shown'; ?> ?
Yes, that works. But as soon as I use ob_gzhandler, instead of foo, nothing is displayed. I run PHP4 and PHP5 at the same time (port 80 is PHP4.3.4, port 83 is PHP5Dev). Could it be that the bug is caused by my PHP4 installation?
Are you sure there is nothing shown in the "View Source" tab? I'm almost sure your browser just hides binary output from you. So try this: telnet localhost 80 GET /script.php HTTP/1.0 <Enter> <Enter>
You're right, that works, and I get the correct result.
That's not a very good test since ob_gzhandler checks the request's accept-encoding and since your telnet test didn't specify that you could accept gzip or deflate, then the gzhandler simply didn't do anything.
Not PHP problem then.
Rasmus is right. The telnet thing works because ob_gzhandler doesn't do anything. As soon as a specify a Content-Encoding: gzip header, nothing gets displayed again. So it's not my browser that's causing the problem.
Also, I disabled PHP4 in my http.conf file, and it still doesn't work properly in PHP5Dev. So I still believe there's a bug in PHP5.
Then provide more info about it; look into the apache logs; try on another server and with another PHP version etc etc. As I've already said, it works perfectly here.
I found someone else who has the same problem as me: http://www.sitepoint.com/forums/showpost.php?p=2093119&postcount=5 However, I can't find any specific information. I looked through my Apache error log, and the only thing I found was: [Wed Aug 10 18:59:56 2005] [error] [client 127.0.0.1] request failed: erroneous characters after protocol string: \\xff\\xfb\\x1f\\xff\\xfb \\xff\\xfb\\x18\\xff\\xfb'\\xff\\xfd\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03G ET /gzip2.php HTTP/1.0 [Wed Aug 10 19:00:21 2005] [error] [client 127.0.0.1] request failed: erroneous characters after protocol string: \\xff\\xfb\\x1f\\xff\\xfb \\xff\\xfb\\x18\\xff\\xfb'\\xff\\xfd\\x01\\xff\\xfb\\x03\\xff\\xfd\\x03G ET /gzip2.php HTTP/1.0 But I'm not sure if that's entirely relevant. But there's nothing more that's related to it.
1) Check your php.ini settings (how they differ to whatever you used as base for your php.ini, -dist or -recommended) 2) Set error_reporting to E_ALL and try again..
I've reproduced the bug. I've been wondering why my script wouldn't run until I saw this bug. I'm using apache server on win xp with php 4 as a module. The above code produced the bug: <?php ob_start('ob_gzhandler'); echo 'Should NOT be shown'; ob_clean(); echo 'Should be shown'; ?> With further debuging this doesn't work either: <?php function handler($buffer,$mode){ $buffer=ob_gzhandler($buffer,$mode); return $buffer; } ob_start('handler'); echo 'Should NOT be shown'; ob_clean(); echo 'Should be shown'; ?> I noticed that the content-length in my headers was non-zero so I tried this and lo and behold: <?php function handler($buffer,$mode){ $buffer=ob_gzhandler($buffer,$mode); header('Content-Encoding:'); header('Content-Type: text/plain'); header('Vary:'); return bin2hex($buffer); } ob_start('handler'); echo 'Should NOT be shown'; ob_clean(); echo 'Should be shown'; ?> I realized the bug is sending bad gz binary but for some reason the following code did work...: <?php function handler($buffer,$mode){ $buffer=ob_gzhandler($buffer,$mode); header('Content-Encoding:'); header('Content-Type: text/plain'); header('Vary:'); return gzuncompress($buffer); } ob_start('handler'); echo 'Should NOT be shown'; ob_clean(); echo 'Should be shown'; ?> To my surprise, this didn't, thus confusing me: <?php function handler($buffer,$mode){ $buffer=ob_gzhandler($buffer,$mode); header('Content-Encoding:'); header('Content-Type: text/plain'); header('Vary:'); return 'test'.gzuncompress($buffer); } ob_start('handler'); echo 'Should NOT be shown'; ob_clean(); echo 'Should be shown'; ?> I'll be looking for the source on cvs and report back to ya!
No feedback was provided for this bug for over a week, so it is being suspended automatically. If you are able to provide the information that was originally requested, please do so and change the status of the bug back to "Open".
this appears to be ob_gzhandler's fault. it seems to send the content-encoding: gzip header even if ob_end_clean or similar is called which produces no output. that means that if you use ob_gzhandler and then change your mind about compression and need to kill that buffer, you're out of luck. in the following small example, "output" WILL be printed in plaintext but will be misinterpreted by the browser as gzipped content. <?php ob_start("ob_gzhandler"); echo("trashed"); ob_end_clean(); echo("output"); ?> you can use telnet to better view the situation because it will almost always result in a white browser window. as others have pointed out, you have to use the accept-encoding to trigger ob_gzhandler's compression: $ telnet localhost 80 GET /ob_test.php HTTP/1.1 Host: localhost Accept-Encoding: gzip,deflate now, should ob_gzhandler really be sending the header if the buffer is ended without outputting anything? should it maybe check the length of the buffer when it is closed to determine whether or not