PHP Conference Japan 2024

ob_gzhandler

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

ob_gzhandlerob_start 回呼函式,用於 gzip 輸出緩衝區

說明

ob_gzhandler(string $data, int $flags): string|false

ob_gzhandler() 預期作為 ob_start() 的回呼函式使用,以協助將 gz 編碼的資料傳送到支援壓縮網頁的網路瀏覽器。在 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();
?>
ob_start("ob_gzhandler") 如果瀏覽器不支援 gzip 則會返回 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
'這是在等級 1 壓縮的';
?>
mariusads[at]helpedia[dot]com
18 年前
請注意,正如其他使用者已經提到的,如果 php 開始標記 "< ?" 之前有字元,壓縮將會失敗。

當使用 Ultraedit 等編輯器以 UTF-8 格式儲存檔案時,也會發生這種情況。請確保您使用定義的選項 UTF-8 NO BOM 儲存檔案,或刪除 BOM,否則兩個 UTF BOM 字元將會寫在檔案的開頭。
Tony Piper
19 年前
我剛剛花了 30 分鐘想知道為什麼我的瀏覽器沒有收到 gzip 版本 :-O

如果您正在使用 Google Web Access,除非您告訴 GWA 忽略特定網站,否則您的網頁將會以未壓縮的方式傳送到瀏覽器。

真是顯而易見。
m at rtij dot nl
19 年前
所有版本的 MSIE 都有一個錯誤,它們不會快取 gzipd 內容。如果您的應用程式依賴快取,這點必須牢記在心。最後,我做了

<?php

// 這些都很有用,它們預設為 true。
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(所有版本)在壓縮和快取方面存在錯誤。所以對於 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 的壓縮功能。但是,您必須指定一個 mode 引數(我正在 RedHat9 上使用 4.2.2)。mode 值會影響自動新增的標頭(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 '發生內部錯誤。';
} 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://apache-httpd.dev.org.tw/docs/2.0/mod/mod_deflate.html 以獲得出色的操作說明。

對於其他網頁伺服器,我非常確定它們有等效的功能。

乾杯,Ronald
lapoint2 at msu dot edu
19 年前
那麼為什麼網頁伺服器不預設執行此操作呢?
如果我這樣做,這對我來說有效
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 年前
xn@bnw.com 所揭露的問題

當您全域呼叫時會導致實際問題
到 ob_gzhandler,例如在 include 設定中
檔案,並且您想要因任何原因傳送非 gzip 內容...

為了解決這個問題,

在您全域呼叫 ob_gzhandler 之後放置以下程式碼

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

如果您嘗試在 ob_gzhandler 呼叫之前覆寫標頭
或在緩衝輸出期間,它將不起作用。
(可能在 ob_gzhandler 呼叫時刪除,並且不允許在
緩衝期間)。

希望這有幫助
walter
15 年前
我的框架中有一個經過良好測試的模組(即:在生產環境中執行了幾年),稱為「fastpage」,它會處理經常請求的內容的記憶體快取和 gzip 壓縮。它會快取每個唯一頁面的一個版本,包括 gzip 壓縮和未 gzip 壓縮的版本,並根據瀏覽器的 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 的內容進行 gzip 解壓縮。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 年前
如果您想要抑制錯誤(例如在標頭已傳送的情況下),並且您不想編寫或修補您的 error_handler,您需要建立一個中間回呼

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.dev.org.tw/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 偵測到缺少對 deflate 的支援,難道不應該傳回未修改的輸入,而不是 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 中運作。也許我會找出原因。
如果我有任何應該更改的地方,請發電子郵件給我,我可以編輯我的文章
Anonymous
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 中)或在您的網路伺服器設定或 .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 年前
我注意到,如果您將 zlib.output_compression 的 php 設定設為 '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(),以指定應該使用的壓縮等級(我假設是 1-9,就像 gzip 二進位檔一樣,9 使用最多的處理器和時間來執行,4 是標準 IIRC),你必須像這樣呼叫 ob_start()

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

- Davey
aki at robotnik dot net
21 年前
如果你收到類似以下的錯誤
"output handler 'ob_gzhandler' cannot be used twice" (輸出處理器 '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"),請確保在之前沒有任何內容被意外 echo:這可能是包含檔案中 '' 標籤之後的空白行,甚至是錯誤。

否則,只有部分內容(ob_start() 之後發送的內容)會被壓縮,這會讓客戶端感到困惑。

在這方面,使用 php.ini 或 Apache 設定檔 (使用 php_flag 指令) 中的 zlib.output_compression 設定壓縮更安全。
Anonymous
19 年前
無論在任何情況下,你都絕對不能檢查 user agent 字串來判斷是否啟用了 gzip 壓縮。HTTP 為此定義了 Accept-Encoding 標頭,你必須在啟用壓縮之前檢查它。
To Top