在執行我的腳本時,我收到幾個如下錯誤:
警告:無法修改標頭資訊- 標頭已由/some/file.php 中的(輸出從/some/file.php:12 開始)發送<第23行<第23行
錯誤訊息中提到的行包含 header() 和 setcookie() 呼叫。
header()
setcookie()
這可能是什麼原因?又該如何解決呢?
在傳送 HTTP 標頭之前發送任何內容(使用 setcookie 或 header)。在 HTTP 標頭之前輸出某些內容的常見原因是:
setcookie
header
意外的空格,通常出現在檔案的開頭或結尾,如下所示:
為了避免這種情況,而省略結尾的 ?> - 無論如何都不需要。
?>
3F 3C
EF BB BF
echo
printf
readfile
passthru
之前的程式碼>
display_errors
$_POST['input']
empty
中,請注意缺少的引號)。
輸出緩衝應該可以解決問題;呼叫ob_start 後的所有輸出都緩衝在記憶體中,直到您釋放緩衝區,例如與ob_end_flush。
在進行任何輸出之前,必須呼叫發送/修改 HTTP 標頭的函數。 摘要⇊# 否則呼叫失敗:
修改 HTTP 標頭的一些函數是:
標題
header_remove
session_start
session_regenerate_id
setrawcookie
輸出可以是:
無意:
# 之前或 ?> 之後的空格
故意:
#print
程式碼之前的原始 部分。
要理解為什麼必須在輸出之前發送標頭,這是必要的 查看典型的 HTTP 回覆. PHP腳本主要產生HTML內容,同時也傳遞一個 傳送到網頁伺服器的 HTTP/CGI 標頭集:
HTTP/1.1 200 OK Powered-By: PHP/5.3.7 Vary: Accept-Encoding Content-Type: text/html; charset=utf-8 PHP page output page Content Some more output follows... and
Some more output follows...
頁面/輸出總是跟隨標題。 PHP 必須透過 首先將標頭髮送到網頁伺服器。它只能這樣做一次。 雙換行之後就再也不能修改它們了。
當 PHP 收到第一個輸出(print、echo、)時,它將 刷新所有收集的標頭。之後它可以發送所有輸出 它想要。但是發送進一步的 HTTP 標頭是不可能的。
print
header() 警告包含所有相關資訊 定位問題原因:
此處「第 100 行」指的是 header() 呼叫 失敗的腳本。
括號內的「輸出開始於」註解更為重要。 它表示先前輸出的來源。在此範例中,它是 auth.php# 和第52#行。這就是您必須尋找過早輸出的地方。
auth.php
52
典型原因:
print 和 echo 語句的有意輸出將終止發送 HTTP 標頭的機會。必須重組應用程式流程以避免這種情況。使用函數 和模板方案。確保 header() 呼叫發生在訊息之前 都寫出來了。
產生輸出的函數包括
列印
vprintf
#trigger_error
ob_flush
ob_end_flush
var_dump
print_r
flush
imagepng
imagejpeg
以及其他和使用者定義的函數。
.php 檔案中未解析的 HTML 部分也是直接輸出。 必須注意將觸發 header() 呼叫的腳本條件 在任何原始區塊之前。
使用模板方案將處理與輸出邏輯分開。
之前的空格表示「script.php 第 1 行」警告
#如果警告引用內聯輸出1,那麼主要是 開頭 標記之前的前導空格、文字或 HTML。
1
標記之前的前導空格、文字或 HTML。
類似地,附加腳本或腳本部分也可能發生這種情況: ?> PHP 實際上會在關閉標籤後佔用一個單一換行符號。但它不會 補償移入此類間隙的多個換行符、製表符或空格。
類似地,附加腳本或腳本部分也可能發生這種情況:
?> PHP 實際上會在關閉標籤後佔用一個單一換行符號。但它不會 補償移入此類間隙的多個換行符、製表符或空格。
PHP 實際上會在關閉標籤後佔用一個單一換行符號。但它不會 補償移入此類間隙的多個換行符、製表符或空格。
##僅換行符和空格就可能是一個問題。但也有“看不見的” 可能導致這種情況的字元序列。最著名的是 UTF-8 BOM(位元組順序標記) 大多數文字編輯器不會顯示它。它是一個位元組序列 EF BB BF,對於 UTF-8 編碼的文檔來說,它是可選且冗餘的。然而 PHP 必須將其視為原始輸出。它可能在輸出中顯示為字元  (如果客戶端將文件解釋為 Latin-1)或類似的「垃圾」。

特別是圖形編輯器和基於 Java 的 IDE 並沒有註意到它 在場。他們沒有將其視覺化(Unicode 標準規定)。 然而,大多數程式設計師和控制台編輯器都會:
這樣很容易儘早發現問題。其他編輯可能會識別 它存在於檔案/設定選單中(Windows 上的 Notepad 可以識別並 解決問題), 檢查 BOM 存在的另一個選擇是使用十六進位編輯器。 在 *nix 系統上 hexdump 通常可用, 如果不是簡化審核這些問題和其他問題的圖形變體:
hexdump
一個簡單的解決方法是將文字編輯器設定為將檔案儲存為“UTF-8(無 BOM)” 或類似這樣的命名法。通常,新手會求助於建立新文件,然後將先前的程式碼複製並貼上回來。
還有自動化工具來檢查和重寫文字文件 (sed/awk代码> 或重新編碼)。 對於 PHP,特別是 phptags 標記 tidier。 它將關閉和打開標籤重寫為長和短形式,而且也很容易 修正了前導和尾隨空格、Unicode 和 UTF-x BOM 問題:
sed
awk代码>
重新編碼
phptags
phptags --whitespace *.php
在整個包含或專案目錄上使用是安全的。
後面有空格? >
如果後面提到了錯誤來源 關閉 ?># 那麼這就是一些空白或原始文本被寫出的地方。 PHP 結束標記此時不會終止腳本執行。其後的任何文字/空格字元都會作為頁面內容寫出 仍然。
通常建議,特別是對於新手,尾隨 ?> PHP 應省略關閉標籤。這避開了這些案例中的一小部分。 (很常見 include()d 腳本是罪魁禍首。)
include()d
如果沒有錯誤來源,通常是 PHP 擴充或 php.ini 設定 具體化了。
gzip
ob_gzhandler
extension=
如果另一個 PHP 語句或表達式導致警告訊息或 注意被印出來,這也算是過早輸出。
在這種情況下,您需要避免錯誤, 延遲語句執行,或使用例如抑制訊息 isset() 或 @()# - 當任何一個都不會妨礙稍後的調試時。
isset()
@()
如果您根據 php.ini 停用了 error_reporting 或 display_errors, 那就不會出現任何警告。但忽略錯誤並不能解決問題 離開。過早輸出後仍然無法發送標頭。
php.ini
error_reporting
因此,當 header("Location: ...") 重定向默默失敗時,這是非常嚴重的 建議探測警告。使用兩個簡單的命令重新啟用它們 在呼叫腳本之上:
header("Location: ...")
error_reporting(E_ALL); ini_set("display_errors", 1);
或set_error_handler("var_dump");如果其他方法都失敗了。
set_error_handler("var_dump");
說到重定向標頭,您應該經常使用這樣的習慣用法 這是最終的程式碼路徑:
exit(header("Location: /finished.html"));
最好是一個列印用戶訊息的實用函數 如果 header() 失敗。
PHP 輸出緩衝 是緩解此問題的解決方法。它通常工作可靠,但不應該 取代正確的應用程式結構並將輸出與控制分開 邏輯。它的實際目的是最大限度地減少到網路伺服器的分塊傳輸。
output_buffering=# 不過,設定還是有幫助的。 在 php.ini 中配置它 或透過 .htaccess 甚至 .user.ini 現代 FPM/FastCGI 設定。 啟用它將允許 PHP 緩衝輸出,而不是立即將其傳遞到網頁伺服器。 PHP 因此可以聚合 HTTP 標頭。
output_buffering=
它同樣可以透過呼叫 ob_start(); 在呼叫腳本之上。然而,由於多種原因,它不太可靠:
ob_start();
即使 開始第一個腳本,空格或 BOM 可能會在渲染之前被打亂無效。
它可以隱藏 HTML 輸出的空白。但是,一旦應用程式邏輯嘗試發送二進位內容(例如生成的圖像), 緩衝的無關輸出成為一個問題。 (需要ob_clean()) 作為進一步的解決方法。 )
緩衝區的大小有限,如果保留預設值,很容易溢位。 這種情況也不少見,很難追蹤一个> 當它發生時。
因此,這兩種方法都可能變得不可靠 - 特別是在兩者之間切換時 開發設定和/或生產伺服器。這就是為什麼輸出緩衝是 廣泛認為只是一個拐杖/嚴格來說是一種解決方法。
另請參閱基本用法範例 在手冊中,以及更多優點和缺點:
如果您之前沒有收到標頭警告,則輸出緩衝 php.ini 設定 已經改變。當前/新伺服器上可能未配置它。
headers_sent()檢查
您總是可以使用 headers_sent() 來偵測是否 仍然可以...發送標頭。這對於有條件列印很有用 資訊或應用其他後備邏輯。
headers_sent()
if (headers_sent()) { die("Redirect failed. Please click on this link: "); } else{ exit(header("Location: /user.php")); }
有用的後備解決方法是:
如果您的應用程式在結構上很難修復,那麼一個簡單的(但 有點不專業)允許重定向的方法是注入 HTML 標籤。可以透過以下方式實現重定向:
或短暫延遲:
當使用超過 部分時,這會導致無效的 HTML。 大多數瀏覽器仍然接受它。
作為替代方案,JavaScript 重定向 可用於頁面重定向:
sssccc
雖然這通常比 解決方法更符合 HTML, 它會導致對支援 JavaScript 的客戶端的依賴。
然而,當真正的 HTTP header() 時,這兩種方法都會產生可接受的後備 呼叫失敗。理想情況下,您總是將其與用戶友好的訊息結合起來, 作為最後手段的可點擊連結。 (例如,http_redirect() PECL 擴充確實如此。 )
session_start()
setcookie() 和 session_start() 都需要傳送 Set-Cookie: HTTP 標頭。 因此,適用相同的條件,並將產生類似的錯誤訊息 用於過早輸出的情況。
Set-Cookie:
(當然,它們也受到瀏覽器中禁用 cookie 的影響 甚至代理問題。會話功能顯然也依賴免費 磁碟空間和其他 php.ini 設定等)
在傳送 HTTP 標頭之前發送任何內容(使用
setcookie
或header
)。在 HTTP 標頭之前輸出某些內容的常見原因是:意外的空格,通常出現在檔案的開頭或結尾,如下所示:
為了避免這種情況,而省略結尾的
?>
- 無論如何都不需要。3F 3C
開頭。您可以安全地從檔案開頭刪除 BOMEF BB BF
。echo
、printf
、readfile
、passthru
、之前的程式碼>
等display_errors
php.ini屬性已設定。 php 不會因程式設計師錯誤而崩潰,而是默默地修復錯誤並發出警告。雖然您可以修改display_errors
或 error_reporting 配置,您應該解決問題。常見原因是存取數組中未定義的元素(例如
$_POST['input']
而不使用empty
## 或isset進行測試是否設定了輸入),或使用未定義的常數而不是字串文字(如$_POST[input]
中,請注意缺少的引號)。
輸出緩衝應該可以解決問題;呼叫ob_start
然而,雖然輸出緩衝可以避免這些問題,但您應該真正確定應用程式在 HTTP 標頭之前輸出 HTTP 正文的原因。這就像接電話並討論你的一天和天氣,然後告訴打電話的人他撥錯了號碼。後的所有輸出都緩衝在記憶體中,直到您釋放緩衝區,例如與ob_end_flush
。
發送標頭之前沒有輸出!
在進行任何輸出之前,必須呼叫發送/修改 HTTP 標頭的函數。 摘要⇊# 否則呼叫失敗:
修改 HTTP 標頭的一些函數是:
標題
/header_remove
session_start
/session_regenerate_id
setcookie
/setrawcookie
輸出可以是:
無意:
# 之前或
?>
之後的空格故意:
#print
、echo
和其他產生輸出的函數程式碼之前的原始
部分。
為什麼會發生這種情況?
要理解為什麼必須在輸出之前發送標頭,這是必要的 查看典型的 HTTP 回覆. PHP腳本主要產生HTML內容,同時也傳遞一個 傳送到網頁伺服器的 HTTP/CGI 標頭集:
頁面/輸出總是跟隨標題。 PHP 必須透過 首先將標頭髮送到網頁伺服器。它只能這樣做一次。 雙換行之後就再也不能修改它們了。
當 PHP 收到第一個輸出(
print
、echo
、)時,它將 刷新所有收集的標頭。之後它可以發送所有輸出 它想要。但是發送進一步的 HTTP 標頭是不可能的。
如何找出過早輸出發生的位置?
header()
警告包含所有相關資訊 定位問題原因:此處「第 100 行」指的是
header()
呼叫 失敗的腳本。括號內的「輸出開始於」註解更為重要。 它表示先前輸出的來源。在此範例中,它是
auth.php
# 和第52
#行。這就是您必須尋找過早輸出的地方。典型原因:
#列印、回顯
print
和echo
語句的有意輸出將終止發送 HTTP 標頭的機會。必須重組應用程式流程以避免這種情況。使用函數 和模板方案。確保header()
呼叫發生在訊息之前 都寫出來了。產生輸出的函數包括
列印
、echo
、printf
、vprintf
#trigger_error
、ob_flush
、ob_end_flush
、var_dump
、print_r
readfile
、passthru
、flush
、imagepng
、imagejpeg
以及其他和使用者定義的函數。
原始 HTML 區域
.php 檔案中未解析的 HTML 部分也是直接輸出。 必須注意將觸發
header()
呼叫的腳本條件 在任何原始區塊之前。
之前的空格表示「script.php 第 1 行」警告
#如果警告引用內聯輸出
1
,那麼主要是 開頭標記之前的前導空格、文字或 HTML。
UTF-8 BOM
##僅換行符和空格就可能是一個問題。但也有“看不見的” 可能導致這種情況的字元序列。最著名的是 UTF-8 BOM(位元組順序標記) 大多數文字編輯器不會顯示它。它是一個位元組序列
EF BB BF
,對於 UTF-8 編碼的文檔來說,它是可選且冗餘的。然而 PHP 必須將其視為原始輸出。它可能在輸出中顯示為字元
(如果客戶端將文件解釋為 Latin-1)或類似的「垃圾」。特別是圖形編輯器和基於 Java 的 IDE 並沒有註意到它 在場。他們沒有將其視覺化(Unicode 標準規定)。 然而,大多數程式設計師和控制台編輯器都會:
這樣很容易儘早發現問題。其他編輯可能會識別 它存在於檔案/設定選單中(Windows 上的 Notepad 可以識別並 解決問題), 檢查 BOM 存在的另一個選擇是使用十六進位編輯器。 在 *nix 系統上
hexdump
通常可用, 如果不是簡化審核這些問題和其他問題的圖形變體:一個簡單的解決方法是將文字編輯器設定為將檔案儲存為“UTF-8(無 BOM)” 或類似這樣的命名法。通常,新手會求助於建立新文件,然後將先前的程式碼複製並貼上回來。
修正實用程式
還有自動化工具來檢查和重寫文字文件 (
sed
/awk代码>
或重新編碼
)。 對於 PHP,特別是phptags
標記 tidier。 它將關閉和打開標籤重寫為長和短形式,而且也很容易 修正了前導和尾隨空格、Unicode 和 UTF-x BOM 問題:在整個包含或專案目錄上使用是安全的。
後面有空格? >
如果後面提到了錯誤來源 關閉
?>
# 那麼這就是一些空白或原始文本被寫出的地方。 PHP 結束標記此時不會終止腳本執行。其後的任何文字/空格字元都會作為頁面內容寫出 仍然。通常建議,特別是對於新手,尾隨
?>
PHP 應省略關閉標籤。這避開了這些案例中的一小部分。 (很常見include()d
腳本是罪魁禍首。)錯誤來源稱為「第 0 行未知」
如果沒有錯誤來源,通常是 PHP 擴充或 php.ini 設定 具體化了。
gzip
流編碼設定 或ob_gzhandler
。extension=
模組 產生隱式 PHP 啟動/警告訊息。前面的錯誤訊息
如果另一個 PHP 語句或表達式導致警告訊息或 注意被印出來,這也算是過早輸出。
在這種情況下,您需要避免錯誤, 延遲語句執行,或使用例如抑制訊息
isset()
或@()
# - 當任何一個都不會妨礙稍後的調試時。沒有錯誤訊息
如果您根據
php.ini
停用了error_reporting
或display_errors
, 那就不會出現任何警告。但忽略錯誤並不能解決問題 離開。過早輸出後仍然無法發送標頭。因此,當
header("Location: ...")
重定向默默失敗時,這是非常嚴重的 建議探測警告。使用兩個簡單的命令重新啟用它們 在呼叫腳本之上:或
set_error_handler("var_dump");
如果其他方法都失敗了。說到重定向標頭,您應該經常使用這樣的習慣用法 這是最終的程式碼路徑:
最好是一個列印用戶訊息的實用函數 如果
header()
失敗。輸出緩衝作為解決方法
PHP 輸出緩衝 是緩解此問題的解決方法。它通常工作可靠,但不應該 取代正確的應用程式結構並將輸出與控制分開 邏輯。它的實際目的是最大限度地減少到網路伺服器的分塊傳輸。
output_buffering=
# 不過,設定還是有幫助的。 在 php.ini 中配置它 或透過 .htaccess 甚至 .user.ini 現代 FPM/FastCGI 設定。啟用它將允許 PHP 緩衝輸出,而不是立即將其傳遞到網頁伺服器。 PHP 因此可以聚合 HTTP 標頭。
它同樣可以透過呼叫
ob_start();
在呼叫腳本之上。然而,由於多種原因,它不太可靠:即使
開始第一個腳本,空格或 BOM 可能會在渲染之前被打亂無效。
它可以隱藏 HTML 輸出的空白。但是,一旦應用程式邏輯嘗試發送二進位內容(例如生成的圖像), 緩衝的無關輸出成為一個問題。 (需要ob_clean()) 作為進一步的解決方法。 )
緩衝區的大小有限,如果保留預設值,很容易溢位。 這種情況也不少見,很難追蹤一个> 當它發生時。
因此,這兩種方法都可能變得不可靠 - 特別是在兩者之間切換時 開發設定和/或生產伺服器。這就是為什麼輸出緩衝是 廣泛認為只是一個拐杖/嚴格來說是一種解決方法。
另請參閱基本用法範例 在手冊中,以及更多優點和缺點:
但它在其他伺服器上工作! ?
如果您之前沒有收到標頭警告,則輸出緩衝 php.ini 設定 已經改變。當前/新伺服器上可能未配置它。
使用
headers_sent()檢查
您總是可以使用
headers_sent()
來偵測是否 仍然可以...發送標頭。這對於有條件列印很有用 資訊或應用其他後備邏輯。有用的後備解決方法是:
HTML
標籤
如果您的應用程式在結構上很難修復,那麼一個簡單的(但 有點不專業)允許重定向的方法是注入 HTML
標籤。可以透過以下方式實現重定向:
或短暫延遲:
當使用超過
部分時,這會導致無效的 HTML。 大多數瀏覽器仍然接受它。
JavaScript 重定向
作為替代方案,JavaScript 重定向 可用於頁面重定向:
雖然這通常比
解決方法更符合 HTML, 它會導致對支援 JavaScript 的客戶端的依賴。
然而,當真正的 HTTP header() 時,這兩種方法都會產生可接受的後備 呼叫失敗。理想情況下,您總是將其與用戶友好的訊息結合起來, 作為最後手段的可點擊連結。 (例如,http_redirect() PECL 擴充確實如此。 )
為什麼
setcookie()
和session_start()
也受到影響setcookie()
和session_start()
都需要傳送Set-Cookie:
HTTP 標頭。 因此,適用相同的條件,並將產生類似的錯誤訊息 用於過早輸出的情況。(當然,它們也受到瀏覽器中禁用 cookie 的影響 甚至代理問題。會話功能顯然也依賴免費 磁碟空間和其他 php.ini 設定等)
更多連結