الاستثناءات في PHP
يتضمّن الإصدار الخامس من PHP نموذج استثناءات مشابهًا للغات البرمجية الأخرى. يمكن رمي الاستثناء والتقاطه في PHP، ويمكن إحاطة الشيفرة بكتلة try
لتسهيل عملية التقاط الاستثناءات المحتملة، ويجب أن ترتبط بكل كتلة try
إما كتلة catch
أو finally
واحدة على الأقل.
يجب أن يكون كائن الاستثناء المرمي نسخة من الصنف Exception
أو صنفًا متفرعًا منه، ويؤدي رمي كائن لا ينتمي إلى هذا الصنف إلى حدوث خطأ من نوع Fatal.
الكتلة catch
يمكن استخدام كتل catch
متعدّدة لالتقاط أصناف مختلفة من الاستثناءات. تستمر عملية تنفيذ الشيفرة الاعتيادية (في حال عدم رمي أي استثناء في الكتلة block) بعد آخر كتلة catch
معرّفة في الشيفرة. يمكن رمي الاستثناءات (أو إعادة رميها) ضمن الكتلة catch
.
لا تنفّذ الشيفرة التي تلي العبارة التي تسبّبت في رمي الاستثناء، وتحاول اللغة العثور على أول كتلة catch مطابقة. في حال عدم التقاط الاستثناء، تطلق اللغة خطأ من نوع PHP مع رسالة "Uncaught Exception ..." إلا إذا كان هناك متحكّم معرّف بواسطة الدالة set_exception_handler()
.
في الإصدار 7.1 وما بعده من اللغة، يمكن لكتلة catch
أن تحدّد استثناءات متعددة باستخدام الرمز (|
). هذه الميزة مفيدة عندما تتعامل اللغة بالطريقة نفسها مع استثناءات مختلفة من تسلسلات هرمية لأصناف مختلفة.
الكتلة finally
في الإصدار 5.5 من اللغة يمكن استخدام الكتلة block بعد أو بدلًا من كتل catch
. تُنفّذ الشيفرة الموجودة ضمن الكتلة finally
دائمًا بعد الشيفرة الموجودة في كتل try
و catch
بغض النظر عمّا إذا كان هناك استثناء مرمي، و قبل العودة إلى عملية تنفيذ الشيفرة الاعتيادية.
ملاحظات
ملاحظة: تستخدم دوال PHP الداخلية بصورة عامة التبليغ عن الأخطاء، أما الاستثناءات فتستخدم من طرف الملحقات الحديثة كائنية التوجّه فقط. ولكن يمكن تحويل الأخطاء ببساطة إلى استثناءات باستخدام الصنف ErrorException
.
نصيحة: تقدم مكتبة PHP القياسية (SPL) عددًا من الاستثناءات المدمجة المفيدة.
أمثلة
المثال 1: رمي استثناء
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// الاستمرار في عملية التنفيذ
echo "Hello World\n";
?>
يعطي المثال السابق المخرجات التالية:
0.2
Caught exception: Division by zero.
Hello World
المثال 2: التعامل مع الاستثناءات بواسطة كتلة finally
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "First finally.\n";
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} finally {
echo "Second finally.\n";
}
// استمرار عملية التنفيذ
echo "Hello World\n";
?>
يعطي المثال السابق المخرجات التالية:
0.2
First finally.
Caught exception: Division by zero.
Second finally.
Hello World
المثال 3: استثناءات متداخلة
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
// رمي الاستثناء مرة أخرى
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>
يعطي المثال السابق المخرجات التالية:
string(4) "foo!"
المثال 4: كتل catch
متعددة للتعامل مع الاستثناءات
<?php
class MyException extends Exception { }
class MyOtherException extends Exception { }
class Test {
public function testing() {
try {
throw new MyException();
} catch (MyException | MyOtherException $e) {
var_dump(get_class($e));
}
}
}
$foo = new Test;
$foo->testing();
?>
يعطي المثال السابق المخرجات التالية:
string(11) "MyException"
توسيع الاستثناءات
يمكن تعريف صنف Exception
خاص من قبل المستخدم وذلك بتوسيع الصنف Exception
المُضمَّن في اللغة، وذلك لإنشاء استثناءات خاصة بالمستخدم.