ไม่มีเอาต์พุตก่อนส่งส่วนหัว!
ฟังก์ชั่นที่ส่ง / ปรับเปลี่ยนส่วนหัว HTTP จะต้องถูกเรียกก่อนที่จะส่งออกใด ๆ ที่จะทำ
summary ⇊
มิฉะนั้นการโทรจะล้มเหลว:
คำเตือน: ไม่สามารถแก้ไขข้อมูลส่วนหัว - ส่วนหัวที่ส่งไปแล้ว (เอาต์พุตเริ่มต้นที่สคริปต์: บรรทัด )
บางฟังก์ชั่นการปรับเปลี่ยนส่วนหัว HTTP คือ:
ผลผลิตสามารถ:
เจตนา:
print
, echo
และฟังก์ชั่นอื่น ๆ การผลิตการส่งออก
- รหัสดิบ
<html>
ส่วนก่อน<?php
ทำไมมันเกิดขึ้น
เพื่อให้เข้าใจว่าเหตุใดจึงต้องส่งส่วนหัวก่อนออกผลลัพธ์จำเป็นต้องดูการ
ตอบกลับ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
<html><head><title>PHP page output page</title></head>
<body><h1>Content</h1> <p>Some more output follows...</p>
and <a href="/"> <img src=internal-icon-delayed> </a>
หน้า / ผลลัพธ์จะตามหลังเสมอหลังหัวPHP ต้องผ่านส่วนหัวไปยังเว็บเซิร์ฟเวอร์ก่อน มันสามารถทำได้เพียงครั้งเดียว หลังจากการแบ่งแถวสองครั้งมันไม่สามารถแก้ไขได้อีก
เมื่อ PHP ได้รับการส่งออกครั้งแรก ( print
, echo
, <html>
) มันจะ
ล้างหัวเก็บรวบรวมทั้งหมด หลังจากนั้นก็สามารถส่งออกทั้งหมดที่ต้องการ แต่การส่งส่วนหัว HTTP เพิ่มเติมนั้นเป็นไปไม่ได้
คุณจะทราบได้อย่างไรว่าเกิดอะไรขึ้นกับการคลอดก่อนกำหนด?
header()
เตือนมีข้อมูลที่เกี่ยวข้องทั้งหมดเพื่อหาสาเหตุปัญหา:
คำเตือน: ไม่สามารถแก้ไขข้อมูลส่วนหัว - ส่วนหัวที่ส่งไปแล้ว
(เอาต์พุตเริ่มต้นที่ / www / usr2345 / htdocs / auth.php: 52 ) ใน /www/usr2345/htdocs/index.php ที่บรรทัด 100
ที่นี่ "บรรทัด 100" หมายถึงสคริปต์ที่การheader()
ร้องขอล้มเหลว
หมายเหตุ " เอาต์พุตเริ่มต้นที่ " ภายในวงเล็บมีความสำคัญมากกว่า มันเป็นแหล่งที่มาของการส่งออกก่อนหน้า ในตัวอย่างนี้มันauth.php
และบรรทัด 52
นั่นคือสิ่งที่คุณต้องมองหาผลลัพธ์ก่อนกำหนด
สาเหตุทั่วไป:
พิมพ์ echo
เอาต์พุตโดยเจตนาจากprint
และecho
ข้อความสั่งจะยุติโอกาสในการส่งส่วนหัว HTTP โฟลว์แอปพลิเคชันต้องถูกปรับโครงสร้างใหม่เพื่อหลีกเลี่ยงปัญหานั้น ใช้ฟังก์ชั่น
และแผนการสร้างเทมเพลต ให้แน่ใจheader()
ว่ามีการโทรเกิดขึ้นก่อนข้อความจะถูกเขียนออกมา
ฟังก์ชั่นที่ผลิตผลผลิตรวมถึง
print
, echo
, printf
,vprintf
trigger_error
, ob_flush
, ob_end_flush
, var_dump
,print_r
readfile
, passthru
, flush
, imagepng
,imagejpeg
ในหมู่ผู้อื่นและฟังก์ชั่นที่ผู้ใช้กำหนด
พื้นที่ HTML ดิบ
ส่วน HTML ที่ไม่ได้แยกวิเคราะห์ใน.php
ไฟล์เป็นเอาต์พุตโดยตรงเช่นกัน เงื่อนไขของสคริปต์ที่จะทริกเกอร์การheader()
โทรต้องถูกบันทึกไว้ก่อนบล็อกดิบใด ๆ<html>
<!DOCTYPE html>
<?php
// Too late for headers already.
ใช้ชุดรูปแบบ templating เพื่อแยกการประมวลผลจากตรรกะเอาต์พุต
- วางโค้ดการประมวลผลแบบฟอร์มบนสคริปต์
- ใช้ตัวแปรสตริงชั่วคราวเพื่อเลื่อนข้อความ
- ตรรกะเอาท์พุทที่เกิดขึ้นจริงและเอาท์พุท HTML ผสมควรจะปฏิบัติตามล่าสุด
ช่องว่างก่อน<?php
สำหรับ "script.php บรรทัด 1คำเตือน "
หากคำเตือนหมายถึงเอาต์พุตในบรรทัดแสดง1
ว่าส่วนใหญ่จะเป็นช่องว่างนำหน้าข้อความหรือ HTML หน้า<?php
โทเค็นการเปิด
<?php
# There's a SINGLE space/newline before <? - Which already seals it.
ในทำนองเดียวกันมันสามารถเกิดขึ้นได้สำหรับสคริปต์ต่อท้ายหรือส่วนของสคริปต์:
?>
<?php
PHP กินค่าการทำลายบรรทัดเดียวหลังจากแท็กปิด แต่จะไม่ชดเชยบรรทัดใหม่หรือแท็บหรือช่องว่างหลายรายการที่เปลี่ยนเป็นช่องว่างดังกล่าว
UTF-8 BOM
Linebreaks และช่องว่างเพียงอย่างเดียวอาจเป็นปัญหาได้ แต่ยังมีลำดับอักขระที่ "มองไม่เห็น" ซึ่งอาจทำให้เกิดสิ่งนี้ ส่วนใหญ่ชื่อเสียง
UTF-8 BOM (Byte-Order-Mark)
ซึ่งไม่ได้แสดงโดยแก้ไขข้อความมากที่สุด เป็นลำดับไบต์EF BB BF
ซึ่งเป็นทางเลือกและซ้ำซ้อนสำหรับเอกสารที่เข้ารหัส UTF-8 อย่างไรก็ตาม PHP ต้องถือว่ามันเป็นผลดิบ มันอาจปรากฏเป็นตัวละคร
ในผลลัพธ์ (ถ้าไคลเอนต์ตีความเอกสารเป็นละติน -1) หรือ "ขยะ" ที่คล้ายกัน
โดยเฉพาะตัวแก้ไขกราฟิกและ IDEs ที่ใช้ Java จะไม่สนใจสถานะของมัน พวกเขาไม่เห็นภาพ (บังคับโดยมาตรฐาน Unicode) โปรแกรมเมอร์และคอนโซลส่วนใหญ่อย่างไรก็ตาม:
มันง่ายที่จะรับรู้ปัญหาก่อน บรรณาธิการอื่น ๆ อาจระบุว่ามีอยู่ในเมนูไฟล์ / การตั้งค่า (Notepad ++ บน Windows สามารถระบุและ
แก้ไขปัญหาที่เกิดขึ้น ) ตัวเลือกในการตรวจสอบการปรากฏ BOMs ก็คือการหันไปยังHexEditor hexdump
โดยปกติแล้วในระบบ * nix จะใช้งานได้หากไม่ใช่ตัวแปรแบบกราฟิกซึ่งช่วยให้การตรวจสอบง่ายขึ้นและปัญหาอื่น ๆ :
การแก้ไขที่ง่ายคือการตั้งค่าเครื่องมือแก้ไขข้อความเพื่อบันทึกไฟล์เป็น "UTF-8 (ไม่มี BOM)" หรือชื่อที่คล้ายกัน ผู้ใช้ใหม่มักจะหันไปใช้การสร้างไฟล์ใหม่และเพียงแค่คัดลอกและวางโค้ดก่อนหน้านี้อีกครั้ง
ยูทิลิตี้การแก้ไข
นอกจากนี้ยังมีเครื่องมืออัตโนมัติเพื่อตรวจสอบและเขียนไฟล์ข้อความ ( sed
/awk
หรือrecode
) สำหรับ PHP เฉพาะมีของtidierphptags
แท็ก มันเขียนแท็กปิดและเปิดในรูปแบบที่ยาวและสั้น แต่ยังสามารถแก้ไขช่องว่างชั้นนำและต่อท้ายปัญหา Unicode และ UTF-x BOM ได้อย่างง่ายดาย:
phptags --whitespace *.php
มันมีเหตุผลที่จะใช้ในไดเรกทอรีรวมหรือโครงการ
ช่องว่างหลังจาก ?>
หากแหล่งที่มาของข้อผิดพลาดที่กล่าวถึงเป็นหลัง
ปิด?>
นี่คือที่ช่องว่างหรือข้อความดิบถูกเขียนออกมา เครื่องหมายสิ้นสุด PHP ไม่ยุติการทำงานของสคริปต์ในตอนนี้ อักขระข้อความ / ช่องว่างใด ๆ หลังจากนั้นจะถูกเขียนเป็นเนื้อหาของหน้าเว็บ
แนะนำโดยเฉพาะอย่างยิ่งกับผู้มาใหม่ที่?>
ควรหลีกเลี่ยงแท็กปิด PHP ต่อท้าย นี้หลีกเลี่ยงส่วนเล็ก ๆ ของกรณีเหล่านี้ ค่อนข้างบ่อยinclude()d
สคริปต์เป็นผู้ร้าย)
แหล่งข้อผิดพลาดที่กล่าวถึงเป็น "ไม่รู้จักในบรรทัด 0"
โดยทั่วไปแล้วจะเป็นการตั้งค่าส่วนขยาย PHP หรือ php.ini หากไม่มีการแก้ไขข้อผิดพลาดที่มา
- เป็นการ
gzip
ตั้งค่าการเข้ารหัสสตรีม
เป็นครั้งคราวหรือob_gzhandler
หรือ
- แต่มันก็อาจจะเป็น
extension=
โมดูลที่โหลดเป็นสองเท่าสร้างข้อความเริ่มต้น / เตือน PHP โดยปริยาย
ก่อนข้อความผิดพลาด
หากคำสั่งหรือนิพจน์ PHP อื่นทำให้ข้อความเตือนหรือการแจ้งเตือนถูกพิมพ์ออกมาสิ่งนั้นจะนับรวมเป็นเอาต์พุตก่อนกำหนด
ในกรณีนี้คุณต้องหลีกเลี่ยงข้อผิดพลาดชะลอการดำเนินการคำสั่งหรือระงับข้อความด้วยเช่น
isset()
หรือ@()
- เมื่อไม่ขัดขวางการแก้ไขข้อบกพร่องในภายหลัง
ไม่มีข้อความแสดงข้อผิดพลาด
หากคุณมีerror_reporting
หรือdisplay_errors
ปิดใช้งานต่อphp.ini
จะไม่มีคำเตือนปรากฏขึ้น แต่การเพิกเฉยข้อผิดพลาดจะไม่ทำให้ปัญหาหายไป ส่วนหัวยังคงไม่สามารถส่งหลังจากการส่งออกก่อนกำหนด
ดังนั้นเมื่อการheader("Location: ...")
เปลี่ยนเส้นทางล้มเหลวอย่างเงียบ ๆ ขอแนะนำให้ตรวจสอบคำเตือน เปิดใช้งานพวกเขาอีกครั้งด้วยสองคำสั่งง่ายๆเหนือสคริปต์การเรียกใช้:
error_reporting(E_ALL);
ini_set("display_errors", 1);
หรือ set_error_handler("var_dump");
ถ้าทุกอย่างอื่นล้มเหลว
เมื่อพูดถึงส่วนหัวของการเปลี่ยนเส้นทางคุณควรใช้สำนวนเช่นนี้เพื่อรับรหัสสุดท้าย:
exit(header("Location: /finished.html"));
ยิ่งกว่านั้นคือฟังก์ชั่นยูทิลิตี้ที่พิมพ์ข้อความผู้ใช้ในกรณีที่ header()
ความล้มเหลว
บัฟเฟอร์ออกเป็นวิธีแก้ปัญหา
การบัฟเฟอร์เอาต์พุต PHPs
เป็นวิธีแก้ไขปัญหาเพื่อบรรเทาปัญหานี้ มันมักจะทำงานได้อย่างน่าเชื่อถือ แต่ไม่ควรแทนที่การสร้างแอปพลิเคชันที่เหมาะสมและแยกออกจากตรรกะการควบคุม วัตถุประสงค์ที่แท้จริงคือลดการถ่ายโอนข้อมูลจำนวนน้อยไปยังเว็บเซิร์ฟเวอร์
การoutput_buffering=
ตั้งค่าสามารถช่วยได้ กำหนดค่าในphp.ini
หรือผ่าน. htaccess
หรือ . user.iniในการตั้งค่า FPM / FastCGI ที่ทันสมัย
การเปิดใช้งานจะช่วยให้ PHP สามารถบัฟเฟอร์เอาต์พุตแทนที่จะส่งไปยังเว็บเซิร์ฟเวอร์ทันที PHP จึงสามารถรวมส่วนหัว HTTP
นอกจากนี้ยังสามารถมีส่วนร่วมกับการเรียกร้องให้ob_start();
อยู่เหนือสคริปต์ภาวนา ซึ่งมีความน่าเชื่อถือน้อยกว่าด้วยเหตุผลหลายประการ:
แม้ว่า<?php ob_start(); ?>
จะเริ่มต้นสคริปต์แรกช่องว่างหรือ BOM อาจได้รับการสับก่อนการกระทำมันไม่ได้ผล
มันสามารถปกปิดช่องว่างสำหรับการส่งออก HTML แต่ทันทีที่แอปพลิเคชันตรรกะพยายามส่งเนื้อหาไบนารี (รูปภาพที่สร้างขึ้น) เอาต์พุตภายนอกที่บัฟเฟอร์จะกลายเป็นปัญหา (จำเป็นob_clean()
ต้องมีวิธีแก้ปัญหาแบบเร่งด่วน)
บัฟเฟอร์มีขนาด จำกัด และสามารถ overrun ได้อย่างง่ายดายเมื่อปล่อยทิ้งไว้เป็นค่าเริ่มต้น และนั่นไม่ใช่สิ่งที่เกิดขึ้นได้ยากเช่นกันยากที่จะติดตาม
เมื่อมันเกิดขึ้น
ดังนั้นทั้งสองวิธีจึงอาจไม่น่าเชื่อถือ - โดยเฉพาะอย่างยิ่งเมื่อสลับระหว่างการตั้งค่าการพัฒนาและ / หรือเซิร์ฟเวอร์ที่ใช้งานจริง ซึ่งเป็นสาเหตุที่การบัฟเฟอร์เอาต์พุตถูกพิจารณาอย่างกว้างขวางเพียงแค่ไม้ค้ำ / วิธีแก้ปัญหาอย่างเคร่งครัด
ดูตัวอย่างการใช้งานพื้นฐาน
ในคู่มือและดูข้อดีและข้อเสียเพิ่มเติมได้ที่:
แต่มันทำงานบนเซิร์ฟเวอร์อื่น!
หากคุณไม่ได้รับคำเตือนส่วนหัวมาก่อนการตั้งค่าบัฟเฟอร์บัฟเฟอร์เอาต์พุต
จะเปลี่ยนไป อาจไม่ได้กำหนดค่าไว้ในเซิร์ฟเวอร์ปัจจุบัน / ใหม่
กำลังตรวจสอบกับ headers_sent()
คุณสามารถใช้headers_sent()
โพรบได้ตลอดเวลาหากยังคงสามารถ ... ส่งส่วนหัว ซึ่งมีประโยชน์ในการพิมพ์ข้อมูลแบบมีเงื่อนไขหรือใช้ตรรกะทางเลือกอื่น
if (headers_sent()) {
die("Redirect failed. Please click on this link: <a href=...>");
}
else{
exit(header("Location: /user.php"));
}
วิธีแก้ปัญหาทางเลือกที่มีประโยชน์คือ:
HTML <meta>
แท็ก
หากแอปพลิเคชันของคุณมีโครงสร้างที่ยากต่อการแก้ไขวิธีที่ง่าย (แต่ค่อนข้างไม่เป็นมืออาชีพ) ในการอนุญาตการเปลี่ยนเส้นทางคือการฉีด<meta>
แท็กHTML
การเปลี่ยนเส้นทางสามารถทำได้ด้วย:
<meta http-equiv="Location" content="http://example.com/">
หรือด้วยความล่าช้าเล็กน้อย:
<meta http-equiv="Refresh" content="2; url=../target.html">
สิ่งนี้นำไปสู่ HTML ที่ไม่ถูกต้องเมื่อใช้ผ่าน<head>
ส่วนนี้ เบราว์เซอร์ส่วนใหญ่ยังคงยอมรับ
การเปลี่ยนเส้นทาง JavaScript
ทางเลือกอื่น
สามารถใช้การเปลี่ยนเส้นทาง JavaScriptสำหรับการเปลี่ยนเส้นทางหน้า:
<script> location.replace("target.html"); </script>
แม้ว่าสิ่งนี้จะสอดคล้องกับ HTML มากกว่า<meta>
วิธีแก้ปัญหา แต่ก็ต้องพึ่งพาไคลเอนต์ที่ใช้ JavaScript ได้
ทั้งสองวิธีทำการตกลงได้เมื่อการเรียกส่วนหัว HTTP () ของแท้ล้มเหลว เป็นการดีที่คุณจะรวมสิ่งนี้กับข้อความที่ใช้งานง่ายและลิงค์ที่คลิกได้เป็นทางเลือกสุดท้าย (ตัวอย่างเช่นhttp_redirectคืออะไร)
ส่วนขยาย PECL ทำ)
ทำไมsetcookie()
และsession_start()
ยังได้รับผลกระทบ
ทั้งสองsetcookie()
และsession_start()
จำเป็นต้องส่งSet-Cookie:
ส่วนหัว HTTP ดังนั้นจึงใช้เงื่อนไขเดียวกันและจะมีการสร้างข้อความแสดงข้อผิดพลาดที่คล้ายกันสำหรับสถานการณ์เอาต์พุตก่อนกำหนด
(แน่นอนว่าพวกเขาได้รับผลกระทบมากขึ้นจากคุกกี้ที่ถูกปิดใช้งานในเบราว์เซอร์หรือแม้กระทั่งปัญหาเกี่ยวกับพร็อกซีการทำงานของเซสชันนั้นขึ้นอยู่กับพื้นที่ว่างในดิสก์และการตั้งค่า php.ini อื่น ๆ ด้วย)
ลิงค์เพิ่มเติม