مقدمة: "لقد أرسلت شيئًا إلى الشاشة بالفعل، ولا أستطيع فعل ذلك"
\nإذا سبق لك أن رأيت على موقعك بدلاً من صفحة جميلة شاشة بيضاء مع رسالة مخيفة Warning: Cannot modify header information - headers already sent — فأنت لست وحدك. هذا هو بلا شك أحد أكثر الأخطاء شيوعًا بين مطوري PHP المبتدئين (وليس فقط المبتدئين). على Stack Overflow، حصل هذا السؤال على مئات الآلاف من المشاهدات، وعدد نسخ الكود الخاطئ في العالم يُقاس بالتيرابايت.
لكن الخبر السار هو أن خطأ "Headers already sent" ليس لعنة، بل مجرد تذكير صارم من PHP: "مرحبًا، لقد بدأت بالفعل في إرسال كود HTML، والآن تحاول تغيير رؤوس HTTP. هذا غير مسموح!".
\nفي هذه المقالة، سنشرح لماذا يحدث خطأ headers already sent في PHP، وكيفية العثور عليه، والأهم من ذلك، كيفية إصلاحه نهائيًا. هيا بنا!
\n\n1. ما هي رؤوس HTTP ولماذا "تم إرسالها بالفعل"؟
\nقبل الخوض في الكود، دعنا نفهم ببساطة ما يحدث "تحت الغطاء". عندما يتم تنفيذ سكريبت PHP، فإنه يرسل أولاً رؤوس HTTP (header) إلى المتصفح. تحتوي هذه الرؤوس على معلومات خدمية: نوع المحتوى (Content-Type)، الكوكيز (Set-Cookie)، إعادة التوجيه (Location)، حالة الاستجابة (200، 404، 301) وهكذا.
وفقط بعد كل الرؤوس يبدأ إرسال نص المستند — وهو HTML الذي تراه على الصفحة. يسمح PHP بتعديل الرؤوس فقط قبل لحظة إرسال أول بايت من المحتوى.
\nبمجرد أن تقوم بإخراج شيء ما على الشاشة (عبر echo، print، var_dump أو حتى مجرد مسافة قبل <?php)، يعتبر PHP أن الرؤوس قد أُرسلت بالفعل. أي محاولة لاستدعاء header()، setcookie() أو session_start() بعد ذلك تؤدي إلى خطأ فادح.
1.1. مثال كلاسيكي على الخطأ
\n<?php\necho "مرحبًا بالعالم!";\n// عذرًا! لقد أخرجنا شيئًا بالفعل.\nheader("Location: https://example.com");\n// Warning: Cannot modify header information - headers already sent\n?>\nفي هذا المثال، يقوم echo بإرسال نص إلى نص المستند. بعد ذلك، لا يستطيع PHP إعادة كتابة الرؤوس لإجراء إعادة التوجيه. هذا منطقي.
2. أهم 5 أسباب لحدوث "Headers already sent"
\nقد لا يكون الخطأ ناتجًا فقط عن echo صريح. إليك السيناريوهات الأكثر شيوعًا التي تقع فيها حتى المطورين ذوي الخبرة:
- \n
- مسافة أو سطر فارغ قبل
<?php— "الخلل" الأكثر شيوعًا. إذا كان هناك مسافة واحدة على الأقل في الملف قبل علامة الفتح، فسيتم إرسالها كمخرجات. \n - أحرف BOM غير المرئية (Byte Order Mark) — تم حفظ الملف بترميز UTF-8 with BOM. تعتبر أول 3 بايتات (EF BB BF) مخرجات. \n
- مخرجات من الملفات المضمنة — في ملف
config.phpتوجد مسافة زائدة بعد?>. \n - الأخطاء والتحذيرات — إذا قام PHP بعرض تحذير (على سبيل المثال، عن استخدام متغير غير معرف)، فهذا يعتبر أيضًا إرسال محتوى. \n
- دوال
print،printf،var_dumpقبل header() — أي دالة إخراج تفسد الرؤوس. \n
3. كيفية العثور على مصدر المشكلة: 3 طرق
\nقبل الإصلاح، تحتاج إلى العثور على مصدر "الإخراج الزائد". استخدم إحدى هذه الطرق.
\n\n3.1. تفعيل عرض جميع الأخطاء
\nأضف في بداية السكريبت (قبل أي إخراج):
\n<?php\nerror_reporting(E_ALL);\nini_set('display_errors', 1);\n?>\nغالبًا ما يعرض PHP السطر الدقيق الذي حدث فيه الإخراج. ابحث عن رسالة مثل Output started at /path/to/file.php:12.
3.2. استخدام الدالة error_get_last()
\nيمكنك اعتراض آخر خطأ