PHP Conference Japan 2024

ob_start

(PHP 4, PHP 5, PHP 7, PHP 8)

ob_start開啟輸出緩衝

描述

ob_start(?callable $callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS): bool

此函數會開啟輸出緩衝。當輸出緩衝啟用時,腳本不會傳送任何輸出,而是將輸出儲存在內部緩衝區中。請參閱哪些輸出會被緩衝? 以了解確切會影響哪些輸出。

輸出緩衝區是可堆疊的,也就是說,可以在另一個緩衝區啟用時呼叫ob_start()。如果有多個輸出緩衝區處於啟用狀態,則輸出會按照巢狀順序依序透過每個緩衝區篩選。請參閱巢狀輸出緩衝區以取得更多詳細資料。

請參閱使用者層級輸出緩衝區以取得輸出緩衝區的詳細描述。

參數

callback

可以指定一個可選的 callback callable。也可以透過傳遞 null 來略過。

當輸出緩衝區被刷新(傳送)、清除,或在腳本結束時刷新輸出緩衝區時,會叫用callback

callback 的簽章如下

handler(string $buffer, int $phase = ?): string
buffer
輸出緩衝區的內容。
phase
PHP_OUTPUT_HANDLER_* 常數的位元遮罩。請參閱傳遞給輸出處理程序的旗標以取得更多詳細資料。

如果callback 傳回 false,則會傳回緩衝區的內容。請參閱輸出處理程序的傳回值以取得更多詳細資料。

警告

從輸出處理程序內呼叫下列任何函數都會導致嚴重錯誤:ob_clean()ob_end_clean()ob_end_flush()ob_flush()ob_get_clean()ob_get_flush()ob_start()

請參閱輸出處理程序使用輸出處理程序,以取得有關 callback(輸出處理程序)的更多詳細資料。

chunk_size

如果傳遞了可選參數 chunk_size,則在任何導致緩衝區長度等於或超過 chunk_size 的程式碼區塊產生輸出後,緩衝區將會被刷新。預設值 0 表示所有輸出都會被緩衝,直到緩衝區關閉為止。請參閱緩衝區大小以取得更多詳細資料。

flags

flags 參數是一個位元遮罩,用於控制可以在輸出緩衝區上執行的操作。預設值是允許清理、刷新和移除輸出緩衝區,這可以使用緩衝區控制旗標明確設定。請參閱允許對緩衝區執行的操作以取得更多詳細資料。

每個旗標都會控制對一組函數的存取,如下所述

常數 函數
PHP_OUTPUT_HANDLER_CLEANABLE ob_clean()
PHP_OUTPUT_HANDLER_FLUSHABLE ob_flush()
PHP_OUTPUT_HANDLER_REMOVABLE ob_end_clean()ob_end_flush()ob_get_clean()ob_get_flush()

傳回值

成功時傳回 true,失敗時傳回 false

範例

範例 1 使用者定義的回呼函數範例

<?php

function callback($buffer)
{
// 將所有的蘋果替換成橘子
return (str_replace("apples", "oranges", $buffer));
}

ob_start("callback");

?>
<html>
<body>
<p>這就像在比較蘋果和橘子。</p>
</body>
</html>
<?php

ob_end_flush
();

?>

上面的範例將輸出

<html>
<body>
<p>It's like comparing oranges to oranges.</p>
</body>
</html>

範例 2 建立一個不可清除的輸出緩衝區

<?php

ob_start
(null, 0, PHP_OUTPUT_HANDLER_STDFLAGS ^ PHP_OUTPUT_HANDLER_REMOVABLE);

?>

參見

新增筆記

使用者貢獻的筆記 39 則筆記

Ray Paseur (Paseur ... ImagineDB.com)
19 年前
您可以使用 PHP 來產生靜態 HTML 頁面。如果您有複雜的腳本,且基於效能考量,您不希望網站訪客重複執行,這會很有用。一個「cron」作業可以執行 PHP 腳本來建立 HTML 頁面。例如

<?php // 建立 index.html
ob_start();
/* 執行複雜查詢、回傳結果等等。 */
$page = ob_get_contents();
ob_end_clean();
$cwd = getcwd();
$file = "$cwd" .'/'. "index.html";
@
chmod($file,0755);
$fw = fopen($file, "w");
fputs($fw,$page, strlen($page));
fclose($fw);
die();
?>
net_navard at yahoo dot com
18 年前
哈囉,朋友們

ob_start() 會開啟一個緩衝區,所有輸出都會儲存在其中。因此,每次您執行 echo 時,該輸出都會被加入緩衝區。當腳本執行完畢,或您呼叫 ob_flush() 時,該儲存的輸出會被傳送到瀏覽器 (如果您使用 ob_gzhandler,則會先進行 gzip 壓縮,這表示下載速度會更快)。

使用 ob_start 最常見的原因是收集原本會被傳送到瀏覽器的資料。

以下是 ob_start() 的兩種用法

1-您能更有效地控制輸出。一個簡單的範例:假設您想要向使用者顯示錯誤訊息,但腳本已經將某些 HTML 傳送到瀏覽器。這看起來會很醜,會有一個半渲染的頁面,然後出現錯誤訊息。使用輸出緩衝區函式,您可以簡單地刪除緩衝區,只傳送錯誤訊息,這表示它看起來會很整潔。
2-發明輸出緩衝區的原因是建立從 php 引擎 -> apache -> 作業系統 -> 網路使用者之間的無縫傳輸。

如果您確定每個都使用相同的緩衝區大小,系統會減少寫入次數、使用較少的系統資源,並能夠處理更多流量。

敬上,Hossein
ed.oohay (a) suamhcs_rodnan
21 年前
輸出緩衝區甚至可以在巢狀範圍中運作,或者可以應用在遞迴結構中...我認為這可以為某人節省一些猜測和測試的時間 :)

<pre><?php

ob_start
(); // 啟動輸出緩衝區 1
echo "a"; // 填入 ob1

ob_start(); // 啟動輸出緩衝區 2
echo "b"; // 填入 ob2
$s1 = ob_get_contents(); // 讀取 ob2 ("b")
ob_end_flush(); // 將 ob2 刷入 ob1

echo "c"; // 繼續填入 ob1
$s2 = ob_get_contents(); // 讀取 ob1 ("a" . "b" . "c")
ob_end_flush(); // 將 ob1 刷入瀏覽器

// 會回傳 "b",接著回傳 "abc",如同以下所示:
echo "<HR>$s1<HR>$s2<HR>";

?></pre>

...至少在 Apache 1.3.28 上能運作

Nandor =)
mjr
20 年前
如果您在 PHP 中使用物件導向程式碼,您可能和我一樣,想要使用物件內部 (也就是類別函式) 的回呼函式。在這種情況下,您會將一個雙元素陣列作為單一參數傳送給 ob_start。第一個元素是物件的名稱 (開頭沒有 $),第二個元素是要呼叫的函式。因此,若要使用名為 '$template' 的物件中的函式 'indent',您會使用 <?php ob_start(array('template', 'indent')); ?>
mchojrin at gmail dot com
12 年前
在此向那些像我一樣從 5.3 升級的人發出警告。我有一段過去可以運作的程式碼

<?php
if ( !ob_start( !DEBUGMODE ? 'ob_gzhandler' : '' ) ) {
ob_start();
}
?>

現在已經無法運作了 (我會收到類似以下的錯誤:警告:ob_start():找不到函式 '' 或函式名稱無效)。

不過,這很容易修正,只需將 '' 變更為 null,像這樣

<?php
if ( !ob_start( !DEBUGMODE ? 'ob_gzhandler' : null ) ) {
ob_start();
}
?>

這會保留程式碼的意圖,而且可以運作 :)
jhlavon
11 年前
當在類別的建構子部分使用時,它必須加上 "self::" 或類別名稱作為前置詞,否則 PHP 會無法建立緩衝區。

function __construct ()
{

$bo = ob_start ("self::callback_ob") ;
...
}
Asher Haig (ahaig at ridiculouspower dot com)
17 年前
當腳本結束時,所有緩衝輸出都會被刷入 (這不是錯誤:http://bugs.php.net/bug.php?id=42334&thanks=4)。當腳本在輸出緩衝區中間擲出錯誤 (並因此結束) 時,會發生什麼事?腳本會先回傳緩衝區中的所有內容,然後再列印錯誤!

以下是我找到最簡單的解決方案。將其放在錯誤處理函式的開頭,以清除所有緩衝資料並只列印錯誤

$handlers = ob_list_handlers();
while ( ! empty($handlers) ) {
ob_end_clean();
$handlers = ob_list_handlers();
}
Chris
14 年前
使用會變更頁面標頭的函式時請小心;結束輸出緩衝區時,該變更將不會還原。

例如,如果您有一個類別會產生影像並設定適當的標頭,它們在 ob 結束後仍然會保留。

例如
<?php
ob_start
();
myClass::renderPng(); //header("Content-Type: image/png"); 在這裡
$pngString = ob_get_contents();
ob_end_clean();
?>

會將影像位元組放入 $pngString,並將內容類型設定為 image/png。雖然影像不會被傳送到用戶端,但 png 標頭仍然存在;如果您在這裡執行 html 輸出,瀏覽器很可能會顯示「影像錯誤,無法檢視」,至少 firefox 是這樣。

您需要手動設定正確的影像類型 (text/html)。
jkloss at hotmail dot com
20 年前
如果 ob_start 看起來沒有對您運作,請注意,使用 Apache 2 時,flush() 函式會導致 PHP 傳送標頭,無論是否在 flush 之前呼叫過 ob_start。

ob_start();
echo 'test';
flush();

會導致 Apache 2 傳送可能堆疊的任何標頭 - 這表示您無法在 flush 之後使用 header(location:xxx)。若要修正此問題,請移除 flush()。我花了數小時才發現這一點。Apache 1.x 不是這樣運作的。
lucky760 at yahoo dot com
18 年前
以下是基於先前發布的 compress() 函數所做的擴展,這個小巧的類別稍微改進了這個想法。基本上,在每次頁面載入時都對所有 CSS 執行 compress() 函數,顯然不是最佳做法,尤其樣式通常只有在最糟的情況下才會不頻繁地變更。

有了這個類別,您可以簡單地指定一個 CSS 檔案名稱陣列,然後呼叫 dump_style()。每個檔案的內容會以 compress() 壓縮過的形式儲存在快取檔案中,而且只有在對應的原始 CSS 變更時才會重新建立。

這個類別適用於 PHP5,但如果您只是將所有物件導向 (OOP) 的部分移除,並可能定義 file_put_contents,它也能以相同的方式運作。

請享用!

<?php

$CSS_FILES
= array(
'_general.css'
);

$css_cache = new CSSCache($CSS_FILES);
$css_cache->dump_style();

//
// class CSSCache
//

class CSSCache {
private
$filenames = array();
private
$cwd;

public function
__construct($i_filename_arr) {
if (!
is_array($i_filename_arr))
$i_filename_arr = array($i_filename_arr);

$this->filenames = $i_filename_arr;
$this->cwd = getcwd() . DIRECTORY_SEPARATOR;

if (
$this->style_changed())
$expire = -72000;
else
$expire = 3200;

header('Content-Type: text/css; charset: UTF-8');
header('Cache-Control: must-revalidate');
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $expire) . ' GMT');
}

public function
dump_style() {
ob_start('ob_gzhandler');

foreach (
$this->filenames as $filename)
$this->dump_cache_contents($filename);

ob_end_flush();
}

private function
get_cache_name($filename, $wildcard = FALSE) {
$stat = stat($filename);
return
$this->cwd . '.' . $filename . '.' .
(
$wildcard ? '*' : ($stat['size'] . '-' . $stat['mtime'])) . '.cache';
}

private function
style_changed() {
foreach (
$this->filenames as $filename)
if (!
is_file($this->get_cache_name($filename)))
return
TRUE;
return
FALSE;
}

private function
compress($buffer) {
$buffer = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $buffer);
$buffer = str_replace(array("\r\n", "\r", "\n", "\t", ' '), '', $buffer);
$buffer = str_replace('{ ', '{', $buffer);
$buffer = str_replace(' }', '}', $buffer);
$buffer = str_replace('; ', ';', $buffer);
$buffer = str_replace(', ', ',', $buffer);
$buffer = str_replace(' {', '{', $buffer);
$buffer = str_replace('} ', '}', $buffer);
$buffer = str_replace(': ', ':', $buffer);
$buffer = str_replace(' ,', ',', $buffer);
$buffer = str_replace(' ;', ';', $buffer);
return
$buffer;
}

private function
dump_cache_contents($filename) {
$current_cache = $this->get_cache_name($filename);

// 快取存在 - 直接輸出
if (is_file($current_cache)) {
include(
$current_cache);
return;
}

// 移除此檔案任何舊的、殘留的快取
if ($dead_files = glob($this->get_cache_name($filename, TRUE), GLOB_NOESCAPE))
foreach (
$dead_files as $dead_file)
unlink($dead_file);

$compressed = $this->compress(file_get_contents($filename));
file_put_contents($current_cache, $compressed);

echo
$compressed;
}
}

?>
ernest at vogelsinger dot at
18 年前
當您依賴 URL 重寫來傳遞 PHP 工作階段 ID 時,您應該小心使用 ob_get_contents(),因為這可能會完全停用 URL 重寫。

範例
ob_start();
session_start();
echo '<a href=".">自身連結</a>';
$data = ob_get_contents();
ob_end_clean();
echo $data;

在上面的範例中,永遠不會發生 URL 重寫。事實上,如果您使用 ob_end_flush() 結束緩衝區,則會發生重寫。在我看來,重寫發生在啟動工作階段的同一個緩衝區中,而不是在最終輸出階段。

如果您需要像上面的場景,使用「內部封包」會有幫助

ob_start();
ob_start(); // 加入內部緩衝封包
session_start();
echo '<a href=".">自身連結</a>';
ob_end_flush(); // 關閉內部封包會啟用 URL 重寫
$data = ob_get_contents();
ob_end_clean();
echo $data;

如果您有興趣,或者像我一樣認為這是一個設計缺陷而非功能,請造訪錯誤 #35933 (http://bugs.php.net/bug.php?id=35933) 並發表評論。
clancy hood at gmail dot com
15 年前
使用 ob 回呼函式時:請注意,傳遞給您方法的第二個參數無法協助您區分 flush 呼叫和 ob_clean 呼叫,但在這兩種情況下都會傳送緩衝區內容,因此您最終會解析不會被使用的資料。另外,請注意,常數 PHP_OUTPUT_HANDLER_START 實際上從未被傳送,而是整數「3」會在第一次 flush 時出現。
<?php

function ob_handler($string, $flag){
static
$input = array();
$done = false;
switch(
$flag){
case
PHP_OUTPUT_HANDLER_START:
$flag_sent = "PHP_OUTPUT_HANDLER_START ($flag)";
break;
case
PHP_OUTPUT_HANDLER_CONT:
$flag_sent = "PHP_OUTPUT_HANDLER_CONT ($flag)";
break;
case
PHP_OUTPUT_HANDLER_END:
$done = true;
$flag_sent = "PHP_OUTPUT_HANDLER_END ($flag)";
break;
default:
$flag_sent = "Flag is not a constant ($flag)";
}
$input[] = "$flag_sent: $string<br />";
$output = "$string<br />";
if(!
$done) return $output;
// print_r($input, 1) 會導致錯誤,而 var_export 就是無法運作
$output .= '<br />';
foreach(
$input as $k=>$v) $output .= "$k: $v";
return
$output;
}

ob_start('ob_handler');

echo
'flush';
ob_flush();

echo
'flush 2';
ob_flush();

echo
'clean';
ob_clean();

echo
'flush 3';
ob_flush();

echo
'end flush';
ob_end_flush();
?>

flush
flush 2
flush 3
end flush

0: Flag is not a constant (3): flush
1: PHP_OUTPUT_HANDLER_CONT (2): flush 2
2: PHP_OUTPUT_HANDLER_CONT (2): clean
3: PHP_OUTPUT_HANDLER_CONT (2): flush 3
4: PHP_OUTPUT_HANDLER_END (4): end flush

我認為 START 旗標問題*可能*是一個錯誤,但我無法在回報之前升級,因為我必須與我的伺服器使用相同的版本 (我使用的是 PHP 5.2.6)。如果有人使用 5.2.11 或其他穩定版本,請隨時測試/回報您認為適合的內容。
Charlie Farrow
17 年前
在某些奇怪的情況下,當在 ob_start() 內部對無法執行的物件執行動作時 (因為物件不存在或方法不存在),腳本將會結束並印出錯誤之前目前函式產生的一切內容,但不會印出其他任何內容,包括錯誤訊息。

我不知道為什麼沒有出現錯誤訊息,並且正在嘗試為開發人員取得一個比我的整個程式更簡單的工作範例!

因此,如果您正在使用 ob_start() 並且沒有獲得任何輸出,請檢查您的物件....您在某處犯了錯誤。唯一的問題是您將不知道錯誤在哪裡,因為沒有錯誤訊息!!
admin at bobfrank dot org
19 年前
如果您想在您建立的字串中間執行程式碼,但您想等待列印...
(因此,如果您想允許使用 bb-code 樣式的 php,並且您想依序執行它,並依序列印所有內容...)

phpRun($code) {
ob_start();
exec($code);
$output = ob_get_contents();
ob_end_clean();
return $output;
}

$str = str_replace("]\n", "]", $str);
$match = array('#\[php\](.*?)\[\/php\]#se');
$replace = array( phpRun( stripslashes('$1') ) );
$str= preg_replace($match, $replace, $str);

echo $str;
rafa dot chacon at factorydea dot com
19 年前
如果您嘗試在迴圈內透過 require_once 包含一個 php 檔案 (例如,一個動態電子郵件範本) 並變更某些變數的值 (例如,取消訂閱的 URL,每個使用者不同),您應該使用

<?php

// ... 一些程式碼

$usermail = array("email1", "email2", ...);

for(
$i = 0; $i < $MAX; $i++)
{
$usermail_unsuscribe = $usermail[$i];
ob_start();
include(
"email_template.php");
ob_clean();
}
?>

否則 $usermail_unsuscribe 將只會取得「email1」的值。
Anonymous
19 年前
我通常會將我的頁面分成四個部分 - 變數初始化、匯入標頭 (使用剛宣告的變數來設定)、主要本文 (大部分是非 PHP)、匯入頁尾。我一直在想,如果將主頁面包含在另一個 PHP 腳本中,是否可以讓另一個 PHP 腳本檢查主本文。我發現我可以透過在頁尾開頭結束標頭中一個未關閉的函式來控制主本文的輸出,從而封閉主本文。然後可以使用輸出緩衝將其讀取到變數中。為了示範如何使用此方法來控制輸出順序,請查看以下範例

<?php
$output
= "";

// 回呼函式來處理緩衝輸出
function capture($buffer)
{
$GLOBALS['output'] .= $buffer;
return
"C ";
}

// 使用輸出擷取來呼叫 printE() 函式
function captureE()
{
ob_start("capture");
printE();
ob_end_flush();
}
?>

A
<?php
// 輸出 'E' (範例情境中的主本文)
function printE()
{
// (在此行之後結束標頭) ?>
E
<?php // (以這一行開始頁尾)
}
?>
B
<?php captureE(); ?>
D
<?php print $output; ?>
F
<?php printE(); ?>
G

輸出為 A B C D E F E G。

對於我上面提到的應用程式,有兩點需要注意
- 單獨執行時,頁面必須輸出其主本文,但檢查腳本應抑制此輸出,也許是透過在包含頁面之前設定的變數,然後在頁尾輸出行中檢查該變數。
- 由於主本文現在位於函式內部,因此它具有不同的命名空間,因此可能需要變更以防止程式碼損壞 (例如,使用全域變數、處理在主本文內定義的函式)。
mariusads at helpedia dot com
15 年前
如果您想使用 ob_start("ob_gzhandler");,請確保您使用的編輯器不會在腳本開頭新增 UTF8/UTF16 BOM。

如果存在這三個字元,像 Firefox 這樣的瀏覽器將無法解碼頁面,並且會回報

內容編碼錯誤

您嘗試檢視的頁面無法顯示,因為它使用無效或不受支援的壓縮形式。

您嘗試檢視的頁面無法顯示,因為它使用無效或不受支援的壓縮形式。

Google Chrome 將只會回報「錯誤 2 (net::ERR_FAILED):不明錯誤」。

將 ob_start 命令註解掉後,頁面將會成功載入,而且瀏覽器通常會偵測到 BOM,而不會在頁面上顯示它,因此一切都很難除錯。
fordiman at gmail dot com
16 年前
這是我每天使用的好用函式。本質上:包含一個 PHP 檔案 - 但將其輸出呈現為變數,而不是緩衝區。它還設定為載入具有變數設定的腳本,並自動將全域變數載入腳本的命名空間,使其成為有效率的範本方案。它還具有錯誤處理,因此在使用輸出緩衝時不會盲目執行。

<?php
$GLOBALS
['BufferedErrors']=Array();
function
errorParse($errno, $errstr, $errfile, $errline, $errcontext) {
$errorTypes = Array(
E_ERROR => '嚴重錯誤',
E_WARNING => '警告',
E_PARSE => '語法錯誤',
E_NOTICE => '注意',
E_CORE_ERROR => '核心嚴重錯誤',
E_CORE_WARNING => '核心警告',
E_COMPILE_ERROR => '編譯錯誤',
E_COMPILE_WARNING => '編譯警告',
E_USER_ERROR => '觸發的錯誤',
E_USER_WARNING => '觸發的警告',
E_USER_NOTICE => '觸發的注意',
E_STRICT => '不建議使用通知',
E_RECOVERABLE_ERROR => '可捕獲的嚴重錯誤'
);
$ret=(object)Array(
'number'=>$errno,
'message'=>$errstr,
'file'=>$errfile,
'line'=>$errline,
'context'=>$errcontext,
'type'=>$errorTypes[$errno]
);
$GLOBALS['BufferedErrors'][]=$ret;
return
false;
}
function
parse($fileToInclude, $argumentsToFile=false) {
$bufferedErrorStack = $GLOBALS['BufferedErrors'];
set_error_handler('errorParse', error_reporting());
$GLOBALS['BufferedErrors']=Array();

if (!
file_exists($fileToInclude))
return
'';
if (
$argumentsToFile === false)
$argumentsToFile = Array();
$argumentsToFile = array_merge($GLOBALS, $argumentsToFile);
foreach (
$argumentsToFile as $variableName => $variableValue)
$
$variableName = $variableValue;
ob_start();
include(
$fileToInclude);
$ret = ob_get_contents();
ob_end_clean();

restore_error_handler();
$errors = $GLOBALS['BufferedErrors'];
$GLOBALS['BufferedErrors'] = $bufferedErrorStack;
if (
count($errors)>0) {
$ret.='<ul class="error">';
foreach (
$errors as $error)
$ret.=
'<li>'.
'<b>'.$error->type.'</b>: '.
$error->message.
'<blockquote>'.
'<i>檔案</i>: '.$error->file.'<br />'.
'<i>行數</i>: '.$error->line.
'</blockquote>'.
'</li>';
$ret.='</ul>';
}
return
$ret;
}
coldshine at gmail dot com
16 年前
參考 dan at roteloftet dot com 的評論

RFC 2616 (HTTP) 指定了一種「透明」的 Content-Encoding,「identity」 (§ 3.5),它非常適合您嘗試使用(無效的)「None」所做的事情。因此,這個方法同樣有效,而且也符合 RFC 規範。

<?php
header
('Content-Encoding: identity', true);
?>
tracey AT archive DOT org
17 年前
將所有 stdout 和 stderr 寫入日誌的方法
從 PHP 腳本 *內部*。
您只需要確保不時呼叫 elog() 來取得輸出。
這是一個「守護進程化」腳本以進行記錄的好方法。
這是一個關於記錄方面「守護進程化」腳本的好方法。

// 這允許我們捕獲所有 stdout 和 stderr(以及 error_log() 呼叫)
// 到這個日誌檔案...
// 「收集的輸出」將在任何時候使用「elog()」時刷新...
ini_set("error_log", "/var/log/script.log");
ob_start();

function elog($str)
{
// 取得任何寫入 stdout 或 stderr 但 *沒有* 使用 elog() 的內容
// 並立即寫入...
$writeme = ob_get_contents();
if ($writeme)
{
error_log($writeme);
ob_end_clean();
ob_start();
}
// 現在寫入此方法被呼叫時的訊息
error_log($str);
}
butch at enterpol dot pl
18 年前
簡單的程式碼,使 phpsession $_GET 對於有效的 XHTML 1.0 Transitional 很好:)

function callback($buffer)
{
$buffer = str_replace("&PHPSESSID", "&amp;PHPSESSID", $buffer);
return $buffer;
}

ob_start("callback");

session_start();
Aleksey
19 年前
此函式動態變更 HTML 頁面的標題

function change_title($new_title) {
$output = ob_get_contents();
ob_end_clean();

$output = preg_replace("/<title>(.*?)<\/title>/", "<title>$new_title</title>", $output);
echo $output;
}

範例
ob_start();
// ... 一些輸出
change_title('新標題!');
me at haravikk dot com
12 年前
我認為值得注意的是,雖然您無法從回呼函式中呼叫任何輸出函式(例如 echo 或 print),但您仍然可以傳送標頭(大概包括 cookie,尚未檢查)。當然,這僅在第一個回呼中起作用,如下所示

<?php

function myCallback($buffer, $flags) {
if (
$flags & PHP_OUTPUT_HANDLER_START) {
header('Server: LastMinuteHeaderServer');
}
}

ob_start('myCallback');
echo
"Hello World!";
ob_end_flush();

?>

這不是最激勵人心的例子,但在這種情況下,程式碼能夠在傳送回應的標頭部分之前偷偷加入最後一刻的標頭。如果您想避免替換不確定的標頭值,這會很方便。

例如,如果您的程式碼可能會傳回影像,但您不想在確定可以成功傳送影像之前設定內容類型,您可以使用回呼將決定權保留到最後一刻,屆時您應該可以確定 HTTP 主體中傳送的內容。
dan at roteloftet dot com
16 年前
某些網頁託管伺服器(至少我的伺服器是)在其 php.ini 中具有以下設定
output_handler = ob_gzhandler

對於傳回影像或一般二進位檔案的 PHP 腳本而言,這被證明是有問題的,因為無法確定壓縮檔案的內容長度。

由於我花了很多時間在網路上搜尋解決方法(由於各種原因,.htaccess 修改不在考慮範圍內),我發現這樣可以很好地取消 php.ini 中指定的 ob_gzhandler

<?php
while (ob_get_level())
ob_end_clean();
header("Content-Encoding: None", true);
?>

將此程式碼放在腳本頂部,在將任何內容寫入頁面之前,這樣腳本結果將不會被壓縮。
Bitwise
15 年前
沒有啟動旗標的問題。只需要注意第二個參數不是模式,而是由按位 OR 運算的旗標組成。

<?php
function ob_handler($string, $flags) {
static
$input = array();
if (
$flags & PHP_OUTPUT_HANDLER_START )
$flags_sent[] = "PHP_OUTPUT_HANDLER_START";
if (
$flags & PHP_OUTPUT_HANDLER_CONT )
$flags_sent[] = "PHP_OUTPUT_HANDLER_CONT";
if (
$flags & PHP_OUTPUT_HANDLER_END )
$flags_sent[] = "PHP_OUTPUT_HANDLER_END";
$input[] = implode(' | ', $flags_sent) . " ($flags): $string<br />";
$output = "$string<br />";
if (
$flags & PHP_OUTPUT_HANDLER_END ) {
$output .= '<br />';
foreach(
$input as $k => $v) $output .= "$k: $v";
}
return
$output;
}

ob_start('ob_handler');

echo
'flush';
ob_flush();

echo
'flush 2';
ob_flush();

echo
'clean';
ob_clean();

echo
'flush 3';
ob_flush();

echo
'end flush';
ob_end_flush();
?>

flush
flush 2
flush 3
end flush

0: PHP_OUTPUT_HANDLER_START | PHP_OUTPUT_HANDLER_CONT (3): flush
1: PHP_OUTPUT_HANDLER_CONT (2): flush 2
2: PHP_OUTPUT_HANDLER_CONT (2): clean
3: PHP_OUTPUT_HANDLER_CONT (2): flush 3
4: PHP_OUTPUT_HANDLER_END (4): end flush
Filip Dalge
12 年前
當拋出嚴重錯誤時,PHP 會在列印錯誤訊息之前,輸出目前輸出控制的緩衝區內容,而不會進行後處理。如果您使用多個輸出控制層級,這可能不會產生預期的行為。

您可以使用輸出回呼處理器來處理這個情況並捨棄輸出。

因此,請將 ob_start("ob_error_handler") 與以下程式碼搭配使用

function ob_error_handler($str) {
$error = error_get_last();
if ($error && $error["type"] == E_USER_ERROR || $error["type"] == E_ERROR) {
return ini_get("error_prepend_string").
"\nFatal error: $error[message] in $error[file] on line $error[line]\n".
ini_get("error_append_string");
}
return $str;
}
bty-adminf2 at trebly dot net
9 年前
這與 ob_start() 使用的預設值有關。

請注意,我可以驗證 ob_start() 和 ob_start(null,0) 並非總是等效。

似乎使用 ob_start() 時,如果您的組態已明確定義輸出緩衝區的目前預設參數,則會使用這些參數開啟輸出緩衝區。

因此,如果您先前已將 $chunk_size 設定為任何值,且傳送的資料大於 $chunk_size,則資料會自動以 $chunk_size 的區塊清除。

如果您明確定義 $chunk_size=0,稍後當您使用任何函式(例如 $my_ob_dataoutput=ob_get_clean())時,您將取回輸出的全部內容(使用 $chunk_size=0 時幾乎沒有限制)。

我發現這個問題是因為我的變數 $my_ob_dataoutput 被截斷了。使用 "ob_get_status (true)" 函式,我可以驗證錯誤(在較低層級或預設情況下超出我的控制)先前將 ob 的 $chunk_size 設定為 4096。
我將 "ob_start()" 變更為 "ob_start(null,0)",一切就正常了。
cj at ceejayoz dot com
15 年前
請注意,自 PHP 5.1.x 起,所有物件的解構函數都會在執行輸出緩衝區回呼函式之前呼叫。因此,全域物件將無法如預期般在函式中使用。

根據 http://bugs.php.net/bug.php?id=40104,這被認為是預期的行為。
mbutscher at gmx dot de
8 年前
如果發生錯誤,輸出緩衝區的內容會與錯誤訊息一起顯示。如果這絕對不能發生,您可以執行下列操作(但這也會消耗錯誤訊息)

<?php
$outputbufferOutput
= FALSE;

function
switchableOutputHandler($buffer) {
global
$outputbufferOutput;

if (
$outputbufferOutput)
return
$buffer;
else
return
"";
}

function
internob_start() {
global
$outputbufferOutput;

$outputbufferOutput = FALSE;
return
ob_start("switchableOutputHandler");
}

function
internob_get_clean() {
global
$outputbufferOutput;

$outputbufferOutput = TRUE;
$result = ob_get_clean();
$outputbufferOutput = FALSE;
return
$result;
}

?>

然後您可以使用 internob_start() 和 internob_get_clean() 來取代 ob_start() 和 ob_get_clean()。其他函式也可以做相應的替換。
geoffrey at nevra dot net
19 年前
當搭配 ob_start() 使用回呼時,例如 ob_get_contents() 等函式不會使用它,請改用 ob_end_flush()。

附註:未以每個 ob_* 函式測試,僅測試 ob_get_contents() 和 ob_end_flush()
Anonymous
13 年前
如果您想要以某種可讀的方式來表示旗標,以下是以私有類別成員的形式呈現的變體

<?php
class foo {
private function
getFlagsReadable($flags) {
$flagNames = array('PHP_OUTPUT_HANDLER_START', 'PHP_OUTPUT_HANDLER_CONT', 'PHP_OUTPUT_HANDLER_END');
$readable = '';
foreach(
$flagNames as $flagName)
if (
$flags & constant($flagName) )
$readable .= (strlen($readable) ? ' | ' : '') . $flagName
;
return
$readable;
}
}
?>
Francois Hill
17 年前
根據 clement dot ayme at st dot com 的評論

根據我的經驗,輸出似乎會被緩衝,但也同時傳送到標準輸出!
codextasy at gmail dot com
16 年前
文件和實際回呼函式調用之間存在差異。
手冊中說:「當呼叫 ob_end_flush() 或在請求結束時將輸出緩衝區清除到瀏覽器時,將會呼叫該函式。」

實際上,一旦透過 ob_start() 設定回呼函式,就會立即呼叫它,而不論情況為何。
以下函式會立即叫用回呼函式
ob_clean
ob_end_clean
ob_end_flush
ob_flush
ob_get_clean

但是只有其中兩個函式會傳回回呼傳回的結果 (ob_end_flush, ob_flush),其他函式則會捨棄它。

在請求結束時,即使未呼叫上述任何函式,仍會呼叫回呼,並且其結果會傳回到瀏覽器(嗯,至少這與手冊相符)。

還有一個技巧
如果您使用 chunk_size > 1 設定回呼函式,則每次輸出緩衝區等於或超過 chunk_size 時,就會呼叫回呼函式,並且其結果會輸出到瀏覽器,即使您稍後呼叫任何 ob_clean()、ob_end_clean() 或 ob_get_clean(),因此請注意這個事實。
Anonymous
22 年前
如果您使用的是 Apache (1.3x 或 2.0),您可能會考慮將自動壓縮功能新增到您傳遞的頁面。

我假設大家都知道如何建立壓縮類別,並在您的程式中使用它們,但是目前還沒有任何類別能夠提供二進位編譯模組的速度和強大功能。此外,這類模組也會在網路記錄檔中記錄「可壓縮」的點擊,因此您的常用網路分析程式就可以向您顯示節省頻寬的報告。

話雖如此,您可以考慮以下兩個適用於 Apache 的模組

1) Apache 1.3x:使用 mod_gzip,可從以下網址取得
http://sourceforge.net/projects/mod-gzip/

2) Apache 2.x:使用 mod_gz,請參閱此處
http://www.mail-archive.com/dev@httpd.apache.org/msg00734.html

3) Apache 1.3x:您也可以使用 mod_defalte,請從以下網址取得
ftp://ftp.lexa.ru/pub/apache-rus/contrib/

希望對您有幫助。
aaron at offtone.com
20 年前
我的回呼儲存在函式類別中,而且使用 ob_start ('Class::callback') 無法正常運作。不想具現化類別(不需要,它是函式類別),我嘗試了這個方法,它運作得很順利

ob_start (array (Class, 'callback'));

PHP 4.3.4
simon
18 年前
發現類別執行個體中的變數在呼叫 ob_start() 後沒有被設定。
然而,在設定變數後呼叫 ob_start,它就能正常運作,但這似乎沒有解決自我包含樣板類別的目標。
解決方法是使用 '&new' 以參考方式指派類別。
以下是簡化的運作範例
<?php
class Buffer {
var
$template = ' - 類別建構子中設定的範本';
function
Buffer() {
$this->startBuffer();
}
function
startBuffer() {
ob_start(array(&$this, 'doFlush'));
}
function
doFlush($buffer) {
/* 簡單的字串串接,展示如何使用
範本字串和緩衝區輸出 */
return $buffer . $this->template;
}
}
/* 範本不會被設定:
$buffer1 = new Buffer();
$buffer1->template = ' - 實例中設定的範本';
echo '一些緩衝區內容';
*/
/* 這會如預期般運作 */
$buffer2 = &new Buffer();
$buffer2->template = ' - 實例中設定的範本';
echo
'一些緩衝區內容';
oersoep at gmail dot com
19 年前
這些很方便。第一個之前已經提過了。

ob_start( array( 'lib_class', 'parse_output' ) );
ob_start( array( $this, 'parse_output' ) );

注意:$this 不是一個參考。任何回呼儲存或記錄的東西,都會在 ob_start 使用的複製中消失。
它確實讓回呼可以使用 $this 的屬性,像是 $this->ar_tpl_value 或任何你的風格。

手冊上說
"如果傳入可選參數 chunk_size,則會在輸出 chunk_size 位元組後的每一個第一個換行符號後呼叫回呼函式。可以透過傳遞 NULL 值來繞過 output_callback 參數。"
這在我的 4.3.11 版中無法運作。可能是 Zend 優化器導致的。不敢關掉它來確認。
php REMOVETHIS at kennel17 dot co dot uk
3 年前
從回呼函式回傳 false 時要非常小心!

儘管文件說明如此,這與回傳未修改的提供的 $buffer 值*不一樣*。在「清除」操作(例如 ob_clean())的上下文中,這會導致您的回呼函式被停用,並跳過任何後續的輸出。

解決方案是確保您始終從回呼回傳一個字串值。這很可能是一個錯誤,它可能會在某個時候被修復,但它適用於 PHP 5.4 到目前版本(撰寫時為 8.1)的所有版本。請注意,這在 PHP 5.3 及更早版本中不是問題。

您可以在這個 Stack Overflow 問題中看到更多詳細資訊:https://stackoverflow.com/questions/69845122/

這個 fiddle 中已設定了問題的示範:https://3v4l.org/66cai
To Top