لإظهار أخطاء PHP، ضع هذين السطرين في أعلى السكربت: ini_set('display_errors', '1'); وerror_reporting(E_ALL);. الأول يجعل PHP تطبع رسائل الأخطاء، والثاني يجعلها تبلّغ عن كل أنواع الأخطاء، بما فيها التحذيرات وإشعارات الإهمال (deprecations).
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
بعد ذلك، يطبع خطأ مثل قراءة متغير لم يُعيَّن قط رسالة فيها الملف ورقم السطر بدلًا من أن يفشل بصمت:
Warning: Undefined variable $total in /home/index.php on line 7
عند استدعاء error_reporting() دون وسيط، تعيد الإعداد الحالي كعدد. اضغط Run لتعيين مستوى ثم فكّه إلى أسمائه:
كل نوع خطأ هو بت واحد، لذا تعني E_ALL & ~E_DEPRECATED «كل شيء ما عدا إشعارات الإهمال». غيّر السطر الأول إلى error_reporting(E_ERROR | E_WARNING); وشغّله مرة أخرى.
إظهار الأخطاء في php.ini
الإعداد داخل السكربت لا يسري إلا بعد بدء تنفيذ السكربت. لإظهار الأخطاء في كل السكربتات، بما فيها أخطاء التحليل، عدّل php.ini (اعثر عليه بالأمر php --ini):
; Development
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
أعد تشغيل Apache أو PHP-FPM بعد التعديل. أما الخادم المدمج php -S وسطر الأوامر فيقرآن الملف عند كل تشغيل. وعلى الاستضافة المشتركة دون وصول إلى php.ini، يقبل Apache مع mod_php أيضًا أسطرًا في .htaccess:
php_flag display_errors on
php_value error_reporting -1
القيمة -1 تعيّن كل البتات، وهي تساوي E_ALL.
مستويات الأخطاء
| الثابت | ما يسببه | هل يوقف السكربت |
|---|---|---|
E_ERROR | خطأ قاتل أثناء التشغيل، مثل نفاد الذاكرة | نعم |
E_PARSE | خطأ في صياغة الملف | نعم |
E_WARNING | متغير أو مفتاح مصفوفة غير معرّف، أو ملف مفقود في include | لا |
E_NOTICE | مشكلات بسيطة (بقي القليل منها في PHP 8) | لا |
E_DEPRECATED | كود سيرفضه إصدار مستقبلي من PHP | لا |
E_USER_* | أخطاء تطلقها أنت بـ trigger_error() | فقط E_USER_ERROR |
E_ALL | كل المستويات، بما فيها أخطاء النواة والترجمة غير المذكورة هنا |
حوّل PHP 8.0 كثيرًا من التحذيرات القديمة إلى استثناءات: القسمة على صفر بـ / وتمرير نوع خاطئ إلى دالة مدمجة يطلقان الآن صنفًا فرعيًا من Error، كما يحدث عند استدعاء دالة غير معرّفة منذ PHP 7. يمكنك التقاط الثلاثة:
الاستثناءات مشروحة في try وcatch. أما التحذيرات والإشعارات وإشعارات الإهمال فما زالت تمر عبر نظام الإبلاغ عن الأخطاء الموصوف هنا.
تسجيل الأخطاء بدلًا من عرضها
في الموقع المنشور، يجب ألا يرى الزوار رسائل الأخطاء أبدًا: فهي تكشف المسارات وSQL والكود. اكتبها في ملف سجل بدلًا من ذلك:
; Production
display_errors = Off
log_errors = On
error_log = /var/log/php/app-errors.log
error_reporting = E_ALL
أبقِ error_reporting = E_ALL في الإنتاج. خفضه لا يجعل الأخطاء تختفي، بل يمنعك فقط من معرفتها. ويمكن أن تذهب رسائلك الخاصة إلى السجل نفسه عبر error_log():
<?php
error_log("Payment failed for order 1042");
ثم راقب الملف أثناء الاختبار: tail -f /var/log/php/app-errors.log.
تحويل التحذيرات إلى استثناءات باستخدام set_error_handler
تستبدل set_error_handler معالجة PHP الافتراضية للتحذيرات والإشعارات وإشعارات الإهمال بدالتك الخاصة. ومن الأنماط الشائعة تحويل كل واحد منها إلى ErrorException، فيوقف مفتاح مصفوفة منسي الكود مثل أي استثناء آخر ويمكن التقاطه:
يستقبل المعالج مستوى الخطأ كوسيط أول، لذا يستطيع معاملة المستويات بشكل مختلف. هذا المعالج يسجّل إشعارات الإهمال فقط ويحوّل كل ما عداها إلى استثناء:
الأخطاء القاتلة وأخطاء التحليل لا تصل إلى المعالج أبدًا، لأن السكربت لا يستطيع المتابعة بعدها. ولتسجيلها، سجّل دالة إيقاف (shutdown function) تقرأ error_get_last().
قراءة آخر خطأ باستخدام error_get_last
بعض الدوال تعيد false ولا تبلّغ عن السبب إلا كتحذير، مثل file_get_contents على ملف مفقود. تعيد error_get_last() هذا التحذير كمصفوفة، وهذا مفيد عندما تريد عرض رسالتك الخاصة:
العامل @ قبل الاستدعاء يكتم الرسالة لذلك التعبير وحده. استخدمه باعتدال، وفقط عندما تتحقق من النتيجة بعده مباشرة كما هنا، فالعامل @ وحده لا يفعل سوى إخفاء الأخطاء البرمجية.
خطأ شائع: الأخطاء مفعّلة لكن الصفحة ما زالت فارغة
إذا أضفت ini_set('display_errors', '1') وما زلت ترى صفحة بيضاء، فالخطأ على الأرجح خطأ تحليل. تحلّل PHP الملف كله قبل تنفيذ السطر الأول، لذا فإن ; أو } مفقودة توقف كل شيء قبل تنفيذ ini_set. افحص الملف من الطرفية، فهي تطبع الرسالة دائمًا:
php -l index.php
Errors parsing index.php
PHP Parse error: syntax error, unexpected token "," in index.php on line 2
يشير رقم السطر إلى حيث توقفت PHP، وهو غالبًا السطر الذي يلي الخطأ الحقيقي. أصلحه، وشغّل php -l مرة أخرى حتى يقول No syntax errors detected، ثم أعد تحميل الصفحة.
الأسئلة الشائعة
كيف أعرض كل الأخطاء في PHP؟
أضف ini_set('display_errors', '1'); ini_set('display_startup_errors', '1'); error_reporting(E_ALL); في أول السكربت تمامًا، أو اضبط display_errors = On وerror_reporting = E_ALL في php.ini وأعد تشغيل خادم الويب أو PHP-FPM.
لماذا تعرض صفحة PHP شاشة بيضاء فارغة؟
أوقف خطأ قاتل السكربت بينما display_errors معطّل، فلم يُطبع شيء. فعّل الأخطاء (أو اقرأ سجل الأخطاء، مثلًا بالأمر tail -f على الملف الذي يشير إليه error_log) لترى الرسالة ورقم السطر.
لماذا لا يعرض ini_set('display_errors', 1) خطأ التحليل؟
خطأ التحليل يوقف PHP قبل تنفيذ أي سطر من الملف، فلا يحدث استدعاء ini_set أبدًا. اضبط display_errors في php.ini، أو ضع ini_set في ملف صغير يستدعي الملف المعطوب بـ include.
هل يجب تفعيل display_errors في بيئة الإنتاج؟
لا. رسائل الأخطاء تكشف للزوار مسارات الملفات وتفاصيل الكود. في الإنتاج استخدم display_errors = Off وlog_errors = On ومسارًا لـ error_log، وأبقِ error_reporting = E_ALL حتى تُسجَّل المشكلات.
ماذا يفعل error_reporting(0)؟
يوقف الإبلاغ عن كل أنواع الأخطاء في بقية السكربت، فلا تُنتج التحذيرات ولا حتى الأخطاء القاتلة أي رسالة. هذا يخفي المشكلات بدلًا من إصلاحها، ففضّل تسجيل الأخطاء على إسكاتها.