搜尋

首頁  >  問答  >  主體

解決PHP中的「Headers already sent」錯誤的方法

在執行我的腳本時,我收到幾個如下錯誤:

警告:無法修改標頭資訊- 標頭已由/some/file.php 中的(輸出從/some/file.php:12 開始)發送<第23行<第23行

錯誤訊息中提到的行包含 header()setcookie() 呼叫。

這可能是什麼原因?又該如何解決呢?

P粉210405394P粉210405394416 天前567

全部回覆(2)我來回復

  • P粉071602406

    P粉0716024062023-10-10 11:29:08

    在傳送 HTTP 標頭之前發送任何內容(使用 setcookieheader)。在 HTTP 標頭之前輸出某些內容的常見原因是:

    • 意外的空格,通常出現在檔案的開頭或結尾,如下所示:

           為了避免這種情況,而省略結尾的 ?> - 無論如何都不需要。

    開啟

    輸出緩衝應該可以解決問題;呼叫ob_start 後的所有輸出都緩衝在記憶體中,直到您釋放緩衝區,例如與ob_end_flush

    然而,雖然輸出緩衝可以避免這些問題,但您應該真正確定應用程式在 HTTP 標頭之前輸出 HTTP 正文的原因。這就像接電話並討論你的一天和天氣,然後告訴打電話的人他撥錯了號碼。

    回覆
    0
  • P粉087951442

    P粉0879514422023-10-10 09:54:05

    發送標頭之前沒有輸出!

    在進行任何輸出之前,必須呼叫發送/修改 HTTP 標頭的函數摘要⇊# 否則呼叫失敗:

    修改 HTTP 標頭的一些函數是:

    輸出可以是:

    • 故意:

      • #printecho 和其他產生輸出的函數
      • 程式碼之前的原始 部分。

    為什麼會發生這種情況?

    要理解為什麼必須在輸出之前發送標頭,這是必要的 查看典型的 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

    頁面/輸出總是跟隨標題。 PHP 必須透過 首先將標頭髮送到網頁伺服器。它只能這樣做一次。 雙換行之後就再也不能修改它們了。

    當 PHP 收到第一個輸出(printecho)時,它將 刷新所有收集的標頭。之後它可以發送所有輸出 它想要。但是發送進一步的 HTTP 標頭是不可能的。

    如何找出過早輸出發生的位置?

    header() 警告包含所有相關資訊 定位問題原因:

    此處「第 100 行」指的是 header() 呼叫 失敗的腳本。

    括號內的「輸出開始於」註解更為重要。 它表示先前輸出的來源。在此範例中,它是 auth.php# 和52#行。這就是您必須尋找過早輸出的地方。

    典型原因:

    1. #列印、回顯

      printecho 語句的有意輸出將終止發送 HTTP 標頭的機會。必須重組應用程式流程以避免這種情況。使用函數 和模板方案。確保 header() 呼叫發生在訊息之前 都寫出來了。

      產生輸出的函數包括

      • 列印echoprintfvprintf
      • #trigger_errorob_flushob_end_flushvar_dumpprint_r
      • readfilepassthruflushimagepngimagejpeg


      以及其他和使用者定義的函數。

    2. 原始 HTML 區域

      .php 檔案中未解析的 HTML 部分也是直接輸出。 必須注意將觸發 header() 呼叫的腳本條件 在任何原始區塊之前。

      
      
      

      使用模板方案將處理與輸出邏輯分開。

      • 將表單處理程式碼置於腳本之上。
      • 使用臨時字串變數來延遲訊息。
      • 實際的輸出邏輯和混合的 HTML 輸出應該放在最後。

    3. 之前的空格表示「script.php 第 1 行」警告

      #如果警告引用內聯輸出1,那麼主要是 開頭 標記之前的前導空格、文字或 HTML。

      
      

      類似地,附加腳本或腳本部分也可能發生這種情況:

      ?>
      
      
      

      PHP 實際上會在關閉標籤後佔用一個單一換行符號。但它不會 補償移入此類間隙的多個換行符、製表符或空格。

    4. 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 問題:

      phptags  --whitespace  *.php

      在整個包含或專案目錄上使用是安全的。

    5. 後面有空格? >

      如果後面提到了錯誤來源 關閉 ?># 那麼這就是一些空白或原始文本被寫出的地方。 PHP 結束標記此時不會終止腳本執行。其後的任何文字/空格字元都會作為頁面內容寫出 仍然。

      通常建議,特別是對於新手,尾隨 ?> PHP 應省略關閉標籤。這避開了這些案例中的一小部分。 (很常見 include()d 腳本是罪魁禍首。)

    6. 錯誤來源稱為「第 0 行未知」

      如果沒有錯誤來源,通常是 PHP 擴充或 php.ini 設定 具體化了。

      • 有時是 gzip 流編碼設定 ob_gzhandler
      • 但它也可以是任何雙重載入的 extension= 模組 產生隱式 PHP 啟動/警告訊息。

    7. 前面的錯誤訊息

      如果另一個 PHP 語句或表達式導致警告訊息或 注意被印出來,這也算是過早輸出。

      在這種情況下,您需要避免錯誤, 延遲語句執行,或使用例如抑制訊息 isset()@()# - 當任何一個都不會妨礙稍後的調試時。

    沒有錯誤訊息

    如果您根據 php.ini 停用了 error_reportingdisplay_errors, 那就不會出現任何警告。但忽略錯誤並不能解決問題 離開。過早輸出後仍然無法發送標頭。

    因此,當 header("Location: ...") 重定向默默失敗時,這是非常嚴重的 建議探測警告。使用兩個簡單的命令重新啟用它們 在呼叫腳本之上:

    error_reporting(E_ALL);
    ini_set("display_errors", 1);

    set_error_handler("var_dump");如果其他方法都失敗了。

    說到重定向標頭,您應該經常使用這樣的習慣用法 這是最終的程式碼路徑:

    exit(header("Location: /finished.html"));

    最好是一個列印用戶訊息的實用函數 如果 header() 失敗。

    輸出緩衝作為解決方法

    PHP 輸出緩衝 是緩解此問題的解決方法。它通常工作可靠,但不應該 取代正確的應用程式結構並將輸出與控制分開 邏輯。它的實際目的是最大限度地減少到網路伺服器的分塊傳輸。

    1. output_buffering=# 不過,設定還是有幫助的。 在 php.ini 中配置它 或透過 .htaccess 甚至 .user.ini 現代 FPM/FastCGI 設定。
      啟用它將允許 PHP 緩衝輸出,而不是立即將其傳遞到網頁伺服器。 PHP 因此可以聚合 HTTP 標頭。

    2. 它同樣可以透過呼叫 ob_start(); 在呼叫腳本之上。然而,由於多種原因,它不太可靠:

      • 即使 開始第一個腳本,空格或 BOM 可能會在渲染之前被打亂無效

      • 它可以隱藏 HTML 輸出的空白。但是,一旦應用程式邏輯嘗試發送二進位內容(例如生成的圖像), 緩衝的無關輸出成為一個問題。 (需要ob_clean()) 作為進一步的解決方法。 )

      • 緩衝區的大小有限,如果保留預設值,很容易溢位。 這種情況也不少見,很難追蹤 當它發生時。

    因此,這兩種方法都可能變得不可靠 - 特別是在兩者之間切換時 開發設定和/或生產伺服器。這就是為什麼輸出緩衝是 廣泛認為只是一個拐杖/嚴格來說是一種解決方法。

    另請參閱基本用法範例 在手冊中,以及更多優點和缺點:

    但它在其他伺服器上工作! ?

    如果您之前沒有收到標頭警告,則輸出緩衝 php.ini 設定 已經改變。當前/新伺服器上可能未配置它。

    使用headers_sent()檢查

    您總是可以使用 headers_sent() 來偵測是否 仍然可以...發送標頭。這對於有條件列印很有用 資訊或應用其他後備邏輯。

    if (headers_sent()) {
        die("Redirect failed. Please click on this link: ");
    }
    else{
        exit(header("Location: /user.php"));
    }

    有用的後備解決方法是:

    然而,當真正的 HTTP header() 時,這兩種方法都會產生可接受的後備 呼叫失敗。理想情況下,您總是將其與用戶友好的訊息結合起來, 作為最後手段的可點擊連結。 (例如,http_redirect() PECL 擴充確實如此。 )

    為什麼setcookie()session_start()也受到影響

    setcookie()session_start() 都需要傳送 Set-Cookie: HTTP 標頭。 因此,適用相同的條件,並將產生類似的錯誤訊息 用於過早輸出的情況。

    (當然,它們也受到瀏覽器中禁用 cookie 的影響 甚至代理問題。會話功能顯然也依賴免費 磁碟空間和其他 php.ini 設定等)

    更多連結

    回覆
    0
  • 取消回覆