Рейтинг@Mail.ru
 
Обработка исключений

Исключение, или исключительная ситуация - это такой поворот событий, когда скрипт начинает работать в нештатном режиме. Другими словами - сбой программы.
Вот рассмотрим простейший код. Смоделируем боевую ситуацию, когда вывод ответа интерпретатора о ошибках совсем не желателен. Их лучше не показывать ни в чем неповинному юзеру. Он не заслужил такого стресса. Да и хакерам совсем не обязательно знать о тонких местах скрипта. Вот такой код не покажет ничего, хотя файла dummy.txt не существует
1
2
3
4
5
6
7
8
<?php

// Убираем вывод ошибок
    
error_reporting(0);
// Пытаемся открыть несуществующий файл
    
$f fopen('dummy.txt''r');



Однако иногда нужно всеже как то отреагироать на это безобразие. Тут применяются разные способы (if... else, or die и так далее).
1
2
3
4
5
6
7
<?php

// Убираем вывод ошибок
    
error_reporting(0);
// Пытаемся открыть несуществующий файл
    
$f fopen('dummy.txt''r') or die('Не могу открыть файл');


Теперь ошибка будет выдана более пристойно и менее информативно.

Есть еще один способ решения такой проблемы - блоки try... catch. Это нововведение в PHP 5-й версии помогает разделить код на участки, и вывести все случившиеся недоразумения в одном месте.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

// Убираем вывод ошибок
    
error_reporting(0);

try 
{

  if(!
$f fopen('dummy.txt''r'))
      throw new 
Exception('Не могу открыть файл');       


catch (
Exception $e)
{  
    echo 
$e -> getMessage();
}


Давайте заглянем этой конструкции под юбку. Как это устроено.
В PHP 5-й версии предусмотрен встроенный класс Exception. Он реагирует на ошибки, помогая правильно их обработать.

Тут мы нужный участок поместили в блок try{...}. Как только в ходе выполнения появится ошибка, оператором throw в этот блок будет "выброшен" объект класса Exception, выполнение последующего кода прервется, а сам объект будет передан в блок catch.

Класс Exception содержит несколько стандартных методов и может принимать два аргумента. Первый - сообщение о ошибке, второй - её код (устанавливаем сами). Код ошибки может помочь разобраться, какие действия произвести в блоке catch. Вот примерно так можно этим пользоваться.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php

// Убираем вывод ошибок
    
error_reporting(0);

try 
{

  if(!
$f fopen('dummy.txt''r'))
      throw new 
Exception('Не могу открыть файл'69);       


catch (
Exception $e)
{  
    echo 
'Произошло фиаско ',     $e -> getMessage(), '<br>';
    echo 
'Код ошибки ',           $e -> getCode(), '<br>';
    echo 
'Файл c ошибкой ',       $e -> getFile(), '<br>';
    echo 
'Строка c ошибкой ',     $e -> getLine(), '<br>';
    echo 
'Строка cтека вызовов '$e -> getTraceAsString(), '<br>';
    
/* Массив стека вызовов */
    
print_r($e -> getTrace());
    
    if(
$e -> getCode() == 69)
        echo 
'Ошибка работы с файлами';
}


В блоке catch{...} можно не только выдавать сообщения, но и производить любые другие действия. Логировать ошибки, отправлять сообщение не E-mail администратора, перенаправлять юзера на специальную страницу ошибок и так далее.

Как и любой другой, класс Exception можно наследовать, расширяя существующий функционал. При этом можно использовать несколько блоков catch{...}, каждый для своего объекта. А еще можно вкладывать блоки друг в друга, передавая исключения по инстанции.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php

// Убираем вывод ошибок
    
error_reporting(0);

/*
* Сделаем наследника от встроенного класса
* в котором определим свои сообщения о математических
* ошибках. Его будем бросать там, где вычисления
*/
class Math_Exception extends Exception
{
    public 
$error;
    private 
$errors = array(
                             
'Деление на ноль',
                             
'Другая ошибка'
                           
);

    public function 
__construct($error)
    {
        
$this->error $this->errors[$error]; 
    }
    
    public function 
getError()
    {
        return 
$this->error
    }    

}

/*
* Начинаем танцы с бубном
*/
try 
{
// Тут теперь нет ошибки, потому что файл создастся (a+) 
    
if(!$f fopen('dummy.txt''a+'))
        throw new 
Exception('Не могу открыть файл dummy.txt');

// Тут ошибка. В объект новоиспеченного (см выше) класса исключения передадим её номер   
    
$num 10 0;       
    if(!
$num)      
        throw new 
Math_Exception(0);             


catch (
Math_Exception $e// Этот блок сработает
{  
    echo 
'Учите математику: ',    $e -> getError(), '<br>';
    echo 
'Ошибка в файле ',       $e -> getFile(), '<br>';
    echo 
'На строке ',            $e -> getLine(), '<br>';    
}
catch (
Exception $e// А этот нет
{  
    echo 
'Произошло фиаско ',     $e -> getMessage(), '<br>';
}

/////////////////////////////////////
/*   РЕЗУЛЬТАТ

Учите математику: Деление на ноль
Ошибка в файле Z:\home\example.den\www\index.php
На строке 42

*/


Вот примерно так.

По поводу того, когда и стоит ли вообще использовать исключения, постоянно идут религиозные войны. Одно определенно точно - не стоит ими увлекаться.
Местами может они и удобны, но в целом приложение обрастает мертвым кодом, так как они полезны в основном при отладке. Ну и на мой взгляд совершенно не придают читабельности или удобства. Готовый к бою код должен предполагать самый минимум возможности возникновения исключительных ситуаций, а значит все это будет лишним и ненужным.

Кроме того, нужно учитывать ресурсоемкость данной операции. Если скрипт работает ровно, без ошибок, то ничего страшного. Но если появится ошибка, то обработка исключения потребует ресурсов больше, чем тот же or die в 20 и более раз.

Лично я предпочитаю более легкую кавалерию, если не требуется каких то специфических условий:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php

// Убираем вывод ошибок
    
error_reporting(0);
// Пытаемся открыть несуществующий файл
    
$f fopen('dummy.txt''r') or except('Не могу открыть файл dummy.txt');   

// Функция обработки ошибок, что то типа блока catch    
    
function except($error)
    {
       
$trace debug_backtrace();
       die( 
'<pre>
            '
$error .'
             File:     '
$trace[0]['file'] .'
             Line:     '
$trace[0]['line'
          ); 
    }
    
/////////////////////////////////////////////////
/*       РЕЗУЛЬТАТ

            Не могу открыть файл dummy.txt
             File:     Z:\home\example\www\index.php
             Line:     6

*/


Синтаксис такой конструкции более прост, код неизбыточен, более читабелен, а основной функционал сохраняется

Собственно решать Вам, все как обычно.