Bạn không nên bắt ngoại lệ trừ khi bạn có ý định làm điều gì đó có ý nghĩa .
"Một cái gì đó có ý nghĩa" có thể là một trong những điều sau:
Xử lý ngoại lệ
Hành động có ý nghĩa rõ ràng nhất là xử lý ngoại lệ, ví dụ: bằng cách hiển thị thông báo lỗi và hủy bỏ thao tác:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
echo "Error while connecting to database!";
die;
}
Ghi nhật ký hoặc dọn dẹp một phần
Đôi khi bạn không biết cách xử lý đúng một ngoại lệ trong một bối cảnh cụ thể; có lẽ bạn thiếu thông tin về "bức tranh lớn", nhưng bạn muốn ghi lại sự thất bại càng gần đến điểm xảy ra càng tốt. Trong trường hợp này, bạn có thể muốn bắt, đăng nhập và ném lại:
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
logException($e); // does something
throw $e;
}
Một kịch bản liên quan là nơi bạn đang ở đúng nơi để thực hiện một số dọn dẹp cho hoạt động thất bại, nhưng không quyết định cách xử lý lỗi ở cấp cao nhất. Trong các phiên bản PHP trước, điều này sẽ được thực hiện như
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
catch (Exception $e) {
$connect->disconnect(); // we don't want to keep the connection open anymore
throw $e; // but we also don't know how to respond to the failure
}
PHP 5.5 đã giới thiệu finally
từ khóa, vì vậy đối với các kịch bản dọn dẹp, bây giờ có một cách khác để tiếp cận điều này. Nếu mã dọn dẹp cần chạy bất kể điều gì đã xảy ra (nghĩa là cả lỗi và thành công), giờ đây bạn có thể làm điều này trong khi cho phép minh bạch mọi ngoại lệ bị ném để tuyên truyền:
$connect = new CONNECT($db, $user, $password, $driver, $host);
try {
$connect->insertSomeRecord();
}
finally {
$connect->disconnect(); // no matter what
}
Lỗi trừu tượng (có chuỗi ngoại lệ)
Trường hợp thứ ba là nơi bạn muốn nhóm một cách hợp lý nhiều thất bại có thể xảy ra dưới một chiếc ô lớn hơn. Một ví dụ cho nhóm hợp lý:
class ComponentInitException extends Exception {
// public constructors etc as in Exception
}
class Component {
public function __construct() {
try {
$connect = new CONNECT($db, $user, $password, $driver, $host);
}
catch (Exception $e) {
throw new ComponentInitException($e->getMessage(), $e->getCode(), $e);
}
}
}
Trong trường hợp này, bạn không muốn người dùng Component
biết rằng nó được triển khai bằng kết nối cơ sở dữ liệu (có thể bạn muốn giữ các tùy chọn của mình mở và sử dụng lưu trữ dựa trên tệp trong tương lai). Vì vậy, đặc điểm kỹ thuật của bạn Component
sẽ nói rằng "trong trường hợp lỗi khởi tạo, ComponentInitException
sẽ bị ném". Điều này cho phép người tiêu dùng Component
nắm bắt các ngoại lệ của loại dự kiến đồng thời cho phép mã gỡ lỗi truy cập tất cả các chi tiết (phụ thuộc vào triển khai) .
Cung cấp bối cảnh phong phú hơn (với chuỗi ngoại lệ)
Cuối cùng, có những trường hợp bạn có thể muốn cung cấp thêm ngữ cảnh cho ngoại lệ. Trong trường hợp này, sẽ rất hợp lý khi bọc ngoại lệ trong một trường hợp khác chứa nhiều thông tin hơn về những gì bạn đã cố gắng thực hiện khi xảy ra lỗi. Ví dụ:
class FileOperation {
public static function copyFiles() {
try {
$copier = new FileCopier(); // the constructor may throw
// this may throw if the files do no not exist
$copier->ensureSourceFilesExist();
// this may throw if the directory cannot be created
$copier->createTargetDirectory();
// this may throw if copying a file fails
$copier->performCopy();
}
catch (Exception $e) {
throw new Exception("Could not perform copy operation.", 0, $e);
}
}
}
Trường hợp này tương tự như ở trên (và ví dụ có lẽ không phải là trường hợp tốt nhất có thể xảy ra), nhưng nó minh họa điểm cung cấp thêm ngữ cảnh: nếu một ngoại lệ được ném, nó cho chúng ta biết rằng sao chép tệp thất bại. Nhưng tại sao nó lại thất bại? Thông tin này được cung cấp trong các trường hợp ngoại lệ được bao bọc (trong đó có thể có nhiều hơn một cấp nếu ví dụ phức tạp hơn nhiều).
Giá trị của việc này được minh họa nếu bạn nghĩ về một kịch bản, ví dụ như việc tạo một UserProfile
đối tượng khiến các tệp bị sao chép vì hồ sơ người dùng được lưu trữ trong các tệp và nó hỗ trợ ngữ nghĩa giao dịch: bạn có thể "hoàn tác" các thay đổi vì chúng chỉ được thực hiện trên một bản sao của hồ sơ cho đến khi bạn cam kết.
Trong trường hợp này, nếu bạn đã làm
try {
$profile = UserProfile::getInstance();
}
và kết quả là một lỗi ngoại lệ "Không thể tạo thư mục đích", bạn sẽ có quyền bị nhầm lẫn. Việc gói ngoại lệ "lõi" này trong các lớp của các ngoại lệ khác cung cấp ngữ cảnh sẽ giúp xử lý lỗi dễ dàng hơn nhiều ("Tạo bản sao hồ sơ không thành công" -> "Không thể tạo thư mục tệp" -> "Không thể tạo thư mục đích").