5. Tạo một lớp ngoại lệ tùy chỉnh

Để tạo một trình xử lý ngoại lệ tùy chỉnh, bạn phải tạo một lớp đặc biệt với các hàm có thể được gọi khi một ngoại lệ xảy ra trong PHP. Lớp này phải được kế thừa lớp Exception. Lớp ngoại lệ tùy chỉnh kế thừa các thuộc tính từ lớp Exception của PHP và bạn có thể thêm các hàm tùy chỉnh vào nó.

Bạn thậm chí có thể định nghĩa các trình xử lý ngoại lệ tùy chỉnh của riêng bạn để xử lý các loại ngoại lệ khác nhau theo một cách khác. Nó cho phép bạn sử dụng một khối catch riêng cho từng loại ngoại lệ. Bạn có thể định nghĩa một ngoại lệ tùy chỉnh bằng cách mở rộng lớp Exception, vì Exception là lớp cơ sở cho tất cả các ngoại lệ. Lớp ngoại lệ tùy chỉnh kế thừa tất cả các thuộc tính và phương thức từ lớp Exception của PHP. Bạn cũng có thể thêm các phương thức tùy chỉnh của mình vào lớp ngoại lệ tùy chỉnh.

Ví dụ:

<?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 = "someone@example...com";
try {
  //check email hợp lệ
  if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
    //throw exception nếu email không hợp lệ
    throw new customException($email);
  }
} 
catch (customException $e) {
  //hiển thị message
  echo $e->errorMessage();
}
?>

Kết quả:

Error on line 17 in C:\xampp\htdocs\php\vi-du-ngoai-le-3.php: someone@example…com is not a valid E-Mail address

Lớp mới là một bản sao của lớp Exception cũ với việc bổ sung hàm errorMessage(). Vì nó là một bản sao của lớp cũ, và nó kế thừa các thuộc tính và phương thức từ lớp cũ, chúng ta có thể sử dụng các phương thức lớp Exception như getLine()getFile()getMessage(). Đoạn code trên ném một ngoại lệ và bắt nó với một lớp ngoại lệ tùy chỉnh:

  • Lớp CustomException() được tạo ra như là một phần mở rộng của lớp Exception. Bằng cách này, nó kế thừa tất cả các phương thức và thuộc tính từ lớp ngoại lệ cũ.
  • Hàm errorMessage() được tạo. Hàm này trả về một thông báo lỗi nếu địa chỉ e-mail không hợp lệ.
  • Biến $email được đặt thành một chuỗi không phải là địa chỉ e-mail hợp lệ.
  • Khối “try” được thực hiện và một ngoại lệ được ném vì địa chỉ e-mail không hợp lệ.
  • Khối “catch” bắt được ngoại lệ và hiển thị thông báo lỗi.

6. Xử lý nhiều ngoại lệ

Có thể cho một tập lệnh sử dụng nhiều ngoại lệ để kiểm tra nhiều điều kiện. Có thể sử dụng một số mệnh đề if..else, switch hoặc tổ hợp nhiều ngoại lệ. Những ngoại lệ này có thể sử dụng các lớp ngoại lệ khác nhau và trả về các thông báo lỗi khác nhau.

Ví dụ:

<?php
class CustomException2 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 = "someone@example.com"; 
try {
  //check if
  if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
    //throw exception nếu email không hợp lệ
    throw new CustomException2($email);
  }
  //kiểm tra tồn tại chuỗi "example" trong địa chỉ email
  if(strpos($email, "example") !== FALSE) {
    throw new Exception("$email is an example e-mail.");
  }
}
catch (CustomException2 $e) {
  echo $e->errorMessage();
} 
catch(Exception $e) {
  echo $e->getMessage();
}
?>

Kết quả: someone@example.com is an example e-mail.

Đoạn mã trên kiểm tra hai điều kiện và ném một ngoại lệ nếu bất kỳ điều kiện nào không được đáp ứng

  • Lớp CustomException2() được tạo ra như là một phần mở rộng của lớp Exception. Bằng cách này, nó kế thừa tất cả các phương thức và thuộc tính từ lớp ngoại lệ cũ.
  • Hàm errorMessage() được tạo. Hàm này trả về một thông báo lỗi nếu địa chỉ e-mail không hợp lệ.
  • Biến $email được đặt thành một chuỗi là địa chỉ e-mail hợp lệ, nhưng chứa chuỗi “example”.
  • Khối “try” được thực hiện và một ngoại lệ không được ném vào điều kiện đầu tiên.
  • Điều kiện thứ hai gây ra một ngoại lệ vì e-mail chứa chuỗi “example”.
  • Khối “catch” bắt được ngoại lệ và hiển thị thông báo lỗi chính xác.

7. Thiết lập trình xử lý ngoại lệ cao cấp

Hàm set_exception_handler() thiết lập một chức năng người dùng định nghĩa để xử lý tất cả các trường hợp ngoại lệ còn tự do.

Ví dụ:

<?php
function myException($exception) {
  echo "<b>Exception:</b> " . $exception->getMessage();
} 
set_exception_handler('myException');
throw new Exception('Uncaught Exception occurred');
?>

Kết quả: Exception: Uncaught Exception occurred

Trong đoạn mã trên không có khối “catch”. Thay vào đó, trình xử lý ngoại lệ cấp cao nhất được kích hoạt. Hàm này nên được sử dụng để bắt các ngoại lệ chưa được catch.

8. Trình xử lý ngoại lệ toàn cầu

Trong Global Exception Handler , nó đặt bộ xử lý ngoại lệ mặc định nếu một ngoại lệ không được bắt trong khối try / catch. Nếu không có khối nào khác được gọi thì hàm set_exception_handler có thể đặt một hàm sẽ được gọi ở vị trí của hàm catch. Việc thực thi sẽ dừng lại sau khi lệnh xử lý ngoại lệ được gọi.

Như chúng ta đã thảo luận trước đó trong chương này nếu một ngoại lệ không bị bắt, PHP tạo ra Lỗi nghiêm trọng với thông báo ‘Uncaught Exception …’. Thông báo lỗi này có thể chứa thông tin nhạy cảm như tên file và số dòng nơi xảy ra sự cố. Nếu bạn không muốn để lộ thông tin đó cho người dùng, bạn có thể tạo một hàm tùy chỉnh và đăng ký nó với hàm set_exception_handler() để xử lý tất cả các ngoại lệ chưa được phát hiện.

Ví dụ:

<?php
function handleUncaughtException($e){
    // Hiển thị thông báo lỗi chung cho người dùng
    echo "Opps! Có lỗi xảy ra. Hãy thử lại hoặc liên hệ với chúng tôi nếu còn lỗi.";
    // Lấy thông tin lỗi
    $error = "Uncaught Exception: " . $message = date("Y-m-d H:i:s - ");
    $error .= $e->getMessage() . " trong file " . $e->getFile() . " ở dòng " . $e->getLine() . "\n";    
    // Ghi nhật ký chi tiết lỗi
    error_log($error, 3, "var/log/exceptionLog.log");
} 
// Đăng ký xử lý ngoại lệ tùy chỉnh
set_exception_handler("handleUncaughtException");
// Ném ra một ngoại lệ
throw new Exception("Kiểm tra ngoại lệ!");
?>

Lưu ý

Một ngoại lệ chưa được lưu sẽ luôn dẫn đến việc chấm dứt tập lệnh. Vì thế, nếu bạn muốn tập lệnh tiếp tục thực thi ngoài điểm xảy ra ngoại lệ, bạn phải có ít nhất một khối catch tương ứng cho mỗi khối try.

9. Đối tượng ngoại lệ

Đối tượng Ngoại lệ chứa thông tin về lỗi hoặc hành vi không mong muốn mà hàm gặp phải.

Giá trị tham số

Tham Số Mô tả
Message Không bắt buộc. Một chuỗi mô tả lý do tại sao ngoại lệ được ném ra
Code Không bắt buộc. Một số nguyên có thể được sử dụng để dễ dàng phân biệt ngoại lệ này với những ngoại lệ khác cùng loại
Previous Không bắt buộc. Nếu ngoại lệ này được đưa vào một khối bắt của một ngoại lệ khác, bạn nên chuyển ngoại lệ đó vào tham số này

Phương pháp

Phương pháp Mô Tả
getMessage() Trả về một chuỗi mô tả lý do tại sao ngoại lệ được ném ra
getPrevious() Nếu ngoại lệ này được kích hoạt bởi một ngoại lệ khác, phương thức này trả về ngoại lệ trước đó. Nếu không, thì nó trả về null
getCode() Trả về mã ngoại lệ
getFile() Trả về đường dẫn đầy đủ của tệp trong đó ngoại lệ đã được ném
getLine() Trả về số dòng của dòng mã đã ném ngoại lệ

Ví dụ: Thông tin đầu ra về một ngoại lệ đã được ném:

<?php
function divide($dividend, $divisor) {
  if($divisor == 0) {
    throw new Exception("Chia cho số không", 1);
  }
  return $dividend / $divisor;
}
try {
  echo divide(5, 0);
} catch(Exception $ex) {
  $code = $ex->getCode();
  $message = $ex->getMessage();
  $file = $ex->getFile();
  $line = $ex->getLine();
  echo "Ngoại lệ được đưa vào tệp $file trên trên dòng $line: [Code $code]$message";
}
?>

10. Quy tắc cho trường hợp ngoại lệ

Mình sẽ đưa ra một số quy tắc mà mình nghĩ các bạn sẽ cần đấy:

  • Code nên được đặt trong khối try, để giúp bắt (catch) các ngoại lệ có thể xảy ra.
  • Mỗi khối try hoặc “throw” phải có ít nhất một khối catch tương ứng.
  • Nhiều khối catch có thể được sử dụng để bắt các lớp ngoại lệ khác nhau.
  • Có thể ném các ngoại lệ (hoặc được ném lại) vào khối catch trong khối try.

Cuối cùng thì mình muốn nói với các bạn rằng bài viết này đơn thuần là giới thiệu về exception trong PHP thuần, tuy nhiên ngày nay công nghệ ngày một phát triển, các framework cũng dần hiện đại hóa, việc bắt các exception thì hầu hết họ đã làm tương đối ổn. Tuy nhiên đối với nhiều dự án chúng ta cần phải hiểu sâu về các kiến thức cơ bản và tự mình có thể customize exception và hỗ trợ việc debug được dễ dàng hơn. Ngoài ra thì việc viết code trong một team thì code của bạn có thể người khác sẽ dùng lại nên code càng sáng sủa bao nhiêu thì thuận tiện cho người sau dùng lại dễ dàng hơn.