Exception:

اگر یک خطای خاصی رخ داده باشد، استثناها (Exception) برای تغییر جریان طبیعی یک اسکریپت استفاده می شوند.

استثنا (Exception) چیست؟

همراه با php5 یک روش شی گراییِ جدید (object oriented) برای مقابله با خطاها ارائه داده شد. این روش بدین صورت است که ، اگر خطای خاص و استثنایی (exceptional) رخ دهد، مدیریت خطاها (Handling Exception) روندِ عادیِ اجرایِ کد، را تغییر می دهد. این خطا را می توان با دستورات شرطی (Condition) مشخص کرد. به این  شرط، استثناء یا Exception می گویند.

وقتی یک استثنا راه اندازی می شود چنین چیزهایی اتفاق می افتد:

  1. وضعیت کنونی کد، ذخیره می شود.
  2. ادامه ی اجرای کد به یک تابع دلخواه (سفارشی) و تعیین شده، که استثنا را مدیریت می کند، منتقل می شود.
  3. بسته به وضعیت، ممکن است، بررسی کننده استثنا یا handler اجرای کد را از نقطه ای که کد را ذخیره کرده است از سر بگیرد یا به اجرای اسکریپت خاتمه دهد یا اسکریپت را از یک مکان دیگر در کد ادامه دهد.

در این آموزش متدهای مختلف مدیریت خطا (error handling) آموزش داده شده است. این متدها بدین قرار هستند :

 

نکته: از استثناها فقط باید زمانی استفاده کرد، که خطایی رخ می دهد . و نباید برای پرش از نقطه ای به نقطه ی خاصی در کد استفاده نمود.

نحوه ساده (basic) استفاده از استثناها (Exceptions) :

هنگامی که یک استثنا رخ داده می شود، کدهای بعد از آن اجرا نخواهد شد و 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 و catch

برای جلوگیری از خطای ایجاد شده در مثال بالا، ما باید یک کد مناسب برای مدیریت یک استثنا ایجاد کنیم.

کد استثنای مناسب باید شامل موارد زیر باشد:

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) می کند:

  1. تابع ()checkNum بررسی می کند  آیا عدد بزرگتر از 1 است. اگر بزرگتر از 1 است یک استثنا پرتاب (throw) می کند
  2. تابع ()checkNum درون بلاک "try" فراخوانی می شود
  3. استثنا داخل تابع checkNum()  پرتاب می شود
  4. بلوک "catch" استثناهای پرتاب شده را می گیرد و یک شی ($e) حاوی اطلاعات استثناها ایجاد می کند
  5. پیام خطای استثنا با فراخوانی $e->getMessage() از شیء استثنا فراخوانی می شود

با این حال، یک راه برای دور زدنِ قانون "هر throw باید یک catch داشته باشد" وجود دارد و آن هم طراحی یک مدیریت استثنای سطح بالاست، که در ادامه به آن می پردازیم.

ایجاد یک کلاس استثنا سفارشی (Custom Exception Class)

برای ایجاد یک کلاس استثنای سفارشی، شما باید یک کلاس خاص با توابعی ایجاد کنید که وقتی یک استثنا در 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 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

 

ارسال مجدد استثنا (Re-throwing Exception)

گاهی اوقات، هنگامی که یک استثنا پرتاب می شود، ممکن است بخواهید آن را به طور متفاوتی از آنچه استاندارد است، اداره کنید. این کار را می توانید، با پرتابِ مجددِ یک استثنا از یک بلوک «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” و یا مراتب بالاتر خواهد گشت.

 

تنظیم یک مدیریت کننده استثنا سطح بالا (Top Level Exception Handler) :

تابع 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" تعریف نشده مورداستفاده قرار می گیرد.

 

قوانین مربوط به exception

راهنمای استفاده از تابع ()error_reporting در زبان PHP به‌ منظور نمایش دادن ارورها

مدیریت خطاها در هر زبان برنامه‌نویسی اهمیت بسیار زیادی دارد و 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

به‌ طور کلی، تابع ()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) نمایش داده شوند.

تفاوت توابع ()error_reporting و ()ini_set

همان‌طور که در کد زیر ملاحظه می‌شود، هر دو تابع کار یکسانی انجام می‌دهند:

<?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");