การตรวจจับข้อยกเว้นหลายประเภทในหนึ่ง catch catch บล็อก


244

ฉันต้องการวิธีที่สะอาดกว่าในการรับฟังก์ชั่นต่อไปนี้เพื่อจับAErrorและBErrorในหนึ่งช่วงตึก:

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

มีวิธีการทำเช่นนี้? หรือฉันต้องจับแยกต่างหาก

AErrorและBerrorมีคลาสฐานที่แชร์ แต่พวกเขายังแชร์กับประเภทอื่น ๆ ที่ฉันอยากผ่านhandler2ดังนั้นฉันจึงไม่สามารถเรียนคลาสฐานได้


7
เพียงเพิ่มนี้เป็นบันทึกย่อด้านข้าง: RFC ได้รับการยื่นสำหรับการจับหลายข้อยกเว้น เรามาดูกันว่าฟีเจอร์นี้เป็นไปได้ไหมในภาษา PHP ... wiki.php.net/rfc/multiple-catch
SimonSimCity

10
^ คุณลักษณะนี้ได้รับการใช้งานใน PHP 7.1
Subin

คำตอบ:


353

ปรับปรุง:

ในฐานะของ PHP 7.1 นี้สามารถใช้ได้

ไวยากรณ์คือ:

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

เอกสาร: https://www.php.net/manual/th/language.exceptions.php#example-287

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

กระทำ: https://github.com/php/php-src/commit/0aed2cc2a440e7be17552cc669d71fdd24d1204a


สำหรับ PHP ก่อน 7.1:

แม้ว่าคำตอบอื่น ๆ เหล่านี้จะพูดคุณสามารถจับAErrorและBErrorอยู่ในบล็อกเดียวกัน (มันค่อนข้างง่ายกว่าถ้าคุณเป็นคนที่กำหนดข้อยกเว้น) แม้จะมีข้อยกเว้นที่คุณต้องการ "ล้มเหลว" คุณควรจะสามารถกำหนดลำดับชั้นเพื่อให้ตรงกับความต้องการของคุณ

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

แล้ว:

catch(LetterError $e){
    //voodoo
}

อย่างที่คุณเห็นที่นี่และที่นี่แม้แต่SPLข้อยกเว้นเริ่มต้นก็มีลำดับชั้นที่คุณสามารถใช้ประโยชน์ได้ นอกจากนี้ตามที่ระบุไว้ในคู่มือ PHP :

เมื่อมีข้อผิดพลาดถูกโยนรหัสต่อไปนี้คำสั่งจะไม่ถูกดำเนินการและPHP จะพยายามค้นหา catch catch ที่ตรงกันแรก

ซึ่งหมายความว่าคุณสามารถมี

class CError extends LetterError {}

ซึ่งคุณต้องจัดการแตกต่างกว่าAErrorหรือBErrorดังนั้นคำสั่ง catch ของคุณจะเป็นดังนี้:

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

หากคุณมีกรณีที่มีข้อยกเว้นยี่สิบข้อขึ้นไปที่อยู่ภายใต้ซูเปอร์คลาสเดียวกันอย่างถูกกฎหมายและคุณจำเป็นต้องจัดการห้าข้อ (หรือกลุ่มใด ๆ ที่มีขนาดใหญ่) ในทางเดียวและที่เหลือคุณสามารถทำได้

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

แล้ว:

catch (Group1 $e) {}

การใช้ OOP เมื่อกล่าวถึงข้อยกเว้นมีประสิทธิภาพมาก ใช้สิ่งที่ชอบget_classหรือinstanceofแฮ็กและควรหลีกเลี่ยงถ้าเป็นไปได้

โซลูชันอื่นที่ฉันต้องการเพิ่มคือการวางฟังก์ชันการจัดการข้อยกเว้นในวิธีการของตนเอง

คุณสามารถมี

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

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

สมมติว่ามีอย่างวิธีที่คุณสามารถควบคุมชั้นวรรณะยกเว้นหรืออินเตอร์เฟซ (และมีเกือบตลอดเวลาไม่มีจะเป็นวิธีการ) คุณสามารถทำต่อไปนี้:

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

ด้วยวิธีนี้คุณยังคงมีรหัสตำแหน่งเดียวที่คุณต้องแก้ไขหากกลไกการจัดการข้อยกเว้นของคุณจำเป็นต้องเปลี่ยนและคุณกำลังทำงานภายในโครงสร้างทั่วไปของ OOP


4
นี่คืออีกคะแนนเสียงสำหรับคำตอบที่ถูกต้อง น่าเสียดายที่สิ่งต่าง ๆ เช่นที่กล่าวไว้ในคำตอบที่ยอมรับและความจริงที่ว่ามันได้รับการยอมรับว่าเป็นคำตอบที่ถูกต้องก็คือสิ่งที่ทำให้ PHP เป็นบ้า
borfast

นี่ควรเป็นคำตอบที่ยอมรับได้ แม้ว่าจะถือว่าคุณสามารถแก้ไขไฟล์ได้ AErrorสามารถนำไปใช้ในไลบรารี่ / ไฟล์ซึ่งมีการปรับปรุงโดยบุคคลที่สาม
Kayla

@ WaffleStealer654 คุณยังคงสามารถคลาสย่อยไฟล์และทำให้มันเป็นกลุ่มของคุณแม้ว่าคุณจะไม่สามารถแก้ไขไฟล์ได้โดยตรง นั่นจะทึกทักเอาเองว่าคุณสามารถโยนข้อยกเว้นได้ แต่คุณสามารถห่อกลไกระดับพื้นฐานได้โดยที่ข้อยกเว้นนั้นจะถูกโยนทิ้งแล้วจับมันแล้วโยนข้อยกเว้นที่ห่อไว้
MirroredFate

3
นี่ไม่ใช่คำตอบที่ยอมรับได้เพราะคุณไม่สามารถทำได้เมื่อคุณใช้ห้องสมุดบุคคลที่สาม
เดนิส V

@DenisV ดูความคิดเห็นของฉันด้านบนของคุณ จะทำตลอดเวลาในซอฟต์แวร์องค์กร การห่อหุ้มนั้นยอดเยี่ยม
MirroredFate

229

ใน PHP> = 7.1 นี่เป็นไปได้ ดูคำตอบด้านล่าง


หากคุณสามารถแก้ไขข้อยกเว้นได้ให้ใช้คำตอบนี้

หากคุณทำไม่ได้คุณสามารถลองทั้งหมดด้วยExceptionแล้วตรวจสอบว่ามีข้อยกเว้นใดถูกโยนทิ้ง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;
    }
}

แต่ก็อาจจะดีกว่าที่จะใช้การจับหลายบล็อกที่อธิบายไว้ในคำตอบดังกล่าวข้างต้น

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

6
นั่นคือสิ่งที่ฉันกลัว การจับพวกมันเข้าด้วยกันและทดสอบประเภทนั้นจะดีถ้ามีข้อผิดพลาดหลายอย่างที่ต้องจัดการด้วยกัน แต่สำหรับเพียง 2 อย่างเช่นในกรณีของฉันการจับพวกมันแยกต่างหากน่าจะสะอาดกว่า ขอบคุณ!
Dominic Gurto

3
@DominicGurto: ใช่ฉันจะไปด้วยเหมือนกัน :) ฉันจะกังวลกับทัศนคติของ PHP ต่อfinallyคำสั่งมากขึ้น ;)
alex

7
แต่อย่าลืมว่าสิ่งนี้จะจับข้อยกเว้นทั้งหมดดังนั้นควรมีบางอย่างเช่น... } else { throw($e); }ถ้ามันไม่ตรงกับทั้งสอง ขออภัยสำหรับไวยากรณ์ที่ผิดอาจไม่เห็น php สักครู่
Dalibor Filus

11
หากคุณอ่านย่อหน้าแรกที่นี่: php.net/manual/en/language.exceptions.phpคุณจะเห็นบล็อก catch หลาย ๆ ตัวเป็นไปได้และเป็นทางออกที่ถูกต้องสมบูรณ์ OP แม้ว่ามีการวางสองคลาสยกเว้นโดยไม่ตั้งใจในคำสั่ง catch หนึ่งคำสั่ง ฉันคิดว่ามันจะเป็นการดีกว่าที่จะอัปเดตคำตอบของคุณด้วยอีกตัวอย่างหนึ่งที่มี catch catch หลายบล็อก
Haralan Dobrev

4
แนะนำวิธีแก้ปัญหาที่กินข้อยกเว้นอื่น ๆ ทั้งหมดของคุณไม่ควรได้รับการยอมรับเลย ...
Stivni

88

การเข้ามาในPHP 7.1คือความสามารถในการตรวจจับได้หลายประเภท

ดังนั้นสิ่งนี้:

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

และ

<?php
try {

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

มีหน้าที่เทียบเท่า


45

ตั้งแต่ PHP 7.1,

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

ที่น่าสนใจคุณยังสามารถ:

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

และใน 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

บทความนี้ครอบคลุมคำถามelectrictoolbox.com/php-catch-multiple-exception-types เนื้อหาของโพสต์คัดลอกโดยตรงจากบทความ:

ตัวอย่างข้อยกเว้น

นี่คือตัวอย่างข้อยกเว้นบางประการที่กำหนดไว้สำหรับจุดประสงค์ของตัวอย่างนี้:

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

จัดการข้อยกเว้นหลายประการ

มันง่ายมาก - สามารถมี catch catch สำหรับแต่ละประเภทยกเว้นที่สามารถโยนได้:

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
}

หากมีการโยนข้อยกเว้นที่ไม่ได้รับการจัดการโดยคำสั่ง catch อื่น ๆ จะถูกจัดการโดยบล็อก catch (Exception $ e) ไม่จำเป็นต้องเป็นอันสุดท้าย


3
ปัญหาของวิธีนี้เกิดขึ้นเมื่อคุณต้องเรียกใช้รหัสเดียวกันสำหรับข้อยกเว้นต่าง ๆ สองข้อขึ้นไป
Parziphal

นี้ถูกดึงมาจากไฟฟ้ากล่องเครื่องมือ กำลังแก้ไขโพสต์เพื่อให้เครดิต
Kayla

ด้วย PHP 7.x คุณจะต้องตรวจcatch (Throwable $e)จับข้อยกเว้นทั้งหมด ดูเพิ่มเติมที่: php.net/manual/en/class.throwable.php
Mikko Rantalainen

21

ในฐานะที่เป็นส่วนขยายของคำตอบที่ยอมรับคุณสามารถเปลี่ยนประเภทของข้อยกเว้นทำให้เกิดรูปแบบที่ค่อนข้างเหมือนกับตัวอย่างดั้งเดิม:

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
ใช้หลายจับแทนวิธีนี้
Alejandro Moreno

5

นี่คือทางเลือกที่สมเหตุสมผลหากคุณไม่สามารถควบคุมการกำหนดข้อยกเว้นได้ ใช้ชื่อของตัวแปร exception เพื่อจัดหมวดหมู่ข้อยกเว้นเมื่อถูกจับ จากนั้นตรวจสอบตัวแปรข้อยกเว้นหลังจากลอง / จับบล็อก

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

วิธีการค้นหาที่ค่อนข้างแปลกนี้น่าจะคุ้มค่าก็ต่อเมื่อมีการทำซ้ำจำนวนมากระหว่างการนำไปใช้งาน catch block


3

นอกจากนี้ฤดูใบไม้ร่วงผ่านก็ยังเป็นไปได้ที่จะก้าวข้ามโดยใช้ข้ามไป มันมีประโยชน์มากถ้าคุณต้องการเห็นโลกที่ถูกเผาไหม้

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

set_exception_handlerเป็นวิธีที่ดีคือการใช้งาน

คำเตือน!!! ด้วย PHP 7 คุณอาจได้รับหน้าจอสีขาวแห่งความตายสำหรับข้อผิดพลาดร้ายแรง ตัวอย่างเช่นถ้าคุณเรียกใช้วิธีการที่ไม่ใช่วัตถุคุณจะได้รับตามปกติFatal error: Call to a member function your_method() on nullและคุณคาดว่าจะเห็นสิ่งนี้หากการรายงานข้อผิดพลาดเปิดอยู่

catch(Exception $e)ข้อผิดพลาดดังกล่าวจะไม่ถูกจับด้วย ข้อผิดพลาดดังกล่าวข้างต้นจะไม่เรียกใด ๆ set_error_handlerชุดข้อผิดพลาดที่กำหนดเองโดยการจัดการ

คุณต้องใช้catch(Error $e){ }เพื่อตรวจจับข้อผิดพลาดใน PHP7 . สิ่งนี้สามารถช่วย:

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

1
... หรือคุณสามารถเขียนcatch (Throwable $e) { ... }และทำมันได้ ดูเพิ่มเติมที่: php.net/manual/en/class.throwable.php
Mikko Rantalainen

0

ตัวเลือกอื่นที่ไม่อยู่ในรายการนี้คือการใช้codeคุณสมบัติข้อยกเว้นเพื่อให้คุณสามารถทำสิ่งนี้:

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

ฉันไม่ได้ลงคะแนน แต่อาจจะเป็นคนพิถีพิถันของ OOP ที่โกรธที่คุณไม่ได้สร้างคลาสยกเว้นใหม่extends \Exceptionใช่ไหม
keyboardSmasher

เข้าใจแล้ว นั่นคือประเด็นทั้งหมดของโซลูชันของฉันคุณไม่จำเป็นต้องสร้างคลาสโดยพลการเพียงเพื่อสร้างเนมสเปซเพื่อแสดงข้อยกเว้นเฉพาะ ฉันแน่ใจว่าเป็นเพราะพวกเขาเพิ่มความสามารถในการระบุรหัส
Mike Purcell

ฉันไม่ได้ลงคะแนนอย่างใดอย่างหนึ่ง แต่ฉันเดาผู้ลงคะแนนเชื่อว่าสิ่งนี้ไม่ตอบคำถาม ฉันขอแนะนำให้เริ่มคำตอบด้วยบางสิ่งที่ทำให้ผู้อ่านเข้าใจได้ชัดเจนว่าคุณเข้าใจคำถามนั้นแล้วและคุณยังต้องการแนะนำวิธีที่แตกต่างกันโดยสิ้นเชิงสำหรับโฟลว์โค้ด คำตอบนี้ไม่ตอบ "วิธีจับข้อยกเว้นหลายประเภท " แต่ "วิธีจัดการกับสาเหตุที่แตกต่างกันหลายประการสำหรับข้อยกเว้น"
Mikko Rantalainen

0

อืมมีวิธีแก้ปัญหามากมายที่เขียนขึ้นสำหรับรุ่น php ที่ต่ำกว่า 7.1

นี่เป็นอีกวิธีง่าย ๆ สำหรับผู้ที่ไม่ต้องการรับการยกเว้นทั้งหมดและไม่สามารถสร้างอินเทอร์เฟซทั่วไปได้:

<?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!
}
?>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.