Menu
flag Ar iconالعربيةdown icon

الاستثناءات في PHP: throw وأصناف الاستثناءات المخصصة

ترمي استثناءً في PHP بـ throw new Exception('message');، ويقرؤه المستدعي بـ $e->getMessage() في كتلة catch. تعلّم أصناف الاستثناءات المدمجة، وكتابة استثناءات مخصصة، وgetCode() وgetLine() وgetPrevious()، وthrow بوصفها تعبيرًا، والأخطاء التي ترميها PHP بنفسها مثل ValueError وTypeError.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

ترمي استثناءً في PHP بـ throw new Exception('message');. يتوقف التنفيذ عند ذلك السطر ويقفز إلى أقرب كتلة catch تقبل نوع الاستثناء، حيث تعيد $e->getMessage() الرسالة. استخدم صنفًا محددًا، مثل InvalidArgumentException، ليتمكن المستدعون من التمييز بين المشكلات.

الاستثناء كائن. تمرره throw صعودًا في مكدس الاستدعاءات إلى أول مستدعٍ لديه catch مطابقة، وذلك المستدعي يقرر ما يفعل؛ ولا تحتاج الدالة التي وجدت المشكلة إلى معرفة ذلك. وآلية الالتقاط وfinally والتقاط عدة أنواع موجودة في صفحة try catch.

ما يحتويه كائن الاستثناء

يحمل كل استثناء رسالة، ورمزًا صحيحًا، والملف والسطر اللذين أُنشئ فيهما، ومسار الاستدعاءات. يأخذ الباني ($message, $code, $previous)، وكلها اختيارية:

getLine() هو سطر throw new، لا سطر catch. ويسرد المسار الاستدعاءات التي أدت إلى هناك (#0 /home/index.php(8): findOrder())، وهو عادة أول ما يُقرأ حين يظهر خطأ في سجل. وتحويل الاستثناء إلى نص (echo $e; أو (string) $e) يطبعه كله بالتنسيق القياسي لـ PHP.

أصناف الاستثناءات المدمجة

تأتي PHP بعائلة من أصناف الاستثناءات في مكتبتها القياسية (SPL). ورمي الصنف الذي يسمّي المشكلة يجعل شيفرتك أوضح ويتيح للمستدعين الالتقاط بدقة. وكلها ترث Exception:

الصنفارمِه عندما
InvalidArgumentExceptionيكون للوسيط شكل خاطئ: اسم فارغ، أو خيار غير معروف
DomainExceptionتكون القيمة خارج المجموعة المعقولة: سعر سالب
OutOfRangeExceptionتطلب الشيفرة فهرسًا لا يمكن أن يوجد أبدًا (خطأ عند المستدعي)
OutOfBoundsExceptionيكون مفتاح أو فهرس مفقودًا من بيانات لا تُعرف إلا وقت التشغيل
RangeExceptionتقع نتيجة محسوبة خارج النطاق الصحيح أثناء التشغيل
LengthExceptionيكون شيء ما أطول أو أقصر من اللازم
RuntimeExceptionمشكلة لا تُكتشف إلا أثناء التشغيل: قرص ممتلئ، انتهاء مهلة
UnexpectedValueExceptionتعيد دالة أو تتلقى قيمة من نوع لم تتوقعه
LogicExceptionتكون الشيفرة نفسها خاطئة، خطأ برمجي لا مُدخل سيئ
JsonExceptionترميها json_encode()/json_decode() مع JSON_THROW_ON_ERROR

ترث InvalidArgumentException وDomainException وLengthException وOutOfRangeException الصنف LogicException؛ وترث OutOfBoundsException وRangeException وUnexpectedValueException الصنف RuntimeException. ويمكنك فحص آباء أي صنف بنفسك:

أبو TypeError هو Error، لا Exception: فهو ينتمي إلى العائلة الثانية، أدناه.

الأخطاء التي ترميها PHP بنفسها

منذ PHP 7، وأكثر بكثير منذ PHP 8، ترمي دوال PHP وعواملها أصنافًا فرعية من Error لمشكلات كانت تحذيرات. تلتقطها بالطريقة نفسها، لكن catch (Exception $e) لن تطابقها:

ترث DivisionByZeroError وTypeError وValueError وArgumentCountError وUnhandledMatchError وError العادي كلها Error. وكل منها يدل على خطأ في الشيفرة المستدعية أكثر مما يدل على بيانات سيئة، ولهذا تعيش خارج شجرة Exception. وdeclare(strict_types=1) هو ما يجعل str_repeat(5, 2) خطأ TypeError؛ ودونه ستحوّل PHP العدد 5 إلى "5".

كتابة صنف استثناء مخصص

كثيرًا ما يكفيك صنف فرعي من سطر واحد: اسم الصنف نفسه يحمل المعنى، ويستطيع المستدعون التقاط ذلك النوع بالضبط. وعندما يحتاج الملتقط إلى بيانات (أي طلب، أي مبلغ)، أضف خصائص ومرّر الرسالة صعودًا بـ parent::__construct():

اختر الأب بعناية: وراثة RuntimeException (بدل Exception العادي) تعني أن الشيفرة التي تلتقط RuntimeException تعالج استثناءك أيضًا. والتسلسل الهرمي الصغير، استثناء أساسي واحد لكل مكتبة أو وحدة مع أصناف فرعية محددة تحته، يتيح للمستدعين الاختيار بين «التقط كل شيء من المدفوعات» و«التقط هذه الحالة وحدها».

تسلسل الاستثناءات بـ previous

عندما تلتقط استثناءً منخفض المستوى وترمي استثناءً أعلى مستوى، مرّر الأصلي وسيطًا ثالثًا. لا يضيع شيء: تعود getPrevious() إلى السبب الجذري.

يرى المستخدم "customers.csv could not be imported"؛ ويحصل السجل، بالمرور على السلسلة، على "row 2 has 2 columns" أيضًا. وإذا لم يُلتقط استثناء متسلسل أبدًا، يطبع الخطأ الفادح في PHP السلسلة كلها: السبب الأصلي أولًا، ثم كل غلاف تحت سطر Next.

throw بوصفها تعبيرًا

منذ PHP 8.0، throw تعبير، لذا يمكن أن تقع حيثما تُتوقع قيمة: بعد ??، وفي العامل الثلاثي، وفي دالة سهمية. وتجعل «احصل على هذا أو افشل» سطرًا واحدًا:

متى لا ترمي

الاستثناءات للمواقف التي لا تستطيع الدالة الحالية معالجتها ولا يتوقعها المسار العادي. أما البحث الذي لا يجد شيئًا، أو الحقل الاختياري الفارغ، أو المستخدم غير المسجل دخوله، فنتائج عادية: أعِد null أو false أو مصفوفة فارغة ودع المستدعي يتحقق، واحتفظ بالاستثناءات للحالات التي يكون فيها المضي قدمًا خطأً.

عادتان أخريان تُبقيان شيفرة الاستثناءات مقروءة: لا تلتقط استثناءً لتتجاهله أبدًا (تخفي catch (Exception $e) {} الفارغة الخطأ الحقيقي التالي)، وارمِ أصنافًا محددة لا Exception المجردة، حتى لا يُجبر المستدعي أبدًا على التقاط كل شيء لمعالجة حالة واحدة.

الأسئلة الشائعة

كيف أرمي استثناءً في PHP؟

أنشئ كائن استثناء وارمِه: throw new InvalidArgumentException('Quantity must be positive');. يتوقف التنفيذ عند ذلك السطر ويقفز إلى أقرب كتلة catch مطابقة؛ وإن لم توجد، ينتهي السكربت بخطأ فادح "Uncaught".

كيف أنشئ استثناءً مخصصًا في PHP؟

ارث Exception أو أحد أصنافه الفرعية: class PaymentFailedException extends RuntimeException {}. يكفي ذلك السطر الواحد لرميه والتقاطه بنوعه الخاص. أضف خصائص وبانيًا عندما يحتاج الملتقط إلى بيانات إضافية، واستدعِ parent::__construct($message, $code, $previous).

ما الفرق بين getMessage وgetCode؟

تعيد getMessage() النص الممرر وسيطًا أول للباني، وهو موجّه للبشر وللسجلات. وتعيد getCode() العدد الصحيح الممرر وسيطًا ثانيًا (0 افتراضيًا)، وهو مفيد للفحوص الآلية مثل ربط الأخطاء برموز حالة HTTP.

أي استثناء أرمي في PHP؟

استخدم صنف SPL مدمجًا يطابق المشكلة: InvalidArgumentException لوسيط خاطئ، وDomainException لقيمة خارج المسموح، وRuntimeException لأعطال لا تظهر إلا وقت التشغيل (قرص ممتلئ، انتهاء مهلة)، وLogicException لأخطاء البرمجة. وللأخطاء التي يحتاج مستدعوك إلى التمييز بينها، أنشئ صنفًا فرعيًا خاصًا بك.

ما هو تسلسل الاستثناءات في PHP؟

تمرير الاستثناء الأصلي وسيطًا ثالثًا للباني عندما ترمي استثناءً جديدًا: throw new ImportException('Import failed', 0, $e);. يعطي الاستثناء الجديد السياق، وتعيد $e->getPrevious() عليه السبب الأصلي، فلا تضيع أي تفاصيل.

رسم توضيحي للغات البرمجة في Coddy

تعلّم البرمجة مع Coddy

ابدأ الآن