PHP Conference Japan 2024

ob_gzhandler

(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)

ob_gzhandlerob_start 回调函数,用于压缩输出缓冲区

描述

ob_gzhandler(字符串 $data, 整数 $flags): 字符串|false

ob_gzhandler() 旨在用作 ob_start() 的回调函数,以帮助方便地将 gz 编码的数据发送到支持压缩网页的 Web 浏览器。在 ob_gzhandler() 实际发送压缩数据之前,它会确定浏览器将接受哪种类型的内容编码(“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_compressionob_gzhandler() 更佳。

参见

  • ob_start() - 打开输出缓冲
  • ob_end_flush() - 刷新(发送)活动输出处理程序的返回值并关闭活动输出缓冲区

添加备注

用户贡献的备注 29 条备注

Jer
14 年前
我刚刚花了 5 个小时来解决应用程序中的一个错误,结果是

<?php
// 不要使用
ob_start("ob_gzhandler");
// 以及
header('HTTP/1.1 304 Not Modified');

// 或最终使用
ob_end_clean();
?>

W3C 标准要求如果设置了 304 标头,则响应主体为空。启用压缩后,即使输出完全为空,它至少也会包含 gzip 流标头!

这以非常细微的方式影响 Firefox(当前版本 3.6.3):304 带非空主体请求之后的其中一个请求会将其响应前缀添加到该主体的内容中。在我的例子中,它是一个 css 文件,并且样式根本没有呈现,这导致问题出现。
przemekryciuk at gmail dot com
15 年前
gzip 压缩最简单的方法是
<?php
if(!ob_start("ob_gzhandler")) ob_start();
?>
如果浏览器不支持 gzip,则 ob_start("ob_gzhandler") 返回 FALSE,然后调用正常的 ob_start();
daijoubu at videotron dot ca
21 年前
关于 Davey 的上一条备注

ob_start(array('ob_gzhandler',9));

不起作用。输出大小根本不受影响,保持不变。

ob_gzhandler 压缩级别使用 zlib.output_compression_level,默认为 -1,级别 6。

要在运行时更改压缩级别,只需使用 ini_set
<?php
ini_set
('zlib.output_compression_level', 1);
ob_start('ob_gzhandler');
echo
'This is compressed at level 1';
?>
mariusads[at]helpedia[dot]com
18 年前
请注意,正如其他用户已经提到的那样,如果在 php 开始标签“< ?”之前有字符,则压缩将失败。

使用 Ultraedit 等编辑器以 UTF-8 格式保存文件时,也会出现这种情况。确保使用定义的选项 UTF-8 NO BOM 保存文件,否则将写入两个 UTF BOM 字符到文件的开头。
Tony Piper
19 年前
我刚刚花了 30 分钟想知道为什么我的浏览器没有获得 gzip 版本 :-O

如果您使用的是 Google Web Access,除非您告诉 GWA 忽略特定站点,否则您的页面将以未压缩的方式传递到浏览器。

真的很明显。
m at rtij dot nl
19 年前
所有版本的 MSIE 都存在一个错误,它们不会缓存 gzipd 内容。如果您的应用程序依赖于缓存,则需要注意这一点。最后,我做了

<?php

// 这些变量非常有用,默认值为真。
if (!isset($use_page_cache))
$use_page_cache = 1;
if (!isset(
$use_compression))
$use_compression = 1;

// 需要在此处添加浏览器,以便检测它们。Opera 是个例外,如果我们不专门检测它,它会被识别为 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")) {
// 启用压缩,显著减少带宽使用。
// 但是,MSIE(所有版本)在压缩和缓存方面存在一个 bug。因此,对于 MSIE
// 只能选择压缩或缓存。我们选择缓存。
ob_start('ob_gzhandler');
}

session_cache_limiter("must-revalidate");

session_start();

// ... 将内容放入 $content 中 ...

if ($use_page_cache) {
// MD5 速度较慢,但在速度较快的服务器(PIII 或更高)上应该可以正常工作
$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;

?>
jazfresh at SPAM-JAVELIN dot hotmail dot com
21 年前
在 `set_error_handler` 的说明中,描述了一种技术,可以在错误显示给用户之前捕获所有错误(甚至解析错误),方法是使用特殊的错误处理程序和输出处理程序。如果此输出处理程序检测到输出缓冲区中的致命错误,则会在其显示给用户之前捕获并处理该错误。如果未检测到错误,则按字面显示输出缓冲区(即,不进行压缩)。

如果使用此方法,仍然可以利用 `ob_gzhandler` 的压缩功能。但是,必须指定模式参数(我在 RedHat9 上使用 4.2.2)。模式值会影响自动添加的标头(Content-Encoding 等)。值为“5”对我有用。“0”或丢弃参数会在 Mozilla 下导致空白屏幕。

<?php

function my_output_handler(&$buffer) {
// 检测输出中的错误
if(ereg("(error</b>:)(.+) in <b>(.+)</b> on line <b>(.+)</b>", $buffer, $regs)) {
my_error_handler(E_ERROR, $regs[2], $regs[3], $regs[4]);
// ...
// ... 在此处插入错误处理 ...
// ...
return 'An internal error occurred.';
} else {
// 页面渲染没有任何错误,因此压缩
// 并输出。
return ob_gzhandler($buffer, 5);
}
}
?>
xn at bnw dot com
22 年前
如果在 `ob_start("ob_gzhandler")` 之后调用 `ob_end_clean()`,则仍然会发送 "Content-Encoding: gzip" 标头(假设浏览器支持编码)。如果不再次使用 `ob_gzhandler` 回调函数调用 `ob_start()`,则输出不会被压缩,但标头会说明已压缩。这会导致 Mozilla(截至 2002032808 版本)显示空白页面。
maratd at gmail dot com
17 年前
之前有人提到 MSIE 不会缓存 gzip 压缩的内容。这是错误的。他误读了信息来源。事实上,无论如何,IE 都会缓存 gzip 压缩的内容。以下是邮件列表中的引用

压缩响应实际上
总是被缓存,无论是否存在 "Vary:" 字段名
正如我所怀疑的那样……这是因为 MSIE
决定它必须缓存带有
"Content-Encoding: gzip" 的响应,因为它必须拥有一个
磁盘(缓存)文件才能进行
解压缩。

来源:http://lists.over.net/pipermail/mod_gzip/2002-December/006826.html
ronald at hidden dot com
16 年前
您好,

如果您使用的是 Apache,我认为最好使用 Apache 的压缩功能,因为它还可以压缩静态文本文件(css、js、html 等),并且可以保持您的 php 代码简洁。

请参阅 https://httpd.apache.org/docs/2.0/mod/mod_deflate.html 获取优秀的教程。

对于其他 Web 服务器,我相信它们也有类似的功能。

此致,Ronald
lapoint2 at msu dot edu
19 年前
那么,为什么 Web 服务器不默认执行此操作呢?
如果我执行以下操作,则此方法对我有用
ini_set('zlib.output_compression_level', 3); ob_start("ob_gzhandler");
甚至只是
ob_start("ob_gzhandler");

我使用了级别 3 压缩,我认为默认级别是 6,并且不想给服务器带来过大的负载。对于 895k 的文件(我的最大文件),压缩级别如下:
1 = 189k
3 = 178k
4 = 163k
6 = 156k(我相信如果省略 ini_set,则默认值为 6)
9 = 155k
我使用 http://www.whatsmyip.org/mod_gzip_test/ 来检查大小。

仅供参考:这适用于 Movable Type 3.x 中的动态文件(我在 3.2 中对其进行了测试),我在 mtview.php 文件的第一行中使用了上述命令。

更多信息:http://www.whatsmyip.org/forum/viewtopic.php?t=43

我在多个站点上阅读到一些浏览器不喜欢压缩的 CSS。
psychones at ifrance dot com
22 年前
[email protected] 发现的问题

当您进行全局调用时,会导致实际问题
例如,在包含配置文件中调用 `ob_gzhandler`
并且您出于任何原因想要发送非 gzip 内容……

解决方法:

在全局调用 `ob_gzhandler` 之后放置此代码

include("conf_that_call_ob_gzhandler.php");
-->ob_end_clean();
-->header("Content-Encoding: none");

如果尝试在 `ob_gzhandler` 调用之前或在
缓冲输出期间重载标头,则无效。
(可能在 `ob_gzhandler` 调用时被删除,并且在
缓冲期间不允许)。

希望对您有所帮助
walter
15 年前
在我的框架中,我有一个经过充分测试的模块(例如:在生产环境中运行了几年)名为“fastpage”,它负责处理频繁请求内容的 memcache 和 gzip 压缩。它为每个唯一页面的 gzip 压缩版本和未压缩版本都存储 memcache,并根据浏览器的 Accept-encoding 标头返回相应的版本。

用法:
$page_spec = array($dependent,$variables,$go,$here);
if(!fastpage_display('content_id',$page_spec)) {
... 在此处创建页面 ...
fastpage_displayed('content_id',$page_spec);
}

不幸的是,在 IE8 beta(在 IE8 模式或 IE7 模拟模式下),启用了 fastpage 的内容未被解压缩。IE 将请求内容,服务器日志文件中将显示 200 状态和正确的字节数,并且 IE 将不会显示任何错误。但是,CSS 不会应用,并且 Javascript 不会执行。

我现在能找到的唯一解决方法是完全禁用此浏览器的 gzip 压缩。用户代理字符串为:Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727

相同的输出在 Firefox(Windows、Linux、Mac)和 Safari 中可以正常工作。

版本为:zlib 1.2.3-r1 / php 5.2.6-r7 / lighttpd 1.4.20

上述“这已在 IE7 中修复”的评论不准确……
jsnell at e-normous dot com
17 年前
如果要抑制错误(例如,当标头已发送时),并且不想编写或修补错误处理程序,则需要创建一个中间回调

function ob_gz_handler_no_errors($buffer)
{
@ob_gzhandler($buffer);
}

ob_start('ob_gzhandler_no_errors');
nicolas dot grekas+php at gmail dot com
17 年前
以下是一些说明

- “mode” 参数接受由 PHP_OUTPUT_HANDLER_START、PHP_OUTPUT_HANDLER_CONT 和 PHP_OUTPUT_HANDLER_END 组成的位字段。请参阅 https://php.net/manual/fr/ref.zlib.php#56216 获取示例。jazfresh 下面推荐的值(5)是正确的,因为 5 == PHP_OUTPUT_HANDLER_START | PHP_OUTPUT_HANDLER_END。

- 当在输出缓冲处理程序内部调用时,如果浏览器不支持压缩页面,`ob_gzhandler` 不会返回 false。它会返回原始字符串。
spam_this at zleelz dot com
18 年前
在编写用于分发的脚本时,我通常会将以下已弃用的超级全局变量设为“null”,以便使用该脚本的用户无法使用它们。
<?php
$HTTP_GET_VARS
= null;
$HTTP_POST_VARS = null;
$HTTP_COOKIE_VARS = null;
$HTTP_POST_FILES = null;
$HTTP_SERVER_VARS = null;
?>

然而,当使用ob_start('ob_gzhandler')时,其中一个超级全局变量会以某种方式禁用此函数。

发现是$HTTP_SERVER_VARS导致了问题。

<?php
ob_start
('ob_gzhandler');

$HTTP_GET_VARS = null;
$HTTP_POST_VARS = null;
$HTTP_COOKIE_VARS = null;
$HTTP_POST_FILES = null;
/**
* 导致问题的变量
*/
//$HTTP_SERVER_VARS = null;
?>

我不知道为什么会出现这种情况,但我只想指出这一点。
richard at mf2fm dot com
18 年前
根据手册,如果ob_gzhander检测到浏览器无法支持deflate或gzip,它将返回FALSE。这意味着如果使用ob_start("ob_gzhandler"),任何不支持gzip/deflate的浏览器都会收到空白页面吗?

我一直遇到IE6显示gzip压缩页面时的问题,并添加了一个测试

<?php

if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler");

else
ob_start();

?>

解决了这个问题。如果ob_gzhandler检测到缺乏对压缩的支持,它应该返回未更改的输入,而不是FALSE?
mazsolt at yahoo dot com
20年前
如果你想向浏览器发送输出(浏览器接受gzip),并且你还没有设置缓冲回调ob_start("ob_gzhandler"),你可以使用gzencode()函数。

header("Content-Encoding: gzip");
echo gzencode("some output", 9, FORCE_GZIP);
brewthatistrue at plzYnoAspamHorOevilO dot com
21 年前
我在尝试使gzip压缩正常工作时遇到了很多麻烦
第一次加载时我不断收到空白页面,尽管后续页面加载正常。
我尝试了ob_flush和ob_end_clean以及各种我不理解的东西。我对不同版本的PHP和不同的gzip压缩方法完全感到困惑。

最终,我成功了(也许以后我会理解)。
我最终使用了phpBB的代码(去掉了非gzip压缩的部分,并做了一些细微的修改)
http://phpbb.com/

index.php
------------
<?php
require_once('top.php');

echo
"<html>\n<head>\n<title>Gzip Test</title>\n<body>\n<h1>testing</h1>\n</body>\n</html>";

require_once(
'bottom.php');
?>

gz_header.php - 来自phpBB的page_header.php
--------------
<?php
$phpver
= phpversion();

$useragent = (isset($_SERVER["HTTP_USER_AGENT"]) ) ? $_SERVER["HTTP_USER_AGENT"] : $HTTP_USER_AGENT;

if (
$phpver >= '4.0.4pl1' && ( strstr($useragent,'compatible') || strstr($useragent,'Gecko') ) )
{
if (
extension_loaded('zlib') )
{
ob_start('ob_gzhandler');
}
}
else if (
$phpver > '4.0' )
{
if (
strstr($HTTP_SERVER_VARS['HTTP_ACCEPT_ENCODING'], 'gzip') )
{
if (
extension_loaded('zlib') )
{
$do_gzip_compress = TRUE;
ob_start();
ob_implicit_flush(0);

header('Content-Encoding: gzip');
}
}
}
?>
gz_footer.php - 来自phpBB的page_tail.php
------------
<?php
// 如果需要,压缩缓冲输出并发送到浏览器
if ( $do_gzip_compress )
{
//
// 来自php.net!
//
$gzip_contents = ob_get_contents();
ob_end_clean();

$gzip_size = strlen($gzip_contents);
$gzip_crc = crc32($gzip_contents);

$gzip_contents = gzcompress($gzip_contents, 9);
$gzip_contents = substr($gzip_contents, 0, strlen($gzip_contents) - 4);

echo
"\x1f\x8b\x08\x00\x00\x00\x00\x00";
echo
$gzip_contents;
echo
pack('V', $gzip_crc);
echo
pack('V', $gzip_size);
}

exit;
?>

我必须承认在Opera 7.11中没有看到这个工作。也许我会弄清楚。
如果有什么需要更改的,请给我发邮件,我可以编辑我的帖子。
匿名
21 年前
使用ob_start("ob_gzhandler")时,请注意必须刷新输出缓冲区才能调用ob_gzhandler回调函数。

这并非总是发生。例如,如果使用ob_get_contents()将输出缓冲区复制到字符串以进行进一步操作,然后使用ob_end_clean()静默丢弃缓冲区,则输出缓冲区永远不会“刷新”,因此ob_gzhandler回调函数永远不会被调用。你的页面不会被压缩。

例如,如果你使用PHP Fusebox架构/框架,就会出现这种情况。
nospam at 1111-internet dot com
21 年前
zlib.output_compression和ob_gzhandler的区别

zlib.output_compression与脚本执行并行运行——它一收到脚本的任何输出就开始压缩,并且每次其缓冲区(默认4K)满时都向客户端发送数据。ob_gzhandler(实际上是'ob_start("ob_gzhandler");')直到脚本刷新(或者通常是退出)才会开始压缩,并且反过来会一次发送整个压缩文档——这使得它更容易导致感知到的延迟。

优点:zlib.output_compression

另一方面,ob_gzhandler允许你进行脚本级控制,允许你选择性地使用它,或者在某些情况下设置它之后取消设置它。尽管某些文档与之相反,但zlib.output_compression似乎无法在脚本级别设置或取消设置;你必须在全局(在php.ini中)或在你的web服务器配置或.htaccess文件中设置它,可能使用FilesMatch类型的机制来控制它将应用于哪些脚本或不应用于哪些脚本——对于大型项目来说,这可能会变得难以处理——特别是那些除了正常的文本输出之外还使用PHP生成图像和其他非文本输出的项目。

优点:ob_gzhandler

最终优势:取决于你的特定需求。我现在正在尝试zlib.output_compression,但我错过了ob_gzhandler提供的灵活性。

附:这是一个Apache 1.3.* httpd.conf/.htaccess文件片段,演示了有条件设置zlib.output_compression的语法

<FilesMatch "\.(php|html?)$">
# 使用php_value将缓冲区设置为2K并启用它
php_value zlib.output_compression 2048
# 或只使用php_flag启用它
# php_flag zlib.output_compression On
</FilesMatch>
dsugar100 at dolphinsoft dot com
22 年前
我注意到,如果将php的zlib.output_compression设置为'On',并且尝试使用ob_gzhandler处理程序来ob_start()输出,那么在PHP 4.2.3中,你在浏览器中获得的输出将被破坏。我猜输出缓冲区正在压缩要发送的输出,然后zlib再次进行压缩,但浏览器只解压一次。

但是使用任何一个都会给你相同的结果(来自脚本的压缩输出)
isoma at altavista dot net
22 年前
RFC 2616建议一种更正确的动态压缩文档的方法是使用gzip传输编码而不是gzip内容编码。但是,可能不建议使用它,因为客户端对它的支持非常有限。

当你从浏览器保存文件时,差异最为明显。如果是gzip内容编码,则浏览器应该(并且可能)以gzip压缩格式保存它。如果是gzip传输编码,则浏览器应该先解压它。
junior at jaj dot com
22 年前
为了使其正常工作,你必须使用“--with-zlib”编译PHP。如果你不这样做,你不会得到任何错误,它只是不会实际压缩任何东西。这是一件了不起的事情。只需少量处理器时间,你就可以大幅减少脚本的带宽需求。几乎没有情况下不应该使用它。
davey at its-explosive dot net
21 年前
要将第二个参数传递给ob_gzhandler(),该参数指定要使用的压缩级别(我假设与gzip二进制文件一样,范围是1-9,其中9使用最多的处理器和时间,4是标准的IIRC),必须像这样调用ob_start()

ob_start(array('ob_gzhandler',9));

- Davey
aki at robotnik dot net
21 年前
如果你遇到类似的错误:
"输出处理器'ob_gzhandler'不能重复使用"
如果你使用了
"ob_start("ob_gzhandler");"

检查你的php.ini文件,它应该像这样:

output_buffering = Off ; 删除4096k值
output_handler =
zlib.output_compression = Off
tehjosh at gamingg dot net
17 年前
faisun at sina dot com
如果你了解输出缓冲,你会发现如果输出缓冲回调返回false,这将指示PHP输出原始字符串而不作任何修改。这就是为什么ob_gzhandler()在不支持编码时返回false。当使用ob_start("ob_gzhandler")且不支持编码时,ob_gzhandler()将返回false,PHP将输出原始的未压缩字符串。

jsnell at e-normous dot com
输出缓冲回调函数最多接受两个参数,因此这可能更适合你的情况

<?php
function ob_gz_handler_no_errors($buffer, $mode)
{
@
ob_gzhandler($buffer, $mode);
}

ob_start('ob_gzhandler_no_errors');
?>

但是,如果你试图抑制由已发送标头引起的错误,最好在可以发送任何输出之前,尽早启动输出缓冲。
grange (at) club-internet (dot) fr
19 年前
这看起来很明显,但是如果你使用ob_start("ob_gzhandler"),请确保在之前没有意外地回显任何内容:这可能是<?php'之前的空行或包含文件中的'?>'标签之后,甚至是错误。

否则,只有一部分内容(在ob_start()之后发送的内容)将被压缩,这会使客户端混淆。

在php.ini或Apache配置文件(使用指令php_flag)中设置zlib.output_compression更安全。
匿名
19 年前
无论在何种情况下,都绝不能检查用户代理字符串以确定是否启用了gzip压缩。HTTP 为此目的精确定义了 Accept-Encoding 头,并且在启用压缩之前必须检查它。
To Top