לא מזמן, בעקבות מאמר אחר שבו כתבתי על cURL, כתבתי מאמר על try-catch ומיד חטפתי (שוב) קיתונות של רותחין (שוב) על למה לא פירטתי על Exceptions ב-PHP. אז אולי המאמר הזה הוא הזדמנות מצוינת לדבר קצת על Exceptions.
Exceptions הם ה-דרך שלנו לדווח על טעויות שונות ומשונות והן מאד מאד שימושיות בשלב הפיתוח ולאחריו. באופן עקרוני, Exception מדווח לנו על תקלות ובעיות שונות בקוד – למשל חוסר הצלחה בחיבור לשירות כלשהו, קלט שלא מתאים למצופה ושלל בעיות נוספות.
בואו ונדגים באמצעות דוגמה מ-php.net, אני אצור פונקציה שמה שהיא עושה זה להחזיר את המספר ההופכי – למשל אם אני דוחף לפונקציה 2, אני אקבל 1/2, אם אני אדחוף לפונקציה 3, אני אקבל 1/3 וכך הלאה.
וכך הפונקציה נראית:
<?php
function inverse($x) {
return 1/$x;
}
למתכנתים מתחילים, הפונקציה הזו נראית סבבה. מתכנתים יותר מנוסים מבינים שיש בעיה פונדמנטלית בפונקציה הזו – היא לא בודקת את הקלט. אם אני אכניס לפונקציה הזו מספר או רחמנא ליצלן אפס, אני אקבל שגיאה! כך למשל:
<?php
function inverse($x) {
return 1/$x;
}
inverse(0);
יניב לי אזהרת PHP לא סימפטית במיוחד:
המתכנת המתחיל בטח יגיד: 'אז מה?!? מי האידיוט שיכניס אפס לפונקציה הזו?' אבל זה בדיוק ההבדל בין תכנות סקלבילי ועמיד בפני בעיות ובין תכנות פריך שכל שינוי ונגיעה בו גורמים לקריסתו ולברדק אדיר.
המתכנת המנוסה יותר, יכניס תנאי שיבדוק את הקלט המתקבל ויחזיר משהו אחר אם הקלט בעייתי:
< ?php
function inverse($x) {
if (!$x) {
print 'Number is zero!!!! ahhhh!';
return 'n/a';
}
else return 1/$x;
}
במקרה הזה המתכנת המעט יותר מנוסה התכונן מראש שיהיה קלט בעייתי ונהג בדרך כלשהי להתמודדות. (אגב, דרך יותר נכונה היא להגדיר מה הקלט שאנחנו רוצים ולא את מה שאנחנו לא רוצים – למשל תנאי שקובע שהקלט הוא מספר שהוא לא אפס, אבל ניחא).
דרך ההתמודדות עם התקלה היא פרימיטיבית למדי – הדפסה היסטרית משהו של 'תקלה חמורה'. אבל, וזה אבל גדול, מדובר בדרך שמתאימה לאפליקציות קטנות בלבד. באפליקציה שבה יש יותר מכמה מאות שורות, דרך התמודדות כזו תהיה בעייתית. לא תמיד אנחנו יכולים להדפיס ולא תמיד אנחנו רוצים שהמשתמש יראה את התקלה. מה עושים? בדיוק בשביל זה יש Exceptions!
דרך השימוש היא פשוטה ביותר – שימו לב!
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
else return 1/$x;
}
inverse(0);
מה יקרה אם אני אריץ את הקוד הזה? ברגע שתתרחש התקלה ה-Exception יקפיץ fatal error וירשום את ההודעה שהגדרנו לו:
'רגע אחד!' חלק מכם בטח אומרים 'מה זה fatal error??? אז בשביל מה אני צריך את זה בכלל???' בדיוק בשביל זה יש את ה-try catch! אם אני מכניס את זה לתוך try-catch, אז אין לי fatal error אלא משהו אחר לגמרי – הנה דוגמה!
< ?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
else return 1/$x;
}
try {
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
התוצאה המתקבלת:
Caught exception: Division by zero.
נחמד, לא?
במאמר על try-catch הזהרתי לא להשתמש במבנה הזה בלי הכרה, אפשר להנות מיתרונות ה-Excpetion בלי fatal error ובלי try-catch. איך עושים את זה? באמצעות הכרזה על פונקציה שמטפלת ב-exceptions ובכל פעם שנזרוק exception הפונקציה הזו תרוץ. למשל:
<?php
function customException($e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
set_exception_handler('customException');
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
else return 1/$x;
}
echo inverse(0);
יש עוד לא מעט בנושא Exceptions, אלו מכם שמתכנתים בגישה מונחית עצמים, ישמחו לדעת שיש class שלם של exceptions שאפשר לרשת ממנו ולעשות איתו דברים מופלאים – כמו להגדיר סוגים שונים של Exceptions – אבל זהו הבסיס. אם לא השתמשתם עד כה ב-exceptions, כדאי לחשוב איך להשתמש בו וליצור סקריפטים יציבים וחזקים יותר.