اذهب إلى المحتوى

التعامل مع الأخطاء في PHP


سارة محمد2

أخطاء شائعة

استدعاء fetch_assoc على قيمة منطقية

إذا حصلت على خطأ مشابه للتالي:

Fatal error: Call to a member function fetch_assoc() on boolean in C:\xampp\htdocs\stack\index.php on line 7

تتضمن الاختلافات الأخرى شيئًا ما مثل:

mysql_fetch_assoc() expects parameter 1 to be resource, boolean given...

تعني هذه الأخطاء أنّه يوجد شيء ما خاطئ إما مع الاستعلام (وهذا خطأ PHP/MySQL) أو مع المرجعية. أُنتج الخطأ السابق بسبب الشيفرة التالي:

$mysqli = new mysqli("localhost", "root", "");

// لاحظ الأخطاء هنا
$query = "SELCT * FROM db"; 
$result = $mysqli->query($query);

$row = $result->fetch_assoc();

لإصلاح هذا الخطأ يُنصَح بجعل mysql يرمي استثناءات بدلًا من ذلك:

// أضف هذه الشيفرة في بداية السكربت
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

ستؤدي هذه الشيفرة إلى رمي استثناء مع رسالة تساعدك كثيرًا:

You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELCT * FROM db' at line 1

مثال آخر يمكن أن ينتج عنه خطأ مشابه، هو عندما تعطي معلومات خاطئة لدالة mysql_fetch_assoc أو لدالة مشابهة:

$john = true;
mysqli_fetch_assoc($john, $mysqli);

Unexpected $end

إذا حصلت على خطأ مشابه للخطأ التالي:

Parse error: syntax error, unexpected end of file in C:\xampp\htdocs\stack\index.php on line 4

أو قد يكون بالشكل unexpected $end وهذا يعتمد على نسخة PHP، ستحتاج عندها إلى التأكد من جميع فواصل الاقتباس وكل الأقواس الهلالية والمعقوصة والمعقوفة….

تنتج الشيفرة التالية الخطأ السابق:

<?php
if (true) {
    echo "asdf";
?>

لاحظ عدم وجود القوس المعقوص وأنّ رقم السطر الموضح في الخطأ لا صلة له بالموضوع، لأنه دائمًا يظهر السطر الأخير من ملفك.

تصريف الأخطاء والتحذيرات

أخطاء التحميل غير المتوقعة وأخطاء بناء الجملة

تعني العبارة "Paamayim Nekudotayim" نقطتان مزدوجتان باللغة العبرية، لذا يشير هذا الخطأ إلى الاستخدام غير المناسب للعامل (::)، ويحدث عادةً بسبب محاولة استدعاء تابع ساكن في حين أنّه في الواقع غير ساكن.

الحل الممكن:

$classname::doMethod();

إذا سببت الشيفرة السابقة هذا الخطأ فغالبًا أنت تحتاج إلى تغيير طريقة استدعاء التابع الآتي:

$classname->doMethod();

حيث يفترض المثال الأخير أنّ ‎$classname هو كائن من صنف والتابع doMethod()‎ ليس تابعًا ساكنًا من هذا الصنف.

إشعار الفهرس غير المعرف Undefined index

  • الظهور: محاولة الوصول إلى مصفوفة عن طريق مفتاح غير موجود في المصفوفة.
  • الحل الممكن: التحقق من التوافرية قبل محاولة الوصول وذلك باستخدام:

تحذير عدم إمكانية التعديل على معلومات الترويسة أو الترويسات المرسلة سلفا

يظهر هذا الخطأ عندما يحاول السكربت إرسال ترويسة HTTP إلى العميل لكن كان هناك خرج بالفعل سابقًا مما يعني أنّ الترويسات قد اُرسلت سابقًا، وقد يعود ذلك إلى:

  • تعليمتي Print وecho: سينهي خرج هاتين التعليمتين فرصة إرسال ترويسات HTTP، يجب إعادة هيكلية تدفق التطبيق لتجنب ذلك.
  • مناطق HTML خام: تعد شيفرة HTML غير المحللة في ملف ‎.php خرج مباشر أيضًا، يجب ملاحظة شروط السكربت التي ستشغّل استدعاء header()‎ قبل أي كتل خام.
<!DOCTYPE html>
<?php
    // فات أوان إرسال الترويسات
  • المسافة البيضاء قبل ‎<?php من أجل التحذير script.php line 1، إذا أشار التحذير إلى الخرج في السطر 1 فغالبًا السبب هو مسافة بيضاء أو نص أو شيفرة HTML قبل وسم الفتح ‎<?php.
<?php
# ‫يوجد مسافة مفردة/سطر جديد قبل ‎<?‎

معالجة الاستثناءات والإبلاغ عن الأخطاء error reporting

ضبط الإبلاغ عن الأخطاء وأماكن عرضها

يمكن ضبط الإبلاغ عن الأخطاء بشكلٍ ديناميكي إذا لم يُضبط سابقًا في ملف php.ini وذلك للسماح بعرض معظم الأخطاء.

  • الصيغة
int error_reporting ([ int $level ] )
  • أمثلة
// ‫يجب أن تُستخدم دائمًا في الإصدارات السابقة للإصدار 5.4
error_reporting(E_ALL);

// (1)
error_reporting(-1);
?// بدون ملاحظات
error_reporting(E_ALL & ~E_NOTICE);

// التحذيرات والملاحظات فقط
// كمثال فقط، لا يُنصح بالحصول على إبلاغ عن هؤلاء فقط
error_reporting(E_WARNING | E_NOTICE);
 

في الموضع (1) يُظهر الوسيط ‎-1 كل خطأ محتمل حتى عندما تُضاف ثوابت ومستويات جديدة في إصدارات لغة PHP المستقبلية، يقوم الوسيط E_ALL بنفس هذا العمل حتى الإصدار 5.4.

ستُسجَّل الأخطاء افتراضيًا من قِبل PHP في ملف error.log في نفس مستوى الملف الذي يُنفَّذ، ويمكنك أن تعرضهم على الشاشة في بيئة التطوير:

ini_set('display_errors', 1);

ويجب أن تصبح الشيفرة في بيئة الإنتاج:

ini_set('display_errors', 0);

وستظهر رسالة المشكلة بشكلٍ واضح أثناء استخدام معالِج الاستثناء أو الخطأ.

تسجيل الأخطاء الفادحة

الخطأ الفادح في PHP هو نوع من الأخطاء التي لا يمكن التقاطها، لا يستأنف البرنامج تنفيذه عند التعرض لخطأ فادح، لكن لتسجيل هذا الخطأ أو معالجة الانهيار بطريقةٍ ما يمكنك استخدام register_shutdown_function لتسجيل معالج الإنهاء.

function fatalErrorHandler() {
    // لنحصل على الخطأ الفادح الأخير
    $error = error_get_last();
    // (1)
    if (null === $error || E_ERROR != $error['type']) {
        return;
    }

    // تسجيل الخطأ الأخير في ملف السجل
    // لنفرض أنّ السجلات موجودة في مجلد داخل مجلد التطبيق
    $logFile = fopen("./app/logs/error.log", "a+");

    // الحصول على معلومات مفيدة عن الخطأ
    $type = $error["type"];
    $file = $error["file"];
    $line = $error["line"];
    $message = $error["message"]

    fprintf(
        $logFile,
        "[%s] %s: %s in %s:%d\n",
        date("Y-m-d H:i:s"),
        $type,
        $message,
        $file,
        $line);

    fclose($logFile);
}
register_shutdown_function('fatalErrorHandler');

في الموضع (1) معالج الخطأ هذا فقط من أجل مثالنا، يعني عدم وجود خطأ أنّه لا يوجد أي خطأ وأنّ الإنهاء كان مناسبًا وتأكد أيضًا أنه سيعالج الأخطاء الفادحة فقط.

تنقيح الأخطاء

عرض المتغيرات

تسمح الدالة var_dump بعرض محتويات المتغير (النوع والمتغير) لتنقيح الأخطاء.

مثال

$array = [3.7, "string", 10, ["hello" => "world"], false, new DateTime()];
var_dump($array);

الخرج

array(6) {
    [0]=>
    float(3.7)
    [1]=>
    string(6) "string"
    [2]=>
    int(10)
    [3]=>
    array(1) {
        ["hello"]=>
        string(5) "world"
    }
    [4]=>
    bool(false)
    [5]=>
    object(DateTime)#1 (3) {
        ["date"]=>
        string(26) "2016-07-24 13:51:07.000000"
        ["timezone_type"]=>
        int(3)
        ["timezone"]=>
        string(13) "Europe/Berlin"
    }
}

عرض الأخطاء

يجب أن تمكّن الإعداد display_errors إذا أردت عرض الأخطاء التي تحدث وقت التنفيذ runtime errors على الصفحة وذلك إما في ملف php.ini أو باستخدام الدالة ini_set.

يمكنك اختيار الأخطاء التي تريد عرضها باستخدام دالة error_reporting، أو في ملف ini والتي تقبل الثوابت E_*‎ مجموعةً باستخدام العوامل الثنائية، ويمكن عرض الأخطاء على شكل نص أو بصيغة HTML وذلك حسب الإعداد html_errors.

  • مثال
ini_set("display_errors", true);

// عرض الأخطاء كنص بسيط
ini_set("html_errors", false);

// ‫عرض كل شيء باستثناء E_USER_NOTICE
error_reporting(E_ALL & ~E_USER_NOTICE);

// E_USER_NOTICE
trigger_error("Pointless error");

// E_NOTICE
echo $nonexistentVariable;

// E_ERROR
nonexistentFunction();

خرج نصي بسيط: (تختلف صيغة HTML بين التنفيذات)

Notice: Undefined variable: nonexistentVariable in /path/to/file.php on line 7
Fatal error: Uncaught Error: Call to undefined function nonexistentFunction() in /path/to/file.php:8
Stack trace:
#0 {main}
thrown in /path/to/file.php on line 8
اقتباس

ملاحظة: إذا كان الإبلاغ عن الخطأ معطلًا في ملف php.ini ومكّنته وقت التنفيذ، فإنّ بعض الأخطاء (مثل الأخطاء التحليلية) لن تُعرض لأنها حدثت قبل تطبيق إعداد وقت التنفيذ.

الطريقة الشائعة لمعالجة error_reporting هي تمكينه بالكامل باستخدام الثابت E_ALL أثناء التطوير وتعطيل عرض الأخطاء للعامة باستخدام display_errors في مرحلة الإنتاج لإخفاء ما يوجد داخل السكربت.

phpinfo()‎

اقتباس

 

تحذير
من الضرورري أن تستخدم phpinfo في بيئة التطوير فقط، لا تطلق شيفرة تحوي هذه الدالة في بيئة الإنتاج أبدًا.
مقدمة يمكن أن تكون دالة phpinfo البسيطة المضمنة في PHP أداةً مفيدة لفهم بيئة PHP (نظام التشغيل، الإعدادات، الإصدارات، المسارات، الوحدات) التي تعمل فيها خاصةً عند متابعة خطأ.

 

 

phpinfo();

لهذه الدالة معامل واحد ‎$what يسمح بتخصيص الخرج، قيمته الافتراضية INFO_ALL وتسبب عرض كل المعلومات وتُستخدم عادةً أثناء التطوير لمعرفة حالة PHP الحالية، ويمكنك تمرير المعامل ثوابت INFO_*‎ مجموعةً باستخدام العوامل الثنائية للحصول على قائمة مخصصة، يمكنك تنفيذها في المتصفح للحصول على التفاصيل بتنسيقٍ جيد، وتعمل أيضًا في واجهة سطر أوامر PHP حيث يمكنك نقلها بعرضٍ أفضل.

مثال

phpinfo(INFO_CONFIGURATION | INFO_ENVIRONMENT | INFO_VARIABLES);

ستعرض هذه الشيفرة قائمة من موجهات PHP (‏ini_get)ومتغيرات البيئة (‎$_ENV) والمتغيرات المعرفة مسبقًا.

Xdebug

Xdebug إضافة PHP توفر إمكانيات تنقيح الأخطاء والتحليل، تستخدم بروتوكول تحديد الأخطاء DBGp والتي هي اختصار لـ ‏DeBugGer Protocol، من ميزات هذه الإضافة:

  • مكدس يتتبع الأخطاء.
  • حماية قصوى بمستوى متداخل وتتبع الوقت.
  • بديل مفيد لدالة var_dump()‎ المعيارية لعرض المتغيرات.
  • تسمح بتسجيل كل استدعاءات الدالة متضمنةً المعاملات والقيم المُعادة في ملف بتنسيقات مختلفة
  • تحليل تغطية الشيفرة
  • تحليل المعلومات
  • تنقيح الأخطاء عن بعد (توفر واجهة للعملاء منقحي الأخطاء الذين يتفاعلون مع سكربت PHP المُنفَّذ).

إنّ هذه الإضافة مناسبة تمامًا لبيئة التطوير، خاصةً ميزة تنقيح الأخطاء عن بعد التي يمكن أن تساعدك في تنقيح أخطاء شيفرة PHP بدون كتابة العديد من تعليمات var_dump واستخدام عملية تنقيح الأخطاء العادية كما في لغة C++‎ وجافا.

تثبيت هذه الإضافة بسيط جدًا:

pecl install xdebug # install from pecl/pear
 

وتفعيلها في ملف php.ini:

zend_extension="/usr/local/php/modules/xdebug.so"

يمكنك الاطلاع على هذه التعليمات للحالات الأكثر تعقيدًا. يجب أن تتذكر عند استخدام هذه الأداة أنها غير مناسبة لبيئات الإنتاج.

الإبلاغ عن الأخطاء

استخدم الدالتين التاليتين معًا:

// تضبط هذه الدالة خيار الإعداد في بيئتك
ini_set('display_errors', '1');

// ‫ستسمح ‎?-1‎ بالإبلاغ عن كل الأخطاء
error_reporting(-1);

phpversion()‎

من الضروري أن تعرف إصدار محلل PHP الحالي أو إحدى الحزم عند العمل مع المكتبات المختلفة والمتطلبات المرتبطة بها.

تقبل هذه الدالة معامل اختياري واحد في شكل اسم الإضافة phpversion('extension')‎، إذا ثُبِّتت الإضافة فإنّ الدالة سترجع سلسلة نصية تتضمن قيمة الإصدار وإلا ستُرجع القيمة FALSE، وإذا لم يتوفر اسم الإضافة ستُرجع الدالة إصدار محلل PHP نفسه.

مثال

print "Current PHP version: " . phpversion();
// Current PHP version: 7.0.8

print "Current cURL version: " . phpversion( 'curl' );
// Current cURL version: 7.0.8
// أو
// false, no printed output if package is missing

المراجع:

ترجمة -وبتصرف- للفصول Common Errors ، وCompilation of Errors and Warnings ، وException Handling and Error Reporting  ، وDebugging من كتاب PHP Notes for Professionals book


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...