Bắt nhiều loại ngoại lệ trong một khối bắt


243

Tôi muốn một cách sạch hơn để có được các chức năng sau, để bắt AErrorBErrortrong một khối:

try
{
    /* something */
}
catch( AError, BError $e )
{
    handler1( $e )
}
catch( Exception $e )
{
    handler2( $e )
}

Có cách nào để làm điều này? Hay tôi phải bắt chúng riêng?

AErrorBerrorcó một lớp cơ sở được chia sẻ, nhưng họ cũng chia sẻ nó với các loại khác mà tôi muốn tìm đến handler2, vì vậy tôi không thể bắt lớp cơ sở.


7
Chỉ cần thêm điều này như một lưu ý phụ: Một RFC đã được đệ trình để nắm bắt nhiều ngoại lệ. Hãy xem liệu tính năng này có được sử dụng ngôn ngữ PHP không ... wiki.php.net/rfc/multipl-catch
SimonSimCity

10
^ Tính năng này đã được triển khai trong PHP 7.1
Subin

Câu trả lời:


351

Cập nhật:

Kể từ PHP 7.1, điều này có sẵn.

Cú pháp là:

try
{
    // Some code...
}
catch(AError | BError $e)
{
    // Handle exceptions
}
catch(Exception $e)
{
    // Handle the general case
}

Tài liệu: https://www.php.net/manual/en/lingu.exceptions.php#example-287

RFC: https://wiki.php.net/rfc/multipl-catch

Cam kết: https://github.com/php/php-src/commit/0aed2cc2a440e7be17552cc669d71fdd24d1204a


Đối với PHP trước 7.1:

Mặc dù những câu trả lời khác nói gì, bạn có thể nắm bắt AErrorBErrortrong cùng một khối (sẽ dễ dàng hơn nếu bạn là người xác định ngoại lệ). Ngay cả khi có những trường hợp ngoại lệ bạn muốn "vượt qua", bạn vẫn có thể xác định thứ bậc để phù hợp với nhu cầu của mình.

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

Sau đó:

catch(LetterError $e){
    //voodoo
}

Như bạn có thể thấy ở đâyở đây , ngay cả các SPLngoại lệ mặc định cũng có một hệ thống phân cấp mà bạn có thể tận dụng. Ngoài ra, như đã nêu trong Hướng dẫn PHP :

Khi một ngoại lệ được đưa ra, mã theo câu lệnh sẽ không được thực thi và PHP sẽ cố gắng tìm khối bắt phù hợp đầu tiên.

Điều này có nghĩa là bạn cũng có thể có

class CError extends LetterError {}

mà bạn cần xử lý khác với AErrorhoặc BError, vì vậy câu lệnh bắt của bạn sẽ giống như thế này:

catch(CError $e){
    //voodoo
}
catch(LetterError $e){
    //voodoo
}

Nếu bạn gặp trường hợp có hai mươi hoặc nhiều trường hợp ngoại lệ thuộc về cùng một siêu lớp và bạn cần xử lý năm (hoặc bất kỳ nhóm lớn nào) theo cách này và phần còn lại, bạn có thể VẪN làm điều này.

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

Và sau đó:

catch (Group1 $e) {}

Sử dụng OOP khi có ngoại lệ là rất mạnh mẽ. Sử dụng những thứ như get_classhoặc instanceoflà hack, và nên tránh nếu có thể.

Một giải pháp khác tôi muốn thêm là đưa chức năng xử lý ngoại lệ vào phương thức riêng của mình.

Bạn có thể có

function handleExceptionMethod1(Exception $e)
{
    //voodoo
}

function handleExceptionMethod2(Exception $e)
{
    //voodoo
}

Giả sử có hoàn toàn không có cách nào bạn có thể kiểm soát phân cấp lớp ngoại lệ hoặc giao diện (và hầu như lúc nào cũng sẽ là một chiều), bạn có thể làm những điều sau đây:

try
{
    stuff()
}
catch(ExceptionA $e)
{
    $this->handleExceptionMethod1($e);
}
catch(ExceptionB $e)
{
    $this->handleExceptionMethod1($e);
}
catch(ExceptionC $e)
{
    $this->handleExceptionMethod1($e);
}
catch(Exception $e)
{
    $this->handleExceptionMethod2($e);
}

Theo cách này, bạn vẫn chỉ có một vị trí mã duy nhất bạn phải sửa đổi nếu cơ chế xử lý ngoại lệ của bạn cần thay đổi và bạn đang làm việc trong các cấu trúc chung của OOP.


4
Đây là một phiếu bầu khác cho điều này là câu trả lời chính xác. Thật không may, những thứ như những gì được nói trong câu trả lời được chấp nhận và thực tế là nó được chấp nhận là câu trả lời chính xác, là điều khiến PHP trở nên điên rồ.
borfast

Đây phải là câu trả lời được chấp nhận. Mặc dù, nó giả định rằng bạn có thể sửa đổi các tập tin. AErrorcó thể được thực hiện trong một thư viện / tập tin được cập nhật bởi bên thứ ba.
Kayla

@ WaffStealer654 Bạn vẫn có thể phân lớp các tệp và khiến chúng thực hiện nhóm của bạn, ngay cả khi bạn không thể chỉnh sửa các tệp trực tiếp. Điều đó sẽ cho rằng bạn có thể đưa ra các ngoại lệ, nhưng bạn chỉ có thể bọc cơ chế cấp cơ sở nhất trong đó ngoại lệ sẽ được ném và sau đó bắt nó và ném ngoại lệ được bọc của bạn.
MirroredFate

3
Đây không phải là câu trả lời được chấp nhận, bởi vì bạn không thể làm điều đó khi bạn sử dụng thư viện của bên thứ 3.
Denis V

@DenisV Xem bình luận của tôi ở trên của bạn. Nó được thực hiện tất cả các thời gian trong phần mềm doanh nghiệp. Đóng gói là tuyệt vời.
MirroredFate

229

Trong PHP> = 7.1, điều này là có thể. Xem câu trả lời dưới đây.


Nếu bạn có thể sửa đổi các ngoại lệ, sử dụng câu trả lời này .

Nếu bạn không thể, bạn có thể thử bắt tất cả Exceptionvà sau đó kiểm tra xem ngoại lệ nào được đưa ra instanceof.

try
{
    /* something */
}
catch( Exception $e )
{
    if ($e instanceof AError OR $e instanceof BError) {
       // It's either an A or B exception.
    } else {
        // Keep throwing it.
        throw $e;
    }
}

Nhưng có lẽ sẽ tốt hơn nếu sử dụng nhiều khối bắt như được mô tả trong câu trả lời đã nói ở trên .

try
{
    /* something */
}
catch( AError $e )
{
   handler1( $e );
}
catch ( BError $b )
{
   handler2( $e );
}

6
Đó là những gì tôi sợ. Bắt chúng lại với nhau và kiểm tra loại sẽ tốt nếu có nhiều loại lỗi cần xử lý cùng nhau, nhưng chỉ có 2, như trong trường hợp của tôi, bắt chúng riêng lẻ có lẽ sạch hơn. Cảm ơn!
Đaminh Gurto

3
@DominicGurto: Vâng, tôi cũng sẽ làm như vậy :) Tôi quan tâm nhiều hơn đến thái độ của PHP đối với một finallytuyên bố. ;)
alex

7
Nhưng đừng quên rằng điều này nắm bắt TẤT CẢ các ngoại lệ, vì vậy sẽ có một cái gì đó giống như ... } else { throw($e); }nếu nó không khớp với cả hai. Xin lỗi vì có thể sai cú pháp, đã không thấy php một thời gian.
Dalibor Filus

11
Nếu bạn đọc đoạn đầu tiên ở đây: php.net/manual/en/lingu.exceptions.php bạn sẽ thấy nhiều khối bắt có thể và giải pháp hoàn toàn hợp lệ. OP mặc dù đã đặt nhầm hai lớp ngoại lệ vào một câu lệnh bắt. Tôi nghĩ sẽ tốt hơn nếu cập nhật câu trả lời của bạn bằng một ví dụ khác với nhiều khối bắt.
Haralan Dobrev

4
Đề xuất một giải pháp ăn tất cả các Ngoại lệ khác của bạn, không nên chấp nhận tất cả ...
Stivni

88

Đến với PHP 7.1 là khả năng bắt được nhiều loại.

Vì vậy, điều này:

<?php
try {
    /* ... */
} catch (FirstException $ex) {
    $this->manageException($ex);
} catch (SecondException $ex) {
    $this->manageException($ex);
}
?>

<?php
try {

} catch (FirstException | SecondException $ex) {
    $this->manageException($ex);
}
?>

có chức năng tương đương.


45

Kể từ PHP 7.1,

catch( AError | BError $e )
{
    handler1( $e )
}

thú vị, bạn cũng có thể:

catch( AError | BError $e )
{
    handler1( $e )
} catch (CError $e){
    handler2($e);
} catch(Exception $e){
    handler3($e);
}

và trong các phiên bản trước của PHP:

catch(Exception $ex){
    if($ex instanceof AError){
        //handle a AError
    } elseif($ex instanceof BError){
        //handle a BError
    } else {
       throw $ex;//an unknown exception occured, throw it further
    }
}

25

Bài viết này bao gồm câu hỏi electrictoolbox.com/php-catch-multipl-exception-types . Nội dung của bài đăng được sao chép trực tiếp từ bài viết:

Ví dụ ngoại lệ

Dưới đây là một số ngoại lệ ví dụ đã được xác định cho mục đích của ví dụ này:

class FooException extends Exception 
{
  public function __construct($message = null, $code = 0) 
  {
    // do something
  }
}

class BarException extends Exception 
{
  public function __construct($message = null, $code = 0) 
  {
    // do something
  }
}

class BazException extends Exception 
{
  public function __construct($message = null, $code = 0) 
  {
    // do something
  }
}

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

Rất đơn giản - có thể có một khối bắt cho từng loại ngoại lệ có thể được ném:

try 
{
  // some code that might trigger a Foo/Bar/Baz/Exception
}

catch(FooException $e) 
{
  // we caught a foo exception
}

catch(BarException $e) 
{
  // we caught a bar exception
}

catch(BazException $e) 
{
  // we caught a baz exception
}

catch(Exception $e) 
{
  // we caught a normal exception
  // or an exception that wasn't handled by any of the above
}

Nếu một ngoại lệ được ném mà không được xử lý bởi bất kỳ câu lệnh bắt nào khác, nó sẽ được xử lý bởi khối bắt (Exception $ e). Nó không nhất thiết phải là người cuối cùng.


3
Vấn đề với phương pháp này xuất hiện khi bạn phải thực thi cùng một mã cho hai hoặc nhiều Ngoại lệ khác nhau.
Parziphal

Điều này đã được lấy từ Hộp công cụ điện . Chỉnh sửa bài để cung cấp tín dụng.
Kayla

Với PHP 7.x, bạn cần catch (Throwable $e)nắm bắt tất cả các ngoại lệ. Xem thêm: php.net/manual/en/class.throwable.php
Mikko Rantalainen

21

Là một phần mở rộng cho câu trả lời được chấp nhận, bạn có thể chuyển loại Loại ngoại lệ dẫn đến một mẫu giống như ví dụ ban đầu:

try {

    // Try something

} catch (Exception $e) {

    switch (get_class($e)) {

        case 'AError':
        case 'BError':
            // Handle A or B
            break;

        case 'CError':
            // Handle C
            break;

        case default:
            // Rethrow the Exception
            throw $e;

    }

}

6
sử dụng nhiều sản phẩm khai thác thay cho giải pháp này.
Alejandro Moreno

5

Đây là một lựa chọn hợp lý nếu bạn không có quyền kiểm soát việc xác định các ngoại lệ. Sử dụng tên của biến ngoại lệ để phân loại các ngoại lệ khi chúng bị bắt. Sau đó kiểm tra biến ngoại lệ sau khối thử / bắt.

$ABError = null;
try {
    // something
} catch (AError $ABError) {  // let the exception fall through
} catch (BError $ABError) {  // let the exception fall through
} catch (Exception $e) {
    handler2($e);
}
if ($ABError) {
    handler1($ABError);
}

Cách tiếp cận có vẻ hơi kỳ quặc này có lẽ chỉ có giá trị nếu có nhiều sự trùng lặp giữa các triển khai khối bắt.


3

Bên cạnh việc té ngã, bạn cũng có thể bước qua bằng cách sử dụng goto . Nó rất hữu ích nếu bạn muốn nhìn thế giới bùng cháy.

<?php

class A_Error extends Exception {}
class B_Error extends Exception {}
class C_Error extends Exception {}

try {
    throw new A_Error();
} 
catch (A_Error $e) { goto abc; }
catch (B_Error $e) { goto abc; }
catch (C_Error $e) {
abc:
    var_dump(get_class($e));
    echo "Gotta Catch 'Em All\n";
}

3v4l.org


1

Một cách tuyệt vời là sử dụng set_exception_handler.

Cảnh báo!!! với PHP 7, bạn có thể nhận được một màn hình trắng cho những lỗi nghiêm trọng. Ví dụ: nếu bạn gọi một phương thức trên một đối tượng không phải là đối tượng bạn thường nhận được Fatal error: Call to a member function your_method() on nullvà bạn sẽ thấy điều này nếu báo cáo lỗi được bật.

Các lỗi trên sẽ KHÔNG được bắt với catch(Exception $e). Lỗi trên sẽ KHÔNG kích hoạt bất kỳ trình xử lý lỗi tùy chỉnh nào được đặt bởi set_error_handler.

Bạn phải sử dụng catch(Error $e){ }để bắt lỗi trong PHP7. . Điều này có thể giúp:

class ErrorHandler{
    public static function excep_handler($e)
    {
        print_r($e);
    }
}
set_exception_handler(array('ErrorHandler','excep_handler'));

1
... Hoặc bạn có thể viết catch (Throwable $e) { ... }và hoàn thành nó. Xem thêm: php.net/manual/en/class.throwable.php
Mikko Rantalainen

0

Một tùy chọn khác không được liệt kê ở đây là sử dụng codethuộc tính của một ngoại lệ, vì vậy bạn có thể làm một cái gì đó như thế này:

try {

    if (1 === $foo) {

         throw new Exception(sprintf('Invalid foo: %s', serialize($foo)), 1);
    }

    if (2 === $bar) {
        throw new Exception(sprintf('Invalid bar: %s', serialize($foo)), 2);
    }
} catch (Exception $e) {

    switch ($e->getCode()) {

        case 1:
            // Special handling for case 1
            break;

        case 2:
            // Special handling for case 2
            break;

        default:

            // Special handling for all other cases
    }
}

Tôi đã không downvote, nhưng có lẽ những người theo chủ nghĩa thuần túy OOP tức giận rằng bạn đã không tạo các lớp ngoại lệ mới bằng cách sử dụng extends \Exception?
bàn

Hiểu rồi. Đó là toàn bộ giải pháp của tôi, bạn không cần tạo các lớp tùy ý chỉ để thiết lập một không gian tên để đưa ra một ngoại lệ cụ thể. Tôi chắc chắn đó là lý do tại sao họ thêm khả năng chỉ định mã.
Mike Purcell

Tôi cũng không downvote nhưng tôi đoán downvoters tin rằng điều này không trả lời câu hỏi. Tôi đề nghị bắt đầu câu trả lời bằng một cái gì đó cho người đọc thấy rõ rằng bạn đã hiểu câu hỏi và bạn vẫn muốn đề xuất một cách hoàn toàn khác cho dòng mã. Câu trả lời này thực sự không trả lời "cách bắt nhiều loại ngoại lệ " mà là "cách xử lý nhiều nguyên nhân khác nhau cho một ngoại lệ".
Mikko Rantalainen

0

Hmm, có nhiều giải pháp được viết cho phiên bản php thấp hơn 7.1.

Đây là một cách đơn giản khác cho những người không muốn bắt tất cả ngoại lệ và không thể tạo giao diện chung:

<?php
$ex = NULL
try {
    /* ... */
} catch (FirstException $ex) {
    // just do nothing here
} catch (SecondException $ex) {
    // just do nothing here
}
if ($ex !== NULL) {
    // handle those exceptions here!
}
?>
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.