Files
php-doc-ru/language/exceptions.xml
2021-06-08 21:04:42 +03:00

559 lines
22 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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