Я хотел бы получить более чистый способ получить следующую функциональность, чтобы поймать AError
и BError
в одном блоке:
try { /* something */ } catch( AError, BError $e ) { handler1( $e ) } catch( Exception $e ) { handler2( $e ) }
Есть какой-либо способ сделать это? Или я должен поймать их отдельно?
AError
и Berror
есть общий базовый класс, но они также Berror
его другим типам, которые я хотел бы handler2
, поэтому я не могу просто поймать базовый класс.
Если вы можете изменить исключения, используйте этот ответ .
Если вы не можете, вы можете попробовать поймать все с помощью 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; } }
Но , вероятно, было бы лучше использовать несколько блоков catch, как описано в вышеупомянутом ответе .
try { /* something */ } catch( AError $e ) { handler1( $e ); } catch ( BError $b ) { handler2( $e ); }
Несмотря на то, что говорят эти другие ответы, вы можете поймать 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.
Это означает, что вы также можете
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); }
Таким образом, у вас все еще есть только одно местоположение кода, которое вы должны изменить, если ваш механизм обработки исключений должен измениться, и вы работаете в общих конструкциях ООП.
Приход в 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); } ?>
функционально эквивалентны.
Начиная с 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 } }
В этой статье рассматривается вопрос 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 } }
Обработка нескольких исключений
Это очень просто – может быть блокировка для каждого типа исключений, которое может быть выбрано:
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). Это необязательно должно быть последним.
В качестве дополнения к принятому ответу вы можете переключить тип исключения, в результате получившийся образец, который похож на исходный пример:
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; } }
Вот разумная альтернатива, если у вас нет контроля над определением исключений. Используйте имя переменной исключения для категоризации исключений, когда они пойманы. Затем проверьте переменную исключения после блока try / catch.
$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.
Помимо провала, можно также перешагнуть с помощью goto . Это очень полезно, если вы хотите, чтобы мир горел.
<?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
Отличный способ – использовать 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'));
Другим вариантом, не указанным здесь, является использование атрибута 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 } }