Menangkap beberapa tipe pengecualian dalam satu blok tangkap

244

Saya ingin cara yang lebih bersih untuk mendapatkan fungsionalitas berikut, untuk menangkap AErrordan BErrordalam satu blok:

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

Apakah ada cara untuk melakukan ini? Atau apakah saya harus menangkapnya secara terpisah?

AErrordan Berrormemiliki kelas dasar bersama, tetapi mereka juga berbagi dengan tipe lain yang ingin saya ikuti handler2, jadi saya tidak bisa hanya menangkap kelas dasar.

Dominic Gurto
sumber
7
Hanya menambahkan ini sebagai catatan: RFC telah diajukan untuk menangkap beberapa pengecualian. Mari kita lihat apakah fitur ini masuk ke dalam bahasa PHP ... wiki.php.net/rfc/multiple-catch
SimonSimCity
10
^ Fitur ini telah diterapkan di PHP 7.1
Subin

Jawaban:

353

Memperbarui:

Pada PHP 7.1, ini tersedia.

Sintaksnya adalah:

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

Documents: https://www.php.net/manual/en/language.exceptions.php#example-287

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

Komit: https://github.com/php/php-src/commit/0aed2cc2a440e7be17552cc669d71fdd24d1204a


Untuk PHP sebelum 7.1:

Terlepas dari apa yang dikatakan jawaban-jawaban lain ini, Anda dapat menangkap AErrordan BErrordi blok yang sama (agak lebih mudah jika Anda yang menentukan pengecualian). Bahkan mengingat bahwa ada pengecualian yang ingin Anda "gagal", Anda masih harus dapat mendefinisikan hierarki yang sesuai dengan kebutuhan Anda.

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

Kemudian:

catch(LetterError $e){
    //voodoo
}

Seperti yang dapat Anda lihat di sini dan di sini , bahkan SPLpengecualian standar memiliki hierarki yang dapat Anda manfaatkan. Selain itu, sebagaimana dinyatakan dalam Manual PHP :

Ketika pengecualian dilemparkan, kode yang mengikuti pernyataan tidak akan dieksekusi, dan PHP akan berusaha untuk menemukan blok tangkap pencocokan pertama.

Ini berarti Anda juga bisa memilikinya

class CError extends LetterError {}

yang perlu Anda tangani secara berbeda dari AErroratau BError, sehingga pernyataan tangkapan Anda akan terlihat seperti ini:

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

Jika Anda memiliki kasus di mana ada dua puluh atau lebih pengecualian yang secara sah berada di bawah superclass yang sama, dan Anda perlu menangani lima (atau kelompok besar apa pun) dari mereka satu arah dan sisanya dengan cara lain, Anda MASIH dapat melakukan ini.

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

Lalu:

catch (Group1 $e) {}

Menggunakan OOP dalam hal pengecualian sangat kuat. Menggunakan hal-hal seperti get_classatau instanceofmerupakan peretasan, dan harus dihindari jika memungkinkan.

Solusi lain yang ingin saya tambahkan adalah menempatkan fungsi penanganan pengecualian dalam metodenya sendiri.

Kamu bisa saja

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

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

Dengan asumsi sekali tidak ada cara Anda dapat mengontrol hierarki kelas pengecualian atau interface (dan ada hampir selalu akan menjadi cara), Anda dapat melakukan hal berikut:

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);
}

Dengan cara ini, Anda masih memiliki satu-satunya lokasi kode yang harus Anda modifikasi jika mekanisme penanganan pengecualian Anda perlu diubah, dan Anda bekerja dalam konstruksi umum OOP.

Mirrored Fate
sumber
4
Berikut suara lain untuk ini sebagai jawaban yang benar. Sayangnya hal-hal seperti apa yang dikatakan dalam jawaban yang diterima dan fakta bahwa itu diterima sebagai jawaban yang benar, adalah apa yang membuat PHP menjadi gila.
borfast
Ini harus menjadi jawaban yang diterima. Meskipun, itu mengasumsikan bahwa Anda dapat memodifikasi file. AErrordapat diimplementasikan di perpustakaan / file yang diperbarui oleh pihak ketiga.
Kayla
@ WaffleStealer654 Anda masih dapat mensubklasifikasikan file dan membuat mereka mengimplementasikan grup Anda, bahkan jika Anda tidak dapat mengedit file secara langsung. Itu akan mengandaikan Anda bisa melempar pengecualian, tetapi Anda bisa saja membungkus mekanisme tingkat dasar yang paling di mana pengecualian akan dibuang dan kemudian menangkapnya dan membuang pengecualian Anda yang dibungkus.
MirroredFate
3
Ini bukan jawaban yang diterima, karena Anda tidak bisa melakukan itu ketika Anda menggunakan perpustakaan pihak ke-3.
Denis V
@DenisV Lihat komentar saya di atas milik Anda. Itu dilakukan setiap saat dalam perangkat lunak perusahaan. Enkapsulasi sangat bagus.
MirroredFate
229

Dalam PHP> = 7.1 ini dimungkinkan. Lihat jawabannya di bawah.


Jika Anda dapat memodifikasi pengecualian, gunakan jawaban ini .

Jika tidak bisa, Anda bisa mencoba menangkap semua dengan Exceptiondan kemudian memeriksa pengecualian yang dilemparkan dengan 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;
    }
}

Tetapi mungkin akan lebih baik untuk menggunakan beberapa blok penangkap seperti dijelaskan dalam jawaban yang disebutkan di atas .

try
{
    /* something */
}
catch( AError $e )
{
   handler1( $e );
}
catch ( BError $b )
{
   handler2( $e );
}
alex
sumber
6
Itu yang saya takutkan. Menangkap mereka bersama-sama dan menguji jenisnya akan baik jika ada banyak jenis kesalahan yang perlu ditangani bersama, tetapi untuk hanya 2, seperti dalam kasus saya, menangkap mereka secara terpisah mungkin lebih bersih. Terima kasih!
Dominic Gurto
3
@ DominicGurto: Ya, saya akan pergi dengan itu juga :) Saya akan lebih peduli dengan sikap PHP terhadap finallypernyataan. ;)
alex
7
Tapi jangan lupa bahwa ini menangkap SEMUA pengecualian, jadi harus ada sesuatu seperti ... } else { throw($e); }jika tidak cocok dengan keduanya. Maaf untuk sintaks yang mungkin salah, tidak melihat php sebentar.
Dalibor Filus
11
Jika Anda membaca paragraf pertama di sini: php.net/manual/en/language.exceptions.php Anda akan melihat beberapa blok tangkapan dimungkinkan dan merupakan solusi yang benar-benar valid. OP meskipun telah secara keliru menempatkan dua kelas pengecualian dalam satu pernyataan tangkap. Saya pikir akan lebih baik untuk memperbarui jawaban Anda dengan contoh lain dengan beberapa blok tangkapan.
Haralan Dobrev
4
Menyarankan solusi yang memakan semua Pengecualian Anda yang lain, seharusnya tidak diterima sama sekali ...
Stivni
88

Datang dalam PHP 7.1 adalah kemampuan untuk menangkap beberapa tipe.

Jadi ini:

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

dan

<?php
try {

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

secara fungsional setara.

Joe Watkins
sumber
45

Pada PHP 7.1,

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

Menariknya, Anda juga dapat:

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

dan dalam versi PHP sebelumnya:

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
    }
}
hanshenrik
sumber
25

Artikel ini membahas pertanyaan electrictoolbox.com/php-catch-multiple-exception-types . Konten tulisan disalin langsung dari artikel:

Pengecualian contoh

Berikut beberapa contoh pengecualian yang telah ditentukan untuk keperluan contoh ini:

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
  }
}

Menangani banyak pengecualian

Ini sangat sederhana - bisa ada catch block untuk setiap jenis pengecualian yang dapat dilemparkan:

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
}

Jika pengecualian dilemparkan yang tidak ditangani oleh salah satu pernyataan tangkapan lainnya, maka akan ditangani oleh blok tangkapan (Pengecualian $ e). Itu tidak harus menjadi yang terakhir.

pengguna1983902
sumber
3
Masalah dengan metode ini muncul ketika Anda harus menjalankan kode yang sama untuk dua atau lebih Pengecualian yang berbeda.
Parziphal
Ini diambil dari Kotak Alat Listrik . Mengedit pos untuk memberikan kredit.
Kayla
Dengan PHP 7.x, Anda harus catch (Throwable $e)menangkap semua pengecualian. Lihat juga: php.net/manual/en/class.throwable.php
Mikko Rantalainen
21

Sebagai ekstensi untuk jawaban yang diterima, Anda bisa mengganti tipe Pengecualian yang menghasilkan pola yang agak seperti contoh aslinya:

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;

    }

}
smix96
sumber
6
gunakan beberapa tangkapan bukan solusi ini.
Alejandro Moreno
5

Berikut adalah alternatif yang masuk akal jika Anda tidak memiliki kendali untuk mendefinisikan pengecualian. Gunakan nama variabel pengecualian untuk mengkategorikan pengecualian ketika mereka ditangkap. Kemudian periksa variabel pengecualian setelah blok coba / tangkap.

$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);
}

Pendekatan yang tampak aneh ini mungkin hanya layak jika ada banyak duplikasi antara implementasi catch block.

mantra
sumber
3

Selain gagal, mungkin juga untuk melangkah dengan menggunakan goto . Ini sangat berguna jika Anda ingin melihat dunia terbakar.

<?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

ml_
sumber
1

Cara terbaik adalah menggunakan set_exception_handler.

Peringatan!!! dengan PHP 7, Anda mungkin mendapatkan layar putih kematian karena kesalahan fatal. Misalnya, jika Anda memanggil metode pada non-objek yang biasanya Anda dapatkanFatal error: Call to a member function your_method() on null dan Anda akan melihat ini jika pelaporan kesalahan aktif.

Kesalahan di atas TIDAK akan tertangkap catch(Exception $e). Kesalahan di atas TIDAK akan memicu penangan kesalahan kustom yang ditetapkan oleh set_error_handler.

Anda harus menggunakan catch(Error $e){ }untuk menangkap kesalahan dalam PHP7. . Ini bisa membantu:

class ErrorHandler{
    public static function excep_handler($e)
    {
        print_r($e);
    }
}
set_exception_handler(array('ErrorHandler','excep_handler'));
Frank Forte
sumber
1
... atau Anda bisa menulis catch (Throwable $e) { ... }dan selesai dengan itu. Lihat juga: php.net/manual/en/class.throwable.php
Mikko Rantalainen
0

Opsi lain yang tidak tercantum di sini adalah menggunakan codeatribut pengecualian, sehingga Anda dapat melakukan sesuatu seperti ini:

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
    }
}
Mike Purcell
sumber
Saya tidak mengundurkan diri, tetapi mungkin para puritan OOP marah karena Anda tidak membuat kelas pengecualian baru menggunakan extends \Exception?
keyboardSmasher
Mengerti. Itulah inti dari solusi saya, Anda tidak perlu membuat kelas sewenang-wenang hanya untuk membangun namespace untuk melempar pengecualian tertentu. Saya yakin itu sebabnya mereka menambahkan kemampuan untuk menentukan kode.
Mike Purcell
Saya tidak downvote baik tetapi saya kira downvoters percaya bahwa ini tidak menjawab pertanyaan. Saya menyarankan memulai jawaban dengan sesuatu yang membuatnya jelas bagi pembaca bahwa Anda telah memahami pertanyaan dan Anda masih ingin menyarankan cara yang sama sekali berbeda untuk aliran kode. Jawaban ini benar-benar tidak menjawab "bagaimana menangkap beberapa jenis pengecualian " melainkan "bagaimana menangani berbagai penyebab pengecualian".
Mikko Rantalainen
0

Hmm, ada banyak solusi yang ditulis untuk versi php lebih rendah dari 7.1.

Ini adalah salah satu yang sederhana bagi mereka yang tidak ingin menangkap semua pengecualian dan tidak dapat membuat antarmuka umum:

<?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!
}
?>
GT
sumber