Php исключения в конструкторе

Обновлено: 23.04.2024

Задача публикации: доступно изложить способ организации иерархии исключений и их обработки в приложении. Без привязки к фреймворкам и конкретной архитектуре. Описываемый способ является де-факто стандартом в сообществе: он используется во многих серьёзных библиотеках и фреймворках. В том числе Zend, Symfony. Не смотря на его логичность и универсальность, формального описания предлагаемого подхода на русском языке я не нашёл. После неоднократного устного изложения концепции коллегам, родилась мысль оформить её в виде публикации на Хабрахабр.

В языке PHP, начиная с 5-ой версии, доступен механизм исключений. В актуальной, 7-ой, версии этот механизм был улучшен и переработан с целью единнобразной обработки разных ошибок при помощи конструкции try<> catch.

В стандартной библиотеке (SPL) PHP предоставляет готовый набор базовых классов и интерфейсов для исключений. В 7-ой версии этот набор был расширен интерфейсом Throwable . Вот диаграмма всех имеющихся в версии 7 типов (изображение — ссылка):

Диаграмма типов исключения в PHP7

Для junior-разработчиков, может быть полезным предварительно уяснить все тонкости синтаксиса, логики работы исключений и обработки ошибок в целом. Могу порекоммендовать следующие статьи на русском языке:

  • Евгений Пястолов: Стандартные исключения в PHP. Когда какое применить.
  • Антон Шевчук (старожил Хабра a.k.a. AntonShevchuk) (перевод Exceptional Code – PART 1): “Исключительный” код – Часть 1
  • @kotiara:

Основные аспекты иерархии исключений вашего приложения.

Общий интерфейс

Используйте общий интерфейс (маркер) для всех исключений определяемых в вашем приложении. Это же правило применимо и к отдельным компонентам, модулям, пакетам, т.е. подпространствам имён вашего кода. Например \YourVendor\YourApp\Exception\ExceptionInterface , либо \YourVendor\YourApp\SomeComponent\Exception\ExceptionInterface , в соответствии с PSR-4.

Пример можно посмотреть в любом компоненте Symfony, Zend, etc.

Для каждой ситуации — свой тип

Каждая новая исключительная ситуация должна приводить к созданию нового типа (класса) исключения. Имя класса должно семантично описывать эту ситуацию. Таким образом код с вызовом конструктора исключения, и его броском, будет: читаем, самодокументирован, прозрачен.

Расширяйте базовые типы

Базовые типы служат для наследования от них собственных типов исключений вашего приложения. Об этом прямо сказано в документации.

Пример можно посмотреть в любом компоненте Symfony, либо Zend.

Проброс и преобразование в соответствии с уровнем абстракции

Таким образом, контроллер не знает о существовании PDO — он работает с абстрактным хранилищем.

Решение, что должно быть проброшено, что преобразовано, а что обработано, в каждом случае лежит на программисте.

Используете возможности стандартного конструктора

Не стоит переопределять стандартный конструктор Exception — он идеально спроектирован для своих задач. Просто не забывайте его использовать по назначению и вызывать родительский со всеми аргументами, если всё же потребовалась перегрузка. Суммируя этот пункт с предыдущим, код может выглядеть примерно так:

Резюме

Зачем построение целой иерархии, с участием интерфейсов, типов, подтипов и весь этот полиморфизм ради обработки ошибок? Каков смысл этой абстракции и чем оправдана её цена?

При проектировании приложения, абстракция — это то, что добавляет гибкости, и позволяет откладывать конкретные решения и детальную реализацию, пока не известны все нюансы, а требования к коду ещё не известны, либо могут поменяться. Это инвестиция, которая окупается со временем.

Когда ваши исключения обладают одновременно интерфейсом-маркером, супертипом SPL (как RuntimeException), конкретным типом соответствующим ситуации, вы можете полностью контролировать их обработку и гибко менять её стратегию в будущем. Закладывая на раннем этапе разработки эти абстракции, в будущем, по мере появления и ужесточения требований к обработке ошибок, вы будете иметь в распоряжении инструмент, который поможет эти требования реализовать.

На этапе прототипа достаточно показать надпись "опаньки", для этого достаточно поймать любой Throwable в index.php.

В альфа версии будет не лишним отличать ситуации 401, 404 и 500.

В бета-тестировании, вы вероятно захотите выводить трейсы всех предыдущих исключений для формирования баг-репортов.

К началу экплуатации вам понадобится единая точка для логирования исключительных ситуаций.

И всё время развития приложения вы будете всего лишь, по мере надобности, добавлять код с логикой обработки, без необходимости внесения изменений в основной код, где исключения генерируются.

Модель исключений (exceptions) в PHP похожа с используемыми в других языках программирования. Исключение можно сгенерировать (выбросить) при помощи оператора throw , и можно перехватить (поймать) оператором catch . Код генерирующий исключение, должен быть окружён блоком try , для того, чтобы можно было перехватить исключение. Каждый блок try должен иметь как минимум один соответствующий ему блок catch или finally .

В случае, если выброшено исключение, для которого нет блока catch в текущей функции, это исключение будет "всплывать" по стеку вызова, пока не будет найден подходящий блок catch . При этом, все встреченные блоки finally будут исполнены. Если стек вызовов раскрутится до глобальной области видимости, не встретив подходящего блока catch , программа завершит работу с фатальной ошибкой, если только у вас не настроен глобальный обработчик исключений.

Генерируемый объект должен принадлежать классу Exception или наследоваться от Exception . Попытка сгенерировать исключение другого класса приведёт к фатальной ошибке PHP.

Начиная с PHP 8.0.0, ключевое слово throw является выражением и может использоваться в контексте других выражений. В более ранних версиях оно являлось оператором и требовало размещения в отдельной строке.

catch

Блок catch определяет то, как следует реагировать на выброшенное исключение. В блоке catch указывается один или более типов исключений или ошибок(Error), которые он будет обрабатывать. Также указывается и переменная, которой будет присвоено пойманное исключение (начиная с PHP 8.0.0 задавать эту переменную не обязательно). Выброшенное исключение или ошибка будут обработаны первым подходящим блоком catch .

Можно использовать несколько блоков catch , перехватывающих различные классы исключений. Нормальное выполнение (когда не генерируются исключения в блоках try ) будет продолжено за последним блоком catch . Исключения могут быть сгенерированы (или вызваны ещё раз) оператором throw внутри блока catch . Если нет, то исполнение будет продолжено после отработки блока catch .

При генерации исключения код, следующий после описываемого выражения, не будет выполнен, а PHP попытается найти первый блок catch , перехватывающий исключение данного класса. Если исключение не будет перехвачено, PHP выдаст фатальную ошибку: " Uncaught Exception . " (Неперехваченное исключение), если не был определён обработчик ошибок при помощи функции set_exception_handler() .

Начиная с PHP 7.1.0, блок catch может принимать несколько типов исключений с помощью символа ( | ). Это полезно, когда разные исключения из разных иерархий классов обрабатываются одинаково.

Начиная с PHP 8.0.0, задание переменной для пойманного исключения опционально. Если она не задана, блок catch будет исполняться, но не будет иметь доступа к объекту исключения.

finally

Блок finally также можно использовать после или вместо блока catch . Код в блоке finally всегда будет выполняться после кода в блоках try и catch , независимо от того, было ли выброшено исключение, перед тем как продолжится нормальное выполнение кода.

Одно важное взаимодействие происходит между блоком finally и оператором return . Если оператор return встречается внутри блоков try или catch , блок finally всё равно будет выполнен. Кроме того, оператор return выполняется, когда встречается, но результат будет возвращён после выполнения блока finally . Если блок finally также содержит оператор return , возвращается значение, указанное в блоке finally .

Глобальный обработчик исключений

Если исключение дошло по стеку вызовов до глобальной области видимости, оно может быть обработано глобальным обработчиком исключений, если он задан. С помощью функции set_exception_handler() можно задать функцию, которая будет выполнена вместо блока catch , если не нашлось подходящего. Эффект аналогичен тому, как будто мы всю нашу программу обернули в блок try - catch , где за реализацию блока catch отвечает установленная функция.

Примечания

function exceptions_error_handler ( $severity , $message , $filename , $lineno ) throw new ErrorException ( $message , 0 , $severity , $filename , $lineno );
>

Примеры

function inverse ( $x ) if (! $x ) throw new Exception ( 'Деление на ноль.' );
>
return 1 / $x ;
>

try echo inverse ( 5 ) . "\n" ;
echo inverse ( 0 ) . "\n" ;
> catch ( Exception $e ) echo 'Выброшено исключение: ' , $e -> getMessage (), "\n" ;
>

// Продолжение выполнения
echo "Привет, мир\n" ;
?>

Результат выполнения данного примера:

function inverse ( $x ) if (! $x ) throw new Exception ( 'Деление на ноль.' );
>
return 1 / $x ;
>

try echo inverse ( 5 ) . "\n" ;
> catch ( Exception $e ) echo 'Поймано исключение: ' , $e -> getMessage (), "\n" ;
> finally echo "Первый блок finally.\n" ;
>

try echo inverse ( 0 ) . "\n" ;
> catch ( Exception $e ) echo 'Поймано исключение: ' , $e -> getMessage (), "\n" ;
> finally echo "Второй блок finally.\n" ;
>

// Продолжение нормального выполнения
echo "Привет, мир\n" ;
?>

Результат выполнения данного примера:

function test () try throw new Exception ( 'foo' );
> catch ( Exception $e ) return 'catch' ;
> finally return 'finally' ;
>
>

Результат выполнения данного примера:

class MyException extends Exception

class Test public function testing () try try throw new MyException ( 'foo!' );
> catch ( MyException $e ) // повторный выброс исключения
throw $e ;
>
> catch ( Exception $e ) var_dump ( $e -> getMessage ());
>
>
>

$foo = new Test ;
$foo -> testing ();

Результат выполнения данного примера:

class MyException extends Exception

class MyOtherException extends Exception

class Test public function testing () try throw new MyException ();
> catch ( MyException | MyOtherException $e ) var_dump ( get_class ( $e ));
>
>
>

$foo = new Test ;
$foo -> testing ();

Результат выполнения данного примера:

Допустимо начиная с PHP 8.0.0

class SpecificException extends Exception <>

function test () throw new SpecificException ( 'Ой!' );
>

try test ();
> catch ( SpecificException ) print "Было поймано исключение SpecificException, но нам безразлично, что у него внутри." ;
>
?>

Допустимо начиная с PHP 8.0.0

class SpecificException extends Exception <>

function test () do_something_risky () or throw new Exception ( 'Всё сломалось' );
>

try test ();
> catch ( Exception $e ) print $e -> getMessage ();
>
?>

User Contributed Notes 12 notes

If you intend on creating a lot of custom exceptions, you may find this code useful. I've created an interface and an abstract exception class that ensures that all parts of the built-in Exception class are preserved in child classes. It also properly pushes all information back to the parent constructor ensuring that nothing is lost. This allows you to quickly create new exceptions on the fly. It also overrides the default __toString method with a more thorough one.

interface IException
/* Protected methods inherited from Exception class */
public function getMessage (); // Exception message
public function getCode (); // User-defined Exception code
public function getFile (); // Source filename
public function getLine (); // Source line
public function getTrace (); // An array of the backtrace()
public function getTraceAsString (); // Formated string of trace

/* Overrideable methods inherited from Exception class */
public function __toString (); // formated string for display
public function __construct ( $message = null , $code = 0 );
>

abstract class CustomException extends Exception implements IException
protected $message = 'Unknown exception' ; // Exception message
private $string ; // Unknown
protected $code = 0 ; // User-defined exception code
protected $file ; // Source filename of exception
protected $line ; // Source line of exception
private $trace ; // Unknown

public function __construct ( $message = null , $code = 0 )
if (! $message ) throw new $this ( 'Unknown ' . get_class ( $this ));
>
parent :: __construct ( $message , $code );
>

public function __toString ()
return get_class ( $this ) . " ' < $this ->message > ' in < $this ->file > ( < $this ->line > )\n"
. " < $this ->getTraceAsString ()> " ;
>
>
?>

Now you can create new exceptions in one line:

class TestException extends CustomException <>
?>

Here's a test that shows that all information is properly preserved throughout the backtrace.

function exceptionTest ()
try throw new TestException ();
>
catch ( TestException $e ) echo "Caught TestException (' < $e ->getMessage ()> ')\n < $e >\n" ;
>
catch ( Exception $e ) echo "Caught Exception (' < $e ->getMessage ()> ')\n < $e >\n" ;
>
>

echo '

' . exceptionTest () . '
' ;
?>

Here's a sample output:

Определённый пользователем класс исключения должен быть определён, как класс расширяющий (наследующий) встроенный класс Exception. Ниже приведены методы и свойства класса Exception, доступные дочерним классам.

public function __construct ( $message = '' , $code = 0 , Throwable $previous = null );

final private function __clone (); // запрещает клонирования исключения

// Переопределяемый
public function __toString (); // отформатированная строка для отображения
>
?>

Если класс, наследуемый от Exception переопределяет конструктор, необходимо вызвать в конструкторе parent::__construct(), чтобы быть уверенным, что все доступные данные были правильно присвоены. Метод __toString() может быть переопределён, чтобы обеспечить нужный вывод, когда объект преобразуется в строку.

Замечание:

Исключения нельзя клонировать. Попытка клонировать исключение приведёт к неисправимой ошибке E_ERROR .

/**
* Определим свой класс исключения
*/
class MyException extends Exception
// Переопределим исключение так, что параметр message станет обязательным
public function __construct ( $message , $code = 0 , Throwable $previous = null ) // некоторый код

// убедитесь, что все передаваемые параметры верны
parent :: __construct ( $message , $code , $previous );
>

// Переопределим строковое представление объекта.
public function __toString () return __CLASS__ . ": [ < $this ->code > ]: < $this ->message > \n" ;
>

public function customFunction () echo "Мы можем определять новые методы в наследуемом классе\n" ;
>
>


/**
* Создадим класс для тестирования исключения
*/
class TestException
public $var ;

const THROW_NONE = 0 ;
const THROW_CUSTOM = 1 ;
const THROW_DEFAULT = 2 ;

function __construct ( $avalue = self :: THROW_NONE )

switch ( $avalue ) case self :: THROW_CUSTOM :
// Выбрасываем собственное исключение
throw new MyException ( '1 - неправильный параметр' , 5 );
break;

case self :: THROW_DEFAULT :
// Выбрасываем встроеное исключение
throw new Exception ( '2 - недопустимый параметр' , 6 );
break;

default:
// Никаких исключений, объект будет создан.
$this -> var = $avalue ;
break;
>
>
>


// Пример 1
try $o = new TestException ( TestException :: THROW_CUSTOM );
> catch ( MyException $e ) < // Будет перехвачено
echo "Поймано собственное переопределённое исключение\n" , $e ;
$e -> customFunction ();
> catch ( Exception $e ) < // Будет пропущено
echo "Поймано встроенное исключение\n" , $e ;
>

// Отсюда будет продолжено выполнение программы
var_dump ( $o ); // Null
echo "\n\n" ;


// Пример 2
try $o = new TestException ( TestException :: THROW_DEFAULT );
> catch ( MyException $e ) < // Тип исключения не совпадёт
echo "Поймано переопределённое исключение\n" , $e ;
$e -> customFunction ();
> catch ( Exception $e ) < // Будет перехвачено
echo "Перехвачено встроенное исключение\n" , $e ;
>

// Отсюда будет продолжено выполнение программы
var_dump ( $o ); // Null
echo "\n\n" ;


// Пример 3
try $o = new TestException ( TestException :: THROW_CUSTOM );
> catch ( Exception $e ) < // Будет перехвачено
echo "Поймано встроенное исключение\n" , $e ;
>

// Продолжение исполнения программы
var_dump ( $o ); // Null
echo "\n\n" ;


// Пример 4
try $o = new TestException ();
> catch ( Exception $e ) < // Будет пропущено, т.к. исключение не выбрасывается
echo "Поймано встроенное исключение\n" , $e ;
>

// Продолжение выполнения программы
var_dump ( $o ); // TestException
echo "\n\n" ;
?>

PHP has an exception model similar to that of other programming languages. An exception can be throw n, and caught (" catch ed") within PHP. Code may be surrounded in a try block, to facilitate the catching of potential exceptions. Each try must have at least one corresponding catch or finally block.

If an exception is thrown and its current function scope has no catch block, the exception will "bubble up" the call stack to the calling function until it finds a matching catch block. All finally blocks it encounters along the way will be executed. If the call stack is unwound all the way to the global scope without encountering a matching catch block, the program will terminate with a fatal error unless a global exception handler has been set.

The thrown object must be an instance of the Exception class or a subclass of Exception . Trying to throw an object that is not will result in a PHP Fatal Error.

As of PHP 8.0.0, the throw keyword is an expression and may be used in any expression context. In prior versions it was a statement and was required to be on its own line.

catch

A catch block defines how to respond to a thrown exception. A catch block defines one or more types of exception or error it can handle, and optionally a variable to which to assign the exception. (The variable was required prior to PHP 8.0.0.) The first catch block a thrown exception or error encounters that matches the type of the thrown object will handle the object.

Multiple catch blocks can be used to catch different classes of exceptions. Normal execution (when no exception is thrown within the try block) will continue after that last catch block defined in sequence. Exceptions can be throw n (or re-thrown) within a catch block. If not, execution will continue after the catch block that was triggered.

When an exception is thrown, code following the statement will not be executed, and PHP will attempt to find the first matching catch block. If an exception is not caught, a PHP Fatal Error will be issued with an " Uncaught Exception . " message, unless a handler has been defined with set_exception_handler() .

As of PHP 7.1.0, a catch block may specify multiple exceptions using the pipe ( | ) character. This is useful for when different exceptions from different class hierarchies are handled the same.

As of PHP 8.0.0, the variable name for a caught exception is optional. If not specified, the catch block will still execute but will not have access to the thrown object.

finally

A finally block may also be specified after or instead of catch blocks. Code within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.

One notable interaction is between the finally block and a return statement. If a return statement is encountered inside either the try or the catch blocks, the finally block will still be executed. Moreover, the return statement is evaluated when encountered, but the result will be returned after the finally block is executed. Additionally, if the finally block also contains a return statement, the value from the finally block is returned.

Global exception handler

If an exception is allowed to bubble up to the global scope, it may be caught by a global exception handler if set. The set_exception_handler() function can set a function that will be called in place of a catch block if no other block is invoked. The effect is essentially the same as if the entire program were wrapped in a try - catch block with that function as the catch .

Notes

Note:

Internal PHP functions mainly use Error reporting, only modern Object-oriented extensions use exceptions. However, errors can be easily translated to exceptions with ErrorException. This technique only works with non-fatal errors, however.

function exceptions_error_handler ( $severity , $message , $filename , $lineno ) throw new ErrorException ( $message , 0 , $severity , $filename , $lineno );
>

Examples

function inverse ( $x ) if (! $x ) throw new Exception ( 'Division by zero.' );
>
return 1 / $x ;
>

try echo inverse ( 5 ) . "\n" ;
echo inverse ( 0 ) . "\n" ;
> catch ( Exception $e ) echo 'Caught exception: ' , $e -> getMessage (), "\n" ;
>

// Continue execution
echo "Hello World\n" ;
?>

The above example will output:

function inverse ( $x ) if (! $x ) throw new Exception ( 'Division by zero.' );
>
return 1 / $x ;
>

try echo inverse ( 5 ) . "\n" ;
> catch ( Exception $e ) echo 'Caught exception: ' , $e -> getMessage (), "\n" ;
> finally echo "First finally.\n" ;
>

try echo inverse ( 0 ) . "\n" ;
> catch ( Exception $e ) echo 'Caught exception: ' , $e -> getMessage (), "\n" ;
> finally echo "Second finally.\n" ;
>

// Continue execution
echo "Hello World\n" ;
?>

The above example will output:

function test () try throw new Exception ( 'foo' );
> catch ( Exception $e ) return 'catch' ;
> finally return 'finally' ;
>
>

The above example will output:

class MyException extends Exception

class Test public function testing () try try throw new MyException ( 'foo!' );
> catch ( MyException $e ) // rethrow it
throw $e ;
>
> catch ( Exception $e ) var_dump ( $e -> getMessage ());
>
>
>

$foo = new Test ;
$foo -> testing ();

The above example will output:

class MyException extends Exception

class MyOtherException extends Exception

class Test public function testing () try throw new MyException ();
> catch ( MyException | MyOtherException $e ) var_dump ( get_class ( $e ));
>
>
>

$foo = new Test ;
$foo -> testing ();

The above example will output:

Only permitted in PHP 8.0.0 and later.

class SpecificException extends Exception <>

function test () throw new SpecificException ( 'Oopsie' );
>

try test ();
> catch ( SpecificException ) print "A SpecificException was thrown, but we don't care about the details." ;
>
?>

Only permitted in PHP 8.0.0 and later.

class SpecificException extends Exception <>

function test () do_something_risky () or throw new Exception ( 'It did not work' );
>

try test ();
> catch ( Exception $e ) print $e -> getMessage ();
>
?>

User Contributed Notes 12 notes

If you intend on creating a lot of custom exceptions, you may find this code useful. I've created an interface and an abstract exception class that ensures that all parts of the built-in Exception class are preserved in child classes. It also properly pushes all information back to the parent constructor ensuring that nothing is lost. This allows you to quickly create new exceptions on the fly. It also overrides the default __toString method with a more thorough one.

interface IException
/* Protected methods inherited from Exception class */
public function getMessage (); // Exception message
public function getCode (); // User-defined Exception code
public function getFile (); // Source filename
public function getLine (); // Source line
public function getTrace (); // An array of the backtrace()
public function getTraceAsString (); // Formated string of trace

/* Overrideable methods inherited from Exception class */
public function __toString (); // formated string for display
public function __construct ( $message = null , $code = 0 );
>

abstract class CustomException extends Exception implements IException
protected $message = 'Unknown exception' ; // Exception message
private $string ; // Unknown
protected $code = 0 ; // User-defined exception code
protected $file ; // Source filename of exception
protected $line ; // Source line of exception
private $trace ; // Unknown

public function __construct ( $message = null , $code = 0 )
if (! $message ) throw new $this ( 'Unknown ' . get_class ( $this ));
>
parent :: __construct ( $message , $code );
>

public function __toString ()
return get_class ( $this ) . " ' < $this ->message > ' in < $this ->file > ( < $this ->line > )\n"
. " < $this ->getTraceAsString ()> " ;
>
>
?>

Now you can create new exceptions in one line:

class TestException extends CustomException <>
?>

Here's a test that shows that all information is properly preserved throughout the backtrace.

function exceptionTest ()
try throw new TestException ();
>
catch ( TestException $e ) echo "Caught TestException (' < $e ->getMessage ()> ')\n < $e >\n" ;
>
catch ( Exception $e ) echo "Caught Exception (' < $e ->getMessage ()> ')\n < $e >\n" ;
>
>

echo '

' . exceptionTest () . '
' ;
?>

Here's a sample output:

Таким образом, все исключения вашего кода можно будет отличить от исключений не вашего кода.


2. Исключения должны быть иерархичны. У вас должен быть базовый класс исключений, от которого наследуются все исключения, бросаемые в вашем коде. Например, у вас в коде есть модуль для работы с файлами fileModule, объявите исключение, которое будет бросаться только этим модулем


Если вам нужна еще бОльшая различимость ошибок, например, среди всех ошибок, связанных с работой с файлами, вы хотите различать ситуацию, когда файл не найден, то нужно объявить еще одно исключение


Соблюдая иерархичность, вы сможете различать исключения от разных модулей в вашем приложении. Я не призываю наплодить кучу исключений, для каждого модуля. Исключения должны проектироваться не от кода, а от ситуаций, которые вы хотите по-особенному обработать.
И обратная ситуация, не скупитесь сделать разные исключения, если того требует обстоятельства

try <
//.
> catch ( fileModuleException $e ) <
switch ( $e -> getCode ( ) ) < //так делать не надо
case 1 : echo 'file not found' ;
case 2 : echo 'file not readable' ;
//.
>
>

3. Не обрабатывайте исключения, если в данном контексте не понятно, как его обработать. Например, если вы следуете паттерну MVC, то в методе модели может быть не понятно, как обработать ошибку — как ее вывести, потому как за логику отвечает control, а за вывод view. Если не понятно, что делать с исключением, то «пробросьте» его дальше.

try <
$db -> begin ( ) ;
//.
$db -> commit ( ) ;
> catch ( Exception $e ) <
$db -> rollback ( ) ;
throw $e ;
>


От метода, который пробрасывает исключения, можно ожидать любых исключений. Можно сузить количество исключений, бросаемых методом, преобразовав исключение:

try <
//.
> catch ( Exception $e ) <
throw new baseException ( $message , 0 , $e ) ; //не разрывайте цепь
>

Тут очень важный момент — не разрывать цепь исключений. Третьим параметром передается изначальное исключение. Этот код нативно работает в 5.3 и с доработкой в 5.2. При таком подходе стек вызовов будет «цельным» от самого первого броска исключения.

4. У вас должен быть глобальный обработчик исключений. Это может быть или try. catch на самом верхнем уровне или ExceptionHandler. Все исключения, которые добрались до глобального обработчика, считаются критическими, так как не были правильно обработаны ранее. Их надо залогировать.


6. Преобразуйте все ошибки утверждений (assertion fail) и не фатальные ошибки в исключения (см. мою предыдущую статью)

7. Никогда не глушите исключения без какой либо обработки


потому, что в противном случае ошибку из-за таких действий будет очень сложно найти. Нужно хотя бы логировать:

8. Документируйте исключения. Указывайте в докблоке, какие исключения выбрасывает метод (таг @throws, можно указывать больше одного). Это упростит всем жизнь.

Вот в принципе и все, что нужно знать про исключения. Еще один интересный факт напоследок — исключения можно ловить по интерфейсу:

interface iException < >
class customException extends baseException implements iException < >
try <
//.
> catch ( iException $e ) <
//.
>

UPD исправлены замечания в комментариях:1, 2 и 3 (спасибо всем, кто поучаствовал в обсуждении).
Отдельное спасибо, хабраюзеру ckopobapkuh за активное участие

Читайте также: