mirror of
https://github.com/php/doc-ru.git
synced 2025-07-22 18:26:31 +00:00
559 lines
22 KiB
XML
559 lines
22 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
||
<!-- EN-Revision: 14cd35a23dab08e3405277d90358853c6312df1f Maintainer: irker Status: ready -->
|
||
<!-- Reviewed: yes -->
|
||
<!-- $Revision$ -->
|
||
<chapter xml:id="language.exceptions" xmlns="http://docbook.org/ns/docbook">
|
||
<title>Исключения</title>
|
||
|
||
<sect1 xml:id="language.exceptions.extending">
|
||
<title>Наследование исключений</title>
|
||
<para>
|
||
Определённый пользователем класс исключения должен быть определён, как
|
||
класс расширяющий (наследующий) встроенный класс Exception. Ниже приведены методы и
|
||
свойства класса Exception, доступные дочерним классам.
|
||
</para>
|
||
<example>
|
||
<title>Встроенный класс Exception</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
class Exception implements Throwable
|
||
{
|
||
protected $message = 'Unknown exception'; // сообщение об исключении
|
||
private $string; // свойство для __toString
|
||
protected $code = 0; // пользовательский код исключения
|
||
protected $file; // файл, в котором было выброшено исключение
|
||
protected $line; // строка, в которой было выброшено исключение
|
||
private $trace; // трассировка вызовов методов и функций
|
||
private $previous; // предыдущее исключение, если исключение вложенное
|
||
|
||
public function __construct($message = '', $code = 0, Throwable $previous = null);
|
||
|
||
final private function __clone(); // запрещает клонирования исключения
|
||
|
||
final public function getMessage(); // сообщение исключения
|
||
final public function getCode(); // код исключения
|
||
final public function getFile(); // файл, где выброшено исключение
|
||
final public function getLine(); // строка, на которой выброшено исключение
|
||
final public function getTrace(); // массив backtrace()
|
||
final public function getPrevious(); // предыдущее исключение
|
||
final public function getTraceAsString(); // отформатированная строка трассировки
|
||
|
||
// Переопределяемый
|
||
public function __toString(); // отформатированная строка для отображения
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<para>
|
||
Если класс, наследуемый от Exception переопределяет <link
|
||
linkend="language.oop5.decon">конструктор</link>, необходимо вызвать в
|
||
конструкторе <link linkend="language.oop5.paamayim-nekudotayim"
|
||
>parent::__construct()</link>, чтобы быть уверенным, что все доступные данные
|
||
были правильно присвоены. Метод <link linkend="language.oop5.magic">__toString()</link>
|
||
может быть переопределён, чтобы обеспечить нужный вывод, когда объект
|
||
преобразуется в строку.
|
||
</para>
|
||
<note>
|
||
<para>
|
||
Исключения нельзя клонировать. Попытка <link
|
||
linkend="language.oop5.cloning">клонировать</link> исключение приведёт
|
||
к неисправимой ошибке <constant>E_ERROR</constant>.
|
||
</para>
|
||
</note>
|
||
<example>
|
||
<title>Наследование класса Exception</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
/**
|
||
* Определим свой класс исключения
|
||
*/
|
||
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";
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</sect1>
|
||
|
||
<simplesect xml:id="language.exceptions.introduction">
|
||
<para>
|
||
Модель исключений (exceptions) в PHP похожа с используемыми в других языках программирования.
|
||
Исключение можно сгенерировать (выбросить) при помощи оператора
|
||
&throw;, и можно перехватить (поймать)
|
||
оператором &catch;. Код генерирующий исключение, должен
|
||
быть окружён блоком &try;, для того, чтобы можно было
|
||
перехватить исключение. Каждый блок &try;
|
||
должен иметь как минимум один соответствующий ему блок &catch; или &finally;.
|
||
</para>
|
||
<para>
|
||
В случае, если выброшено исключение, для которого нет блока &catch; в текущей функции,
|
||
это исключение будет "всплывать" по стеку вызова, пока не будет найден
|
||
подходящий блок &catch;. При этом, все встреченные блоки &finally; будут исполнены.
|
||
Если стек вызовов раскрутится до глобальной области видимости, не встретив подходящего
|
||
блока &catch;, программа завершит работу с фатальной ошибкой, если только у вас
|
||
не настроен глобальный обработчик исключений.
|
||
</para>
|
||
<para>
|
||
Генерируемый объект должен принадлежать классу <classname>Exception</classname>
|
||
или наследоваться от <classname>Exception</classname>. Попытка сгенерировать
|
||
исключение другого класса приведёт к фатальной ошибке PHP.
|
||
</para>
|
||
<para>
|
||
Начиная с PHP 8.0.0, ключевое слово &throw; является выражением и может использоваться
|
||
в контексте других выражений. В более ранних версиях оно являлось оператором и требовало
|
||
размещения в отдельной строке.
|
||
</para>
|
||
</simplesect>
|
||
|
||
<simplesect xml:id="language.exceptions.catch">
|
||
<title><literal>catch</literal></title>
|
||
<para>
|
||
Блок &catch; определяет то, как следует реагировать на выброшенное исключение.
|
||
В блоке &catch; указывается один или более типов исключений или ошибок(Error), которые он
|
||
будет обрабатывать. Также указывается и переменная, которой будет присвоено
|
||
пойманное исключение (начиная с PHP 8.0.0 задавать эту переменную не обязательно).
|
||
Выброшенное исключение или ошибка будут обработаны первым подходящим блоком &catch;.
|
||
</para>
|
||
<para>
|
||
Можно использовать несколько блоков &catch;, перехватывающих различные классы
|
||
исключений. Нормальное выполнение (когда не генерируются исключения в блоках
|
||
&try;) будет продолжено за последним блоком &catch;. Исключения могут быть
|
||
сгенерированы (или вызваны ещё раз) оператором &throw; внутри блока &catch;.
|
||
Если нет, то исполнение будет продолжено после отработки блока &catch;.
|
||
</para>
|
||
<para>
|
||
При генерации исключения код, следующий после описываемого выражения,
|
||
не будет выполнен, а PHP попытается найти
|
||
первый блок &catch;, перехватывающий исключение данного
|
||
класса. Если исключение не будет перехвачено, PHP выдаст фатальную
|
||
ошибку: "<literal>Uncaught Exception ...</literal>" (Неперехваченное
|
||
исключение), если не был определён обработчик ошибок при помощи
|
||
функции <function>set_exception_handler</function>.
|
||
</para>
|
||
<para>
|
||
Начиная с PHP 7.1.0, блок &catch; может принимать несколько типов исключений с помощью
|
||
символа (<literal>|</literal>). Это полезно, когда разные исключения из разных
|
||
иерархий классов обрабатываются одинаково.
|
||
</para>
|
||
<para>
|
||
Начиная с PHP 8.0.0, задание переменной для пойманного исключения опционально.
|
||
Если она не задана, блок &catch; будет исполняться, но не будет иметь доступа
|
||
к объекту исключения.
|
||
</para>
|
||
</simplesect>
|
||
|
||
<simplesect xml:id="language.exceptions.finally">
|
||
<title><literal>finally</literal></title>
|
||
<para>
|
||
Блок &finally; также можно использовать
|
||
после или вместо блока &catch;. Код в блоке
|
||
&finally; всегда будет выполняться после кода в блоках
|
||
&try; и &catch;, независимо от того, было ли
|
||
выброшено исключение, перед тем как продолжится нормальное выполнение кода.
|
||
</para>
|
||
<para>
|
||
Одно важное взаимодействие происходит между блоком &finally; и оператором &return;.
|
||
Если оператор &return; встречается внутри блоков &try; или &catch;, блок &finally; всё равно будет выполнен. Кроме того, оператор &return; выполняется, когда встречается, но результат будет возвращён после выполнения блока &finally;.
|
||
Если блок &finally; также содержит оператор &return;, возвращается значение, указанное в блоке &finally;.
|
||
</para>
|
||
</simplesect>
|
||
|
||
<simplesect xml:id="language.exceptions.exception-handler">
|
||
<title><literal>Глобальный обработчик исключений</literal></title>
|
||
<para>
|
||
Если исключение дошло по стеку вызовов до глобальной области видимости, оно может быть
|
||
обработано глобальным обработчиком исключений, если он задан.
|
||
С помощью функции <function>set_exception_handler</function> можно задать функцию,
|
||
которая будет выполнена вместо блока &catch;, если не нашлось подходящего. Эффект
|
||
аналогичен тому, как будто мы всю нашу программу обернули в блок &try;-&catch;, где
|
||
за реализацию блока &catch; отвечает установленная функция.
|
||
</para>
|
||
</simplesect>
|
||
|
||
<simplesect xml:id="language.exceptions.notes">
|
||
&reftitle.notes;
|
||
|
||
<note>
|
||
<para>
|
||
Внутренние функции PHP в основном используют
|
||
<link linkend="ini.error-reporting">сообщения об ошибках</link>, и только
|
||
новые <link linkend="language.oop5">объектно-ориентированные</link>
|
||
расширения используют исключения. Однако, ошибки можно легко преобразовать
|
||
в исключения с помощью класса <link linkend="class.errorexception">ErrorException</link>.
|
||
Однако это не сработает для фатальных ошибок.
|
||
</para>
|
||
<example>
|
||
<title>Преобразование сообщения об ошибках в исключение</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
function exceptions_error_handler($severity, $message, $filename, $lineno) {
|
||
throw new ErrorException($message, 0, $severity, $filename, $lineno);
|
||
}
|
||
|
||
set_error_handler('exceptions_error_handler');
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</note>
|
||
<tip>
|
||
<para>
|
||
<link linkend="intro.spl">Стандартная библиотека PHP (SPL)</link> предоставляет
|
||
хороший набор <link linkend="spl.exceptions">встроенных классов исключений</link>.
|
||
</para>
|
||
</tip>
|
||
</simplesect>
|
||
|
||
<simplesect xml:id="language.exceptions.examples">
|
||
&reftitle.examples;
|
||
|
||
<example>
|
||
<title>Выбрасывание исключений</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
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";
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
0.2
|
||
Выброшено исключение: Деление на ноль.
|
||
Привет, мир
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example>
|
||
<title>Обработка исключений с помощью блока &finally;</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
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";
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
0.2
|
||
Первый блок finally.
|
||
Поймано исключение: Деление на ноль.
|
||
Второй блок finally.
|
||
Привет, мир
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example>
|
||
<title>Взаимодействие между блоками &finally; и &return;</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
function test() {
|
||
try {
|
||
throw new Exception('foo');
|
||
} catch (Exception $e) {
|
||
return 'catch';
|
||
} finally {
|
||
return 'finally';
|
||
}
|
||
}
|
||
|
||
echo test();
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
finally
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example>
|
||
<title>Вложенные исключения</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
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();
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
string(4) "foo!"
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example>
|
||
<title>Обработка нескольких исключений в одном блоке catch</title>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
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();
|
||
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
&example.outputs;
|
||
<screen>
|
||
<![CDATA[
|
||
string(11) "MyException"
|
||
]]>
|
||
</screen>
|
||
</example>
|
||
<example>
|
||
<title>Пример блока &catch; без указания переменной</title>
|
||
<para>Допустимо начиная с PHP 8.0.0</para>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
class SpecificException extends Exception {}
|
||
|
||
function test() {
|
||
throw new SpecificException('Ой!');
|
||
}
|
||
|
||
try {
|
||
test();
|
||
} catch (SpecificException) {
|
||
print "Было поймано исключение SpecificException, но нам безразлично, что у него внутри.";
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
<example>
|
||
<title>Throw как выражение</title>
|
||
<para>Only permitted in PHP 8.0.0 and later.</para>
|
||
<programlisting role="php">
|
||
<![CDATA[
|
||
<?php
|
||
|
||
class SpecificException extends Exception {}
|
||
|
||
function test() {
|
||
do_something_risky() or throw new Exception('Всё сломалось');
|
||
}
|
||
|
||
try {
|
||
test();
|
||
} catch (Exception $e) {
|
||
print $e->getMessage();
|
||
}
|
||
?>
|
||
]]>
|
||
</programlisting>
|
||
</example>
|
||
</simplesect>
|
||
|
||
</chapter>
|
||
|
||
<!-- Keep this comment at the end of the file
|
||
Local variables:
|
||
mode: sgml
|
||
sgml-omittag:t
|
||
sgml-shorttag:t
|
||
sgml-minimize-attributes:nil
|
||
sgml-always-quote-attributes:t
|
||
sgml-indent-step:1
|
||
sgml-indent-data:t
|
||
indent-tabs-mode:nil
|
||
sgml-parent-document:nil
|
||
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
||
sgml-exposed-tags:nil
|
||
sgml-local-catalogs:nil
|
||
sgml-local-ecat-files:nil
|
||
End:
|
||
vim600: syn=xml fen fdm=syntax fdl=2 si
|
||
vim: et tw=78 syn=sgml
|
||
vi: ts=1 sw=1
|
||
-->
|