PHP Conference Japan 2024

headers_sent

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

headers_sent檢查標頭是否已送出或送出位置

說明

headers_sent(字串 &$filename = null, 整數 &$line = null): 布林值

檢查標頭是否已送出或送出位置。

一旦標頭區塊已經送出,您就不能再使用 header() 函式新增任何標頭行。使用此函式至少可以防止收到與 HTTP 標頭相關的錯誤訊息。另一個選項是使用輸出緩衝

參數

filename

如果設定了選用的 filenameline 參數,headers_sent() 會將 PHP 原始碼檔案名稱和開始輸出的行號分別放入 filenameline 變數中。

注意事項:

如果在執行 PHP 原始碼檔案之前就已經開始輸出(例如因為啟動錯誤),filename 參數將會被設為空字串。

line

開始輸出的行號。

返回值

如果尚未發送任何 HTTP 標頭,headers_sent() 將返回 false,否則返回 true

範例

範例 #1 使用 headers_sent() 的範例

<?php

// 如果沒有發送標頭,則發送一個
if (!headers_sent()) {
header('Location: http://www.example.com/');
exit;
}

// 使用選用檔案和行參數的範例
// 注意 $filename 和 $linenum 是傳入以供稍後使用。
// 事先不要賦予它們值。
if (!headers_sent($filename, $linenum)) {
header('Location: http://www.example.com/');
exit;

// 你很可能會在這裡觸發錯誤。
} else {

echo
"標頭已在 $filename 的第 $linenum 行送出\n" .
"無法重新導向,目前請點擊此 <a " .
"href=\"http://www.example.com\">連結</a> 來代替\n";
exit;
}

?>

備註

注意事項:

只有在使用支援標頭的 SAPI 時,才能存取和輸出標頭。

參見

  • ob_start() - 開啟輸出緩衝
  • trigger_error() - 產生使用者層級的錯誤/警告/通知訊息
  • headers_list() - 返回已發送(或準備發送)的回應標頭列表
  • 更詳細的相關說明,請參考 header() - 發送原始 HTTP 標頭

新增註釋

使用者貢獻的註釋 7 則註釋

yesmarklapointe at hotmail dot com
16 年前
我在理解相關概念時遇到了一些困難。以下是我的困境以及我得出的結論,希望能幫助到其他人。

我使用的環境是 WAMPserver:Windows XP SP3 上的 PHP 5.2.6 和 Apache 2.2.8。如果這對您的復現過程很重要,
我在 WAMPserver 中找到兩個 php.ini 檔案,其中的 output_buffering 設定為 4096。為了這次測試,我將它們改為 OFF。

以下是您可以復現我的情況的方法:使用 IE 7.0,前往「工具」>「顯示 ieHTTP 標頭」> 並重複執行以下腳本,觀察發生的情況:

<?php
header
( 'Expires: Mon, 26 Jul 1998 05:00:00 GMT' );
//var_dump(headers_sent());
//print("whatever");
//flush();
//echo "whatever";
var_dump(headers_sent());
?>

結果:headers_sent() 函式的最後一次 var_dump
總是會返回 FALSE,除非它上面的任何一行或多行註釋被取消。取消註釋這些語句會將輸出發送給使用者,而不僅僅是發送給他們的瀏覽器,之後最後一次 var_dump 將返回 TRUE。讓我感到困惑的是,即使所有輸出行都被註釋掉,ieHTTPheaders 工具也顯示標頭正在發送到使用者的瀏覽器。那麼為什麼在這種情況下 headers_sent() 會返回 FALSE 呢?因為您可以繼續發送其他標頭。headers_sent 函式旨在提醒您何時無法再發送標頭。我的測試表明,除非在標頭之後也發送了一些其他輸出,否則它不會返回 true,從而發出信號:「標頭已發送並以使用者輸出結束。現在您無法再發送任何標頭。」

其他人也在一個(錯誤的)錯誤報告中解決了這個問題:http://bugs.php.net/bug.php?id=30264
以下是專業人士回覆的相關部分
「當您使用壓縮時,整個頁面會被緩衝在記憶體中,直到請求結束。因此,您可以隨時發送標頭,因為當您列印數據時,實際上沒有數據發送給使用者。在 PHP 實際決定將任何頁面輸出發送給使用者之前,您仍然可以發送額外的標頭,這就是 headers_sent() 函式返回 false 的原因。它只會在輸出開始傳送給使用者且您無法再發送任何額外標頭時返回 true,表示標頭已發送。」

總而言之,我的觀點是,僅將標頭發送到瀏覽器(之後可以跟隨其他標頭)與將標頭發送並以使用者輸出結束之間存在差異。該函式應該有一個更清晰的名稱,例如 headers_concluded()。
Jakob B.
18 年前
<?php
函式 redirect($filename) {
if (!
headers_sent())
header('Location: '.$filename);
else {
echo
'<script type="text/javascript">';
echo
'window.location.href="'.$filename.'";';
echo
'</script>';
echo
'<noscript>';
echo
'<meta http-equiv="refresh" content="0;url='.$filename.'" />';
echo
'</noscript>';
}
}
redirect('http://www.google.com');
?>
php at fufachew dot REMOVEME dot com
20 年前
回覆:antti at haapakangas dot net 的文章

我修改了程式碼,如果未設定 $_SERVER['HTTP_HOST'],則會使用 $_SERVER['SERVER_NAME']。$_SERVER['SERVER_NAME'] 並不符合我的需求,但我認為可以將其作為備用方案。我也修正了 meta refresh 行中的問題 — 它的 content 屬性缺少「url=」的部分。

<?php
函式 server_url()
{
$proto = "http" .
((isset(
$_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") ? "s" : "") . "://";
$server = isset($_SERVER['HTTP_HOST']) ?
$_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
return
$proto . $server;
}

函式
redirect_rel($relative_url)
{
$url = server_url() . dirname($_SERVER['PHP_SELF']) . "/" . $relative_url;
if (!
headers_sent())
{
header("Location: $url");
}
else
{
echo
"<meta http-equiv=\"refresh\" content=\"0;url=$url\">\r\n";
}
}
?>
trevize (shtrudel) gmail.com
19 年前
請注意,在 IIS(或至少是 W2K 伺服器隨附的版本)中,伺服器似乎會進行一些緩衝,因此即使您輸出一些內容或造成警告,headers_sent() 的值也可能是 false,因為標頭尚未發送。

所以這並不是判斷您的腳本中是否遇到警告的可靠方法。
collectours at free dot fr
17 年前
回覆 K.Tomono 和 alexrussell101 at gmail dot com

是的,
即使您使用 print() 或 header() 向輸出發送了一些內容,如果 php.ini 中的 output_buffering 設定不是 Off,且您發送的內容長度未超過 output_buffering 的大小,則 headers_sent() 將返回 false。

要測試它,請使用 php.ini 中的這些值嘗試以下程式碼:
1) output_buffering=32
2) output_buffering = 4096

[程式碼]
<?php
echo "Yo<br />";
echo
"已送出標頭:",headers_sent(),"<br />";
echo
"足夠的文字填滿緩衝區直到溢出 ;-)<br />";
echo
"已送出標頭:",headers_sent(),"<br />";
?>
[/程式碼]

然後設定
3) output_buffering = Off
並嘗試以下程式碼
[程式碼]
<?php
echo "Yo<br />";
echo
"已送出標頭:",headers_sent(),"<br />";
?>
[/程式碼]
這次將無條件顯示標頭已送出。

這在 php.ini 的註釋中有說明:
「輸出緩衝允許您即使在發送主體內容後也能發送標頭行(包括 Cookie),但代價是會稍微降低 PHP 的輸出層速度。」

注意:這與 implicit_flush 的設定完全無關。
rojasredes at gmail dot com
6 年前
請記住,使用 UTF-8 編碼 (無 BOM) 儲存原始碼檔案 *.php。
如果使用 UTF-8 BOM 儲存,則以下程式碼將始終返回「true」

<?php
if headers_sent($source,$numline)
{
die(
"true");
}
else
{
die(
"false");
}
?>

即使 $numline 為 1,並且在 <?php 之前和 ?> 之後有任何字元。
將檔案儲存為無 BOM 格式,一切都會正常。
Jase
15 年前
試圖描述標頭行為的文章數量變得令人煩惱。

標頭會先出現在傳送到使用者瀏覽器的資料中。

如果已使用 header() 函式呼叫標頭,但尚未將任何資料傳送到輸出緩衝區(使用 echo、readfile 等),則標頭將在指令碼執行結束時傳送;否則,它們將在緩衝區達到其限制並清空時傳送。

很簡單。

這表示如果沒有任何內容傳送到輸出緩衝區,headers_sent() 將返回 false,因為標頭將在指令碼結束時傳送。

這也不是錯誤,這是預期的行為。將標頭保留到強制傳送出去是一個好主意,因為可以採取某些措施,例如防止 Meta Injection 等(header() 中的選項可以替換尚未傳送的標頭)。

此外,當您嘗試發送標頭但輸出緩衝區已被清空時(例如在 PHP 錯誤處理的情況下),`headers_sent()` 函式就很有用。顯然,如果緩衝區已被清空,則發送標頭將無法運作。
To Top