اگر یک خطای خاصی رخ داده باشد، استثناها (Exception) برای تغییر جریان طبیعی یک اسکریپت استفاده می شوند.
همراه با php5 یک روش شی گراییِ جدید (object oriented) برای مقابله با خطاها ارائه داده شد. این روش بدین صورت است که ، اگر خطای خاص و استثنایی (exceptional) رخ دهد، مدیریت خطاها (Handling Exception) روندِ عادیِ اجرایِ کد، را تغییر می دهد. این خطا را می توان با دستورات شرطی (Condition) مشخص کرد. به این شرط، استثناء یا Exception می گویند.
وقتی یک استثنا راه اندازی می شود چنین چیزهایی اتفاق می افتد:
در این آموزش متدهای مختلف مدیریت خطا (error handling) آموزش داده شده است. این متدها بدین قرار هستند :
نکته: از استثناها فقط باید زمانی استفاده کرد، که خطایی رخ می دهد . و نباید برای پرش از نقطه ای به نقطه ی خاصی در کد استفاده نمود.
هنگامی که یک استثنا رخ داده می شود، کدهای بعد از آن اجرا نخواهد شد و PHP سعی خواهد کرد بلوک تطابقی استثنای مذکور که "catch" نامیده می شود را پیدا کند.
اگر php برای یک استثنا، بلاک تطابقی آنرا نیابد، یک fatal error (خطای مهلک)، همراه با پیغام "Uncaught Exception" یعنی "استثنا یافت نشد" را صادر می کند.
در کد زیر، یک exception را در برنامه ایجاد کرده ایم، بدون این که آن را ردگیری کنیم یعنی بدون آنکه برای آن بلوک Catch تعریف نماییم :
<?php
//create function with an exception
function checkNum($number) {
if($number>1) {
throw new Exception("Value must be 1 or below");
}
return true;
}
//trigger exception
checkNum(2);
?>
کد بالا یک خطایی مثل این خواهد داشت:
Fatal error: Uncaught exception 'Exception'
with message 'Value must be 1 or below' in C:\webfolder\test.php:6
Stack trace: #0 C:\webfolder\test.php(12):
checkNum(28) #1 {main} thrown in C:\webfolder\test.php on line 6
برای جلوگیری از خطای ایجاد شده در مثال بالا، ما باید یک کد مناسب برای مدیریت یک استثنا ایجاد کنیم.
کد استثنای مناسب باید شامل موارد زیر باشد:
Try - تابعی که از یک استثنا استفاده میکند، باید در بلوک «try» باشد. اگر استثنا اجرا نشود، فرآیند اجرای کد به صورت معمول ادامه خواهد یافت. اما اگر استثنا اجرا شود، در اصطلاح می گوییم که آن استثنا پرتاب یا thrown شده است.
Throw – بخش "throw" مشخص می کند، که چگونه شما یک استثنا را راه اندازی می کنید. هر “throw” بایستی حداقل یک بخش “catch” داشته باشد.
Catch - بلاک “Catch” استثنای رخ داده شده را می گیرد و یک شیء "$e" حاوی اطلاعات Exception را ایجاد می کند.
اجازه دهید تا یک استثنا را با کد معتبر راه اندازی کنیم:
<?php
//create function with an exception
function checkNum($number) {
if($number>1) {
throw new Exception("Value must be 1 or below");
}
return true;
}
//trigger exception in a "try" block
try {
checkNum(2);
//If the exception is thrown, this text will not be shown
echo 'If you see this, the number is 1 or below';
}
//catch exception
catch(Exception $e) {
echo 'Message: ' .$e->getMessage();
}
?>
کد بالا یک خطایی مثل این خواهد داشت:
Message: Value must be 1 or below
توضیح مثال بالا:
کد بالا یک استثناء را فعال می کند و آن را دریافت (catch) می کند:
با این حال، یک راه برای دور زدنِ قانون "هر throw باید یک catch داشته باشد" وجود دارد و آن هم طراحی یک مدیریت استثنای سطح بالاست، که در ادامه به آن می پردازیم.
برای ایجاد یک کلاس استثنای سفارشی، شما باید یک کلاس خاص با توابعی ایجاد کنید که وقتی یک استثنا در PHP فعال می شود، آنها فراخوانی شوند. این کلاس باید یک زیرمجموعه یا فرزند از کلاس اصلی exception class در PHP باشد.
کلاسِ استثنا دلخواه یا سفارشی تان، خواص (properties) را از کلاس اصلی PHP Exception Class به ارث می برد و شما می توانید توابع دلخواهتان را به آن اضافه کنید.
اجازه دهید یک کلاس استثنا سفارشی ایجاد کنیم :
<?php
class customException extends Exception {
public function errorMessage() {
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = " clicksite.ir@gmail...com ";
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new customException($email);
}
}
catch (customException $e) {
//display custom message
echo $e->errorMessage();
}
?>
خروجی :
Error on line 17 in C:\wamp64\www\cod\index.php: clicksite.ir@gmail...com is not a valid E-Mail address
کلاس جدید یک نسخه از کلاس exception class قدیمی است که به آن تابع errorMessage را اضافه نموده ایم. از آنجا که کلاس جدید، یک کپی از کلاس قدیمی است و خواص و متدها (properties and methods) را از کلاس قدیمی به ارث می برد، می توانیم از متدهای کلاس استثنای قدیمی، مانند getLine () و getFile () و getMessage () استفاده کنیم.
توضیح مثال بالا:
کد بالا یک استثنا را پرتاب(throw) می کند و این استثنا توسط کلاس استثنایی که خودمان تعریف نموده ایم، گرفته میشود(catch).
ممکن است یک اسکریپت از چندین استثناء(multiple exceptions) استفاده کند تا چندین شرایط(multiple conditions) را بررسی کند.
همچنین می توانید از چندین بلوک if..else، یا یک switch یا استثناء چندگانه (multiple exceptions) استفاده کنید.
این استثنائات می توانند از کلاس های استثنای مختلفی (exception classes) استفاده کنند و پیام های خطای مختلف را برگردانند.
<?php
class customException extends Exception {
public function errorMessage() {
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "clicksite.ir@gmail.com";
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new customException($email);
}
//check for "gmail" in mail address
if(strpos($email, "gmail") !== FALSE) {
throw new Exception("$email is an gmail e-mail");
}
}catch (customException $e) {
echo $e->errorMessage();
}catch(Exception $e) {
echo $e->getMessage();
}
?>
خروجی:
clicksite.ir@gmail.com is an gmail e-mail
توضیح مثال:
<?php
class customException extends Exception {
public function errorMessage() {
//error message
$errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
.': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
return $errorMsg;
}
}
$email = "clicksite.ir@gmail...com";
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new customException($email);
}
//check for "example" in mail address
if(strpos($email, "gmail") !== FALSE) {
throw new Exception("$email is an gmail e-mail");
}
}
catch(Exception $e) {
echo $e->getMessage();
}
?>
خروجی:
clicksite.ir@gmail...com
گاهی اوقات، هنگامی که یک استثنا پرتاب می شود، ممکن است بخواهید آن را به طور متفاوتی از آنچه استاندارد است، اداره کنید. این کار را می توانید، با پرتابِ مجددِ یک استثنا از یک بلوک «catch» عملی نمایید. اسکریپتی که یک برنامه نویس می نویسید، باید اشتباهات برنامه را از دید کاربران پنهان کند.خطاهای سیستم ممکن است برای برنامه نویس مهم باشداما دیدن آنها برای کاربران معمولی جالب و جذاب نیست.
برای این کار لازم است که برنامه نویس استثناها را با یک پیغام دوستانه، دوباره پرتاب کند(re-throw the exception):
<?php
class customException extends Exception {
public function errorMessage() {
//error message
$errorMsg = $this->getMessage().' is not a valid E-Mail address.';
return $errorMsg;
}
}
$email = "clicksite.ir@gmail...com";
try {
try {
//check if
if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
//throw exception if email is not valid
throw new Exception($email);
}
}
catch(Exception $e) {
//re-throw exception
throw new customException($email);
}
}
catch (customException $e) {
//display custom message
echo $e->errorMessage();
}
?>
خروجی :
clicksite.ir@gmail...com is not a valid E-Mail address.
توضیح مثال :
اگر خطا یا exception در بلوک “try” جاری آن گرفته نشود، برنامه به دنبال دریافت آن در بلوک “catch” و یا مراتب بالاتر خواهد گشت.
تابع set_exception_handler() نامِ یک تابعِ تعریف شده توسط کاربر را می گیرد.و از آن برای همه استثناهایی که بلوک catch برایشان تعریف نشده است استفاده می کند.
<?php
function myException($exception) {
echo "<b>Exception:</b> " . $exception->getMessage();
}
set_exception_handler('myException');
throw new Exception('Uncaught Exception occurred');
?>
خروجی:
Exception: Uncaught Exception occurred
در کد بالا هیچ بلوک "catch" ی وجود نداشت. در عوض یک مدیریت کننده exception سطح بالا(top level exception handler) تعریف شده بود.exception ما بلوک catch نداشت، این تابع برای استثاهایی که برایشان بلوک "Catch" تعریف نشده مورداستفاده قرار می گیرد.
مدیریت خطاها در هر زبان برنامهنویسی اهمیت بسیار زیادی دارد و PHP نیز از این قضیه مستثنی نیست که خوشبختانه تنظیمات مدیریت خطا در PHP بسیار راحت است که در ادامه یک راهنمای خلاصه و کاربردی برای دولوپرهای تازهکار این زبان جمعآوری کردهایم تا به راحتی بتوانند خطاها و اِکسپشنها را در این زبان برنامهنویسی محبوب مدیریت کنند.
سطح خطاها در زبان PHP
Constant (مقدار ثابت) و مقادیر عددی که در ادامه معرفی میشوند را میتوانید به عنوان آرگومان در تابع ()error_reporting استفاده کرده و خطاهای اسکریپت خود را به راحتی مدیریت کنید:
مقدار | کانستنت | توضیحات |
1 | E_ERROR | این کانستنت ارورهای به اصطلاح Fatal که اجرای ادامهٔ اسکریپت را متوقف میکنند، نمایش میدهد. |
2 | E_WARNING | این کانستنت Warning به وجود آمده در حین اجرای اسکریپت (Run-time) را نمایش میدهد (لازم به ذکر است که این هشدارها باعث متوقف شدن ادامهٔ اجرای اسکریپت نخواهند شد.) |
4 | E_PARSE | این کانستنت ارورهایی که توسط مُفسر زبان PHP در حین کامپایل وب اپلیکیشن به وجود میآیند را نمایش میدهد. |
8 | E_NOTICE | این کانستنت Notice به وجود آمده در حین اجرای اسکریپت را نمایش میدهد. به عبارت دیگر، چیزی که به نظر میرسد یک ارور باشد! |
16 | E_CORE_ERROR | این کانستنت ارورهای به اصطلاح Fatal در فرایند استارت شدن PHP را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_ERROR است با این تفاوت که توسط هستهٔ PHP ایجاد میگردد.) |
32 | E_CORE_WARNING | این کانستنت ارورهای به اصطلاح Non-fatal در فرایند استارت شدن PHP را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_WARNING است با این تفاوت که توسط هستهٔ PHP ایجاد میگردد.) |
64 | E_COMPILE_ERROR | این کانستنت ارورهایی از نوع Fatal که در زمان کامپایل به وجود میآیند را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_ERROR است با این تفاوت که توسط موتور Zend ایجاد میگردد.) |
128 | E_COMPILE_WARNING | این کانستنت ارورهای به اصطلاح Non-fatal که در زمان کامپایل به وجود میآیند را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_WARNING است با این تفاوت که توسط موتور Zend ایجاد میگردد.) |
256 | E_USER_ERROR | این کانستنت ارورهای به اصطلاح Fatal که توسط کاربر ایجاد میشوند را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_ERROR است با این تفاوت که توسط تابع ()trigger_error زبان PHP ایجاد میگردد.) |
512 | E_USER_WARNING | این کانستنت هشدارهای به اصطلاح Non-fatal که توسط کاربر ایجاد میشوند را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_WARNING است با این تفاوت که توسط تابع ()trigger_error زبان PHP ایجاد میگردد.) |
1024 | E_USER_NOTICE | این کانستنت Notice ایجاد شده توسط کاربر را نمایش میدهد (لازم به ذکر است که این کانستنت شبیه به E_NOTICE است با این تفاوت که توسط تابع ()trigger_error زبان PHP ایجاد میگردد.) |
2048 | E_STRICT | این کانستنت امکانی را فراهم میسازد تا PHP پیشنهاداتی به منظور اِعمال در سورسکد ارائه دهد تا از سازگاری کد در آینده اطمینان حاصل شود. |
4096 | E_RECOVERABLE_ERROR | این کانستنت ارورهای به اصطلاح Catchable Fatal را نمایش میدهد. به عبارت دیگر، نمایش میدهد که احتمالاً یک ارور خطرناک ایجاد شده است اما موتور زِند را در شرایطی بیثبات ترک نمیکند و اگر ارورهایی از این دست توسط دولوپر هندل نشوند، اپلیکیشن همانگونه که در شرایط E_ERROR متوقف میشود، از ادامه باز میایستد. |
8192 | E_DEPRECATED | این کانستنت Notice ایجاد شده در حین Run-time را نمایش میدهد که با فعال کردن این دسته از ارورها، هشدارهایی در مورد کد نمایش داده خواهند شد که در نسخههای بعدی PHP کار نخواهند کرد. |
16384 | E_USER_DEPRECATED | این کانستنت پیامهای هشدارآمیز ایجاد شده توسط کاربر را نمایش میدهد (لازم به ذکر است که این مقدار شبیه به E_DEPRECATED است با این تفاوت که توسط تابع ()trigger_error زبان PHP ایجاد میگردد.) |
32767 | E_ALL | این کانستنت تمامی ارورها و هشدارهایی که در نسخهٔ PHP نصب شده روی سرور ساپورت میشوند را نمایش میدهد (لازم به ذکر است که از مقدار 1- نیز به جای E_ALL میتوان استفاده کرد.) |
به طور کلی، تابع ()error_reporting مشخص میکند که کدام دسته از ارورهای اسکریپت ما در معرض دید کاربر قرار بگیرند و لازم به ذکر است که پارامتر ورودی این تابع (Level) اختیاری است و مشخص میسازد که چه سطحی از نمایش ارورها در نظر گرفته شود که هم از اعداد از پیش تعریف شده و هم از کانستنتهایی که در بالا بدانها اشاره شد میتوان به عنوان پارامتر ورودی استفاده کرد (توصیه میشود که تا حد امکان به جای اعداد از کانستنتها استفاده شود تا در ورژنهای آتی این زبان با هیچگونه کانفلیکتی برخورد نکنید.)
برای روشنتر شدن نحوهٔ هندل کردن ارورها در زبان PHP، در ادامه چند مثال از نحوهٔ استفاده از این تابع برای مدیریت خطاها در این زبان آورده شده است:
<?php
// Turn off all error reporting
error_reporting(0);
// Report all PHP errors
error_reporting(E_ALL);
error_reporting(-1);
// Report all errors except E_NOTICE
error_reporting(E_ALL ^ E_NOTICE);
//Report errors and warnings
error_reporting(E_ERROR | ERROR_WARNING);
همانطور که میتوان حدس زد، مقدار ورودی 0 برای این تابع که در خط سوم در نظر گرفته شده منجر بدین خواهد گشت تا هیچ اروری نمایش داده نشود. در خطوط ششم و هفتم هم میبینیم که مقادیر E_ALL و 1- در نظر گرفته شدهاند که باعث میگردند تمامی ارورهای پیاچپی نمایش داده شوند (البته لازم به ذکر است که یکی از این خطوط برای نمایش خطاها کافی است و صرفاً از این جهت هر دو مورد را آوردهایم که با هر دو روش آشنا شوید.) در خط دهم نیز دستور دادهایم که تمامی ارورها (E_ALL) نمایش داده شوند به جزء E_NOTICE و در نهایت در خط سیزدهم هم گفتهایم هم ارورها (E_ERROR) و هم هشدارها (ERROR_WARNING) نمایش داده شوند.
همانطور که در کد زیر ملاحظه میشود، هر دو تابع کار یکسانی انجام میدهند:
<?php
// The same as error_reporting(E_ALL);
ini_set("error_reporting", E_ALL);
در واقع، کاری که تابع ()ini_set انجام میدهد این است که تنظیمات فایل php.ini را به اصطلاح Override (رونویسی) میکند. در پایان هم بایستی یادآور شویم برای آنکه ارورها نمایش داده شوند، حتماً بایستی مقدار display_errors در فایل php.ini و یا تابع ()ini_set برابر با 1 یا On قرار داده شده باشد:
<?php
ini_set("display_errors", 1); // Or ini_set("display_errors", "On");