Welcome to php club

PHP FAQ from PHPclub.ru: PHP5/Exception ...

Начало | Каталог | Изменения | НовыеКомментарии | Вам запрещён доступПользователи | Вам запрещён доступРегистрация | Вход:  Пароль:  

Исключения

Общие сведения

PHP4 не имел никакой обработки исключений. В PHP5 появилась модель исключений аналогичная таковой в других языках программирования. Следует отметить, что имеется поддержка “catch all”, но не “finally”. Исключения бросаются оператором throw, ловятся в блоках try и обрабатываются в блоках catch.
Пример:

<?php
try {
    
// здесь производим опасные действия, т.е. те действия которые могут бросить исключение
} catch (Exception $e) {
    
// здесь мы оказываемся если поймали исключение с типом Exception, которое помещается в переменную $e
}
?>

Заметим, что на один try блок может быть несколько catch блоков, и в этом случае при исключении они просматриваются сверху вниз до первого совпадения между классом исключения и классом, указанным в блоке catch. Сравнение производится оператором instanceof.
Блоки try/catch также могут быть вложенными. И если при исключении в текущем блоке не найден его обработчик, то поиск продолжается в родительском. Так происходит, пока исключение не достигнет самого внешнего блока, а если и там не будет найдено соответствующего блока catch, то исключение станет не пойманным, а выполнение программы прервется.
Исключение можно перебросить в родительский блок try/catch, даже если оно было поймано в текущем. Для этого достаточно просто выполнить throw $e; в блоке catch.
Пример:
<?php
class MyException extends Exception {
    function
display() {
        echo
'MyException: ' . $this->getMessage() . "\n";
    }
}

class
MyExceptionFoo extends MyException {
    function
display() {
        echo
'MyExceptionFoo: ' . $this->getMessage() . "\n";
    }
}

try {
    throw new
MyExceptionFoo('Исключение');
} catch (
MyException $exception) {
    
$exception->Display();
} catch (
Exception $exception) {
    echo
$exception;
}
?>

Результат:

Любой пользовательский класс-исключение должен быть потомком встроенного класса Exception. Это сделано для того, чтобы catch (Exception $e) всегда ловил исключение и таким образом реализовывал “catch all”. Встроенный класс исключений также позволяет собрать много полезной информации не доступной никаким другим способом.
Встроенный класс Exception можно представить следующим PHP-кодом. Комментарии показывают назначение каждого свойства и соответствующих методов-получателей. Однако, т.к. некоторые методы используются внутренне, они помечены как финальные. В итоге класс очень ограничен, т.к. ему нужно быть уверенным, что все, что используется внутренне, работает, так как ожидается.
<?php
class Exception {
    function
__construct(/*string*/ $message = NULL, /*int*/ $code = 0) {
        if (
func_num_args()) {
            
$this->message = $message;
        }
        
$this->code = $code;
        
$this->file = __FILE__; // места с ключевым словом throw
        
$this->line = __LINE__; // места с ключевым словом throw
        
$this->trace = debug_backtrace();
        
$this->string = StringFormat($this);
    }

    protected
$message = 'Unknown exception'; // сообщение исключения
    
protected $code = 0; // определенный пользователем код исключения
    
protected $file; // имя файла, где произошло исключение
    
protected $line; // номер строки, где произошло исключение

    
private $trace; // бэктрейс исключения
    
private $string; // строковое представление исключения

    
final function getMessage() {
        return
$this->message;
    }
    final function
getCode() {
        return
$this->code;
    }
    final function
getFile() {
        return
$this->file;
    }
    final function
getTrace() {
        return
$this->trace;
    }
    final function
getTraceAsString() {
        return
self::TraceFormat($this);
    }
    function
__toString() {
        return
$this->string;
    }
    static private function
StringFormat(Exception $exception) {
        
// функция не доступная в виде php-скрипта, которая
        // возвращает всю информацию в виде строки.
    
}
    static private function
TraceFormat(Exception $exception) {
        
// функция не доступная в виде php-скрипта, которая
        // возвращает бэктрейс в виде строки.
    
}
}
?>

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

Проблемы с обратной совместимостью

Cтарый код без функций, методов или классов с именами throw, try и catch должен работать без изменений.

Дополнительные сведения

Описание в оригинальной документации: http://www.php.net/exceptions

Преимущества и недостатки

Преимущества:

  • Единый механизм обработки ошибок.
  • Гарантированный выход из опасного блока. Т.е. нет необходимости анализировать код возврата функции и предпринимать какие-то действия, кроме кидания исключения.
  • Все встроенные классы исключений, будь то DOMException, SQLLiteException, com_exception и др. наследованы от стандартного класса Exception и, следовательно, имеют стандартный интерфейс.

Недостатки:

  • Все встроенные функции не бросают исключений, но это можно легко исправить.

Использование исключений для обработки ошибок

Лучше всего исключения подходят для обработки ошибок ваших собственных скриптов и библиотек, т.к. вы сами определяете каким образом и с какими параметрами бросать исключение. Однако многим хотелось бы обрабатывать через исключения и обычные ошибки времени выполнения PHP. Имея некоторые знания, мы легко можем это сделать.
Исходя из того, что стандартные функции PHP не бросают исключений, а делают обработку ошибок «по старинке», с использованием стандартных способов PHP, можно этим воспользоваться. Также принимая во внимание, что для E_NOTICE будет странным бросать исключение, которое, будучи не пойманным, остановит работу скрипта, можно сконструировать конвертер из сообщений в исключения. Однако для особых параноиков можно второй параметр функции set_error_handler() поменять на E_ALL | E_STRICT.

<?php
class phpException extends exception {
    public function
__construct($errno, $errstr, $errfile, $errline) {
        
parent::__construct();
        
$this->code = $errno;
        
$this->message = $errstr;
        
$this->file = $errfile;
        
$this->line = $errline;
    }
}

function
err2exc($errno, $errstr, $errfile, $errline) {
    throw new
phpException($errno, $errstr, $errfile, $errline);
}

set_error_handler('err2exc', E_ALL & ~E_NOTICE &~ E_USER_NOTICE | E_STRICT);
error_reporting(E_ALL | E_STRICT);
?>

Таким образом, мы имеем возможность ловить phpException для ошибок PHP и любой другой класс исключений для своих собственных исключений.
Приведем пример для большей наглядности. Предположим, что вышеозначенный код лежит в файле class.phpException.php
<?php
require_once 'class.phpException.php';

class
calc {
    
// ...
    
static public function div($num1, $num2) {
        
$num1 = (float) $num1;
        
$num2 = (float) $num2;
        if (
$num2 == 0) {
            throw new
Exception('Деление на ноль');
        }
        return
$num1 / $num2;
    }
    
// ...
}

try {
    
intval($not_exists_var);
    echo
calc::div(1, 0);
    
fopen('non_exists_file');
} catch (
exception $e) {
    echo
$e;
}
?>

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


 
Комментариев нет. [Показать комментарии/форму]