Exceptions
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 instanceof Throwable . 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.
Example #1 Converting error reporting to exceptions
function exceptions_error_handler ( $severity , $message , $filename , $lineno ) throw new ErrorException ( $message , 0 , $severity , $filename , $lineno );
>
?php
Tip
Examples
Example #2 Throwing an Exception
function inverse ( $x ) if (! $x ) throw new Exception ( ‘Division by zero.’ );
>
return 1 / $x ;
>
?php
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:
0.2 Caught exception: Division by zero. Hello World
Example #3 Exception handling with a finally block
function inverse ( $x ) if (! $x ) throw new Exception ( ‘Division by zero.’ );
>
return 1 / $x ;
>
?php
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:
0.2 First finally. Caught exception: Division by zero. Second finally. Hello World
Example #4 Interaction between the finally block and return
function test () try throw new Exception ( ‘foo’ );
> catch ( Exception $e ) return ‘catch’ ;
> finally return ‘finally’ ;
>
>
The above example will output:
finally
Example #5 Nested Exception
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:
string(4) "foo!"
Example #6 Multi catch exception handling
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:
string(11) "MyException"
Example #7 Omitting the caught variable
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.» ;
>
?>
Example #8 Throw as an expression
Only permitted in PHP 8.0.0 and later.
function test () do_something_risky () or throw new Exception ( ‘It did not work’ );
>
try test ();
> catch ( Exception $e ) print $e -> getMessage ();
>
?>
User Contributed Notes 14 notes
14 years ago
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:
Caught TestException (‘Unknown TestException’)
TestException ‘Unknown TestException’ in C:\xampp\htdocs\CustomException\CustomException.php(31)
#0 C:\xampp\htdocs\CustomException\ExceptionTest.php(19): CustomException->__construct()
#1 C:\xampp\htdocs\CustomException\ExceptionTest.php(43): exceptionTest()
#2
Обработка исключений
В процессе работы программы могут возникать различные ошибки, которые могут прервать работу программы. Например, рассмотрим следующую ситуацию:
$a = 5; $b = 0; $result = $a / $b; echo $result; echo "Конец работы программы";
Программа выводит результат деления. Поскольку делитель равен 0, а на ноль делить нельзя, то при выполнении деления программа завершится, и в браузере мы увидим что-то типа следующего:
Fatal error: Uncaught DivisionByZeroError: Division by zero in D:\localhost\hello.php:11 Stack trace: #0 thrown in D:\localhost\hello.php on line 11
Браузер отобразит нам произошедшую ошибку, причем дальше после строки с делением программа даже не будет выполняться.
Кто-то может сказать, что ситуация искуственная, так как мы сами определили делитель равный нулю. Но данные могут передаваться извне. Кроме того, кроме деления на ноль есть различные ситуации, при которых могут происходить ошибки. Но PHP предоставляет ряд возможностей для обработки подобных ситуаций.
Для обработки исключений в PHP применяется конструкция try-catch :
try < // код, который может вызвать исключение >catch(Тип_исключения $ex) < // обработка исключения >
Эта конструкция в общем варианте состоит из двух блоков — try и catch . В блок try помещается код, который потенциально может вызвать исключение. А в блоке catch помещается обработка возникшего исключения. Причем каждого типа исключения мы можем определить свою логику обработки. Конкретный тип исключения, который мы хотим обработать, указывается в круглых скобках после оператора catch :
catch(Тип_исключения $ex)
После названия типа указывается переменная этого типа (в данном случае $ex ), которая будет хранить информацию об исключении и которую мы можем использовать при обработке исключения.
Если в блоке try при выполнении кода возникает ошибка, то блок try прекращает выполнение и передает управление блоку catch , который обрабатывает ошибку. А после завершения выполнения кода в блоке catch программа продолжает выполнять инструкции, которые размещены после блока catch .
Если в блоке try при выполнении кода не возникает ошибок, то блок catch не выполняется, а после завершения блока try программа продолжает выполнять инструкции, которые размещены после блока catch .
Например, обработаем ошибку с делением на ноль:
try < // код, который может вызвать исключение $a = 5; $b = 0; $result = $a / $b; echo $result; >catch(DivisionByZeroError $ex) < // обработка исключения echo "Произошло исключение:
"; echo $ex . "
"; > echo "Конец работы программы";
В данном случае код деления на ноль, поскольку он может потенциально вызвать ошибку, помещен в блок try .
В блоке catch обрабатывается ошибка типа DivisionByZeroError , которая генерируется при делении на ноль. Вся обработка сводится к выводу информации на экран.
В итоге при выполнении программа выведет следующее:
Произошло исключение: DivisionByZeroError: Division by zero in D:\localhost\hello.php:14 Stack trace: #0 Конец работы программы
Как видно из вывода программы, она не завершается аварийно при делении на ноль, а продолжает работу.
Типы ошибок и исключений
В PHP для разных ситуаций есть множество типов, которые описывают ошибки. Все эти встроенные типы применяют интерфейс Throwable :
Все типы делятся на две группы: собственно ошибки (класс Error ) и собственно исключения (класс Exception ). А от классов Error и Exception наследуются классы ошибок и исключений, которые описывают конкретные ситуации. Например, от класса Error наследуется класс ArithmeticError , который описывает ошибки, возникающие при выполнении арифметических операций. А от класса ArithmeticError наследуется класс DivisionByZeroError , который представляют ошибку при делении на ноль.
Блок catch
Конструкция try..catch позволяет определить несколько блоков catch — для обработки различных типов ошибок и исключений:
try < $result = 5 / 0; echo $result; >catch(ParseError $p) < echo "Произошла ошибка парсинга"; >catch(DivisionByZeroError $d)
При возникновении ошибки будет для ее обработки будет выбираться тот блок catch , который соответствует вошникшей ошибки. Так, в данном случае при делении на ноль будет выполняться второй блок catch .
Если бы в блоке try возникла бы ошибка, которая бы не соответствовала типам из блоков catch (в данном случае — типам DivisionByZeroError и ParseError), то такая ошибка не была бы обработана, и соответственно программа бы аварийно завершила свое выполнение.
Блоки catch с более конкретными типами ошибок и исключений должны идти в начале, а более с более общими типа — в конце:
try < $result = 5 / 0; echo $result; >catch(DivisionByZeroError $ex) < echo "На ноль делить нельзя"; >catch(ArithmeticError $ex) < echo "Ошибка при выполнении арифметической операции"; >catch(Error $ex) < echo "Произошла ошибка"; >catch(Throwable $ex)
Класс DivisionByZeroError унаследован от ArithmeticError, который, в свою очередь, унаследован от Error, реализующего интерфейс Throwable. Поэтому класс DivisionByZeroError представляет более конкретный тип и представляемые им ошибки должны обрабатываться в первую очередь. А тип Throwable представляет наиболее общий тип, так как ему соответствуют все возможные ошибки и исключения, поэтому блоки catch с таким типом должны идти в конце.
В данном случае опять же в блоке try происходит ошибка деления на ноль. Но этой ошибке соответствуют все четыре блока catch . Для обработки PHP будет выбирать первый попавшийся, который соответствует типу ошибки. В данном случае это блок для обработки ошибки типа DivisionByZeroError.
Если нам надо обрабатывать в принципе все ошибки и исключения, то мы можем определить только обработку общего для всех них типа Throwable:
try < $result = 5 / 0; echo $result; >catch(Throwable $ex)
Начиная с версии PHP 8.0 в блоке catch можно просто указать тип обрабатываемого исключения, не определяя переменную:
catch(DivisionByZeroError)
Получение информации об ошибках и исключениях
Интерфейс Throwable предоставляет ряд методов, которые позволяют получить некоторую информацию о возникшем исключении:
- getMessage() : возвращает сообщение об ошибке
- getCode() : возвращает код исключения
- getFile() : возвращает название файла, в котором возникла ошибка
- getLine() : возвращает номер строки, в которой возникла ошибка
- getTrace() : возвращает трассировку стека
- getTraceAsString() : возвращает трассировку стека в виде строки
Применим некоторые из этих методов:
try < $result = 5 / 0; echo $result; >catch(DivisionByZeroError $ex) < echo "Сообщение об ошибке: " . $ex->getMessage() . "
"; echo "Файл: " . $ex->getFile() . "
"; echo "Номер строки: " . $ex->getLine() . "
"; >
Сообщение об ошибке: Division by zero Файл: D:\localhost\hello.php Номер строки: 11
Блок finally
Конструкция try..catch также может определять блок finally . Этот блок выполняется в конце — после блока try и catch вне зависимости, возникла или нет ошибка. Нередко блок finally используется для закрытия ресурсов, которые применяются в блоке try.
try < $result = 5 / 0; echo $result . "
"; > catch(Throwable $ex) < echo "Ошибка при выполнении программы
"; > finally < echo "Блок finally
"; > echo "Конец работы программы";
Ошибка при выполнении программы Блок finally Конец работы программы
Конструкция try..catch..finally может содержать либо все три блока, либо только два блока try и либо блок catch , либо блок finally .
Исключения — PHP: Погружаясь в классы
Исключения – один из немногих примеров удачного использования наследования. В этом уроке мы научимся создавать свои исключения и перехватывать их.
Обычно исключения используются так. Ближе к началу программы стоит конструкция try/catch, которая ловит исключения и показывает пользователю адекватное сообщение:
try doSomethingDangerous(); > catch (\Exception $e) echo 'Something happens'; >
Но как понять что случилось? Иногда это важно. Разные ошибки могут приводить к разному поведению программы. Кроме того, не все ошибки требуют обработки в текущем месте программы.
Разделять ошибки можно с помощью разных классов (или интерфейсов) наследующихся от класса Exception, который в свою очередь реализует интерфейс Throwable . По техническим причинам, реализовать этот интерфейс напрямую нельзя, поэтому остаётся только один путь – наследование.
namespace App; class MyException extends \Exception <>
Причём в отличие от других примеров наследования, в исключениях редко нужно добавлять или изменять поведение. Основная цель использования наследования исключений – описание всех возможных типов ошибок.
Теперь посмотрим как этим можно воспользоваться:
try // Какой-то код > catch (MyException $e) // Проверка instanceof // Делаем что-нибудь одно > catch (\Exception $e) // Делаем что-нибудь другое >
Конструкция try/catch работает очень похоже на switch, но только для исключений. С её помощью можно описать обработку каждого типа исключений добавив новый блок catch. Во время срабатывания исключения, PHP проверяет каждый блок catch на instanceof, начиная с верхнего. Поэтому порядок блоков catch имеет важное значение.
В самом PHP уже есть иерархия исключений, которая позволяет разделять реакцию на разные типы ошибок:
Перехват любого базового исключения, автоматически влечёт за собой перехват всех наследников текущего класса. Например, если в блоке catch перехватывать RuntimeException, то этот блок поймает все ошибки, входящие в иерархию RuntimeException.
В PHP есть негласное правило о том, как работать с иерархиями исключений. Любая программа, должна определять своё собственное высокоуровневое исключение, которое наследуется от RuntimeException. Все остальные исключения библиотеки наследуются от него. Такой подход позволяет изолировать обработку ошибок конкретной библиотеки буквально одним блоком catch. Например, в HTTP-клиенте Guzzle , базовое исключение TransferException . Это значит, что среди всех ошибок мы можем перехватить ошибки этого клиента:
try $client = new \GuzzleHttp\Client(); $response = $client->get('https://ru.hexlet.io'); > catch (Guzzle\TransferException) // Сюда попадут все ошибки библиотеки Guzzle >
Блок finally
В некоторых ситуациях бывает нужно продолжить работу независимо от того, возникло исключение или нет. Используя только try/catch, эту задачу нельзя выполнить без дублирования. Придётся размещать код как после всей конструкции try/catch, так и в каждом блоке catch.
Это привело к тому, что саму конструкцию расширили, добавив в неё блок finally. Этот блок вызывается в самом конце и в любом случае:
try // Какой-то код > catch (MyException $e) // Делаем что-нибудь одно > finally
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Блок catch не срабатывает (PHP, конструкция try catch)
У меня конечно элементарный вопрос, но вот никак не могу понять принцип работы конструкции try-catch на php. В общем имеем максимально простой код, как внизу. Функция checkNum($number) проверяет является ли число больше или меньше 5. Функция выполняется в блоке try. Если в блоке try мы нарочно сделаем какую-то ошибку (например назовем функцию checkNNNums, которой не существует), то это по идее должно приводить к исключению и выполнению блока catch. Действительно появляется ошибка, но код, который в блоке catch не срабатывает, т.е. например мы не видим заложенной в него надписи ‘Сработал блок Catch — Сообщение: ‘, а просто получаем Fatal error: Uncaught Error: Call to undefined function checkNums() Мне нужно отработать именно try-catch, понимаю, что есть варианты решения через альтернативные варианты, но нужно именно таким образом. PHP 7.3 Код ниже:
5) < echo "число больше 5"; >else < echo "число меньше 5"; >> try < checkNum(7); echo 'Сработал блок try'; >catch(Exception $e) < echo 'Сработал блок Catch - Сообщение: ' .$e->getMessage(); > ?>
Отслеживать
user285292
задан 6 янв 2020 в 19:11
11 4 4 бронзовых знака
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Если в блоке try мы нарочно сделаем какую-то ошибку (например назовем функцию checkNNNums, которой не существует), то это по идее должно приводить к исключению
Совершенно верно для вашей версии PHP. (не уверен в своей памяти и быстро проверить не удалось, вероятно для всех PHP 7.0 и новее)
Ваше заблуждение и ошибка в том, что вы ожидаете генерацию исключения класса Exception . Возможно вы считаете что все исключения наследованы от Exception . И как раз вот это неверно. Исключение в этом случае генерируется класса Error и этот класс в своём дереве наследования не опирается на Exception . Дерево наследования исключений в PHP7.0 и новее выглядит так:
Throwable Error ArithmeticError DivisionByZeroError AssertionError CompileError ParseError TypeError ArgumentCountError Exception
И если вы замените свой catch на:
catch(\Throwable $e)
То он уже будет ловить эту ошибку в вашей версии PHP.
Тем не менее обратите внимание, что не все ошибки PHP заменены на генерацию исключений. Механизм ошибок и исключений не связаны друг с другом.
