21 ошибка программиста PHP. Часть 2 - Слабая устойчивость к ошибкам

ОГЛАВЛЕНИЕ

 

12. Слабая устойчивость к ошибкам

В природе существует огромное количество скриптов абсолютно не справляющихся с пользовательскими ошибками. Своим появлением такие скрипты обязаны программистам, которые не удосуживаются правильно распланировать будущий проект и определить все места возможных ошибок. Причём этим следует заняться до того, как скрипт был написан. Недоработки подобного рода приводят к сбоям программы, что чревато не только получением некорректных результатов, но и падением системы!

Предусмотреть худшее

Любой скрипт может "свалиться" при наступлении каких-либо "критичных" условий. Чтобы свести такой риск к минимуму всегда нужно:

Проверка результатов вызова функций

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

В приведённом ниже примере на шестом витке цикла возникает ошибка "деление на ноль", поскольку $i наращивается на 1, а $j уменьшается на 1. На шестом проходе $i=$j=1.

<?php
mt_srand
((double)microtime() * 10000000);

function
do_math ($a, $b) {
    return ((
$a - $b) * 2) / mt_rand();
}

for (
$i = 5, $j = -5; $i > -5; $i--, $j++){
    print
$j / do_math ($i, $j) . "\n";
}
?>

Проверка результатов системных вызовов

При обращении к внешним файлам или процессам всегда проверяйте, всё ли работает корректно.

Блестящий тому пример - проверка ответа системы при вызове функции sql_connect(). Стоит проверить этот ответ и убедиться, что подключение к БД действительно имело место. Если этого не сделать, то все запросы к БД могут не состояться, а некоторые данные могут быть утеряны; вы же будете пребывать в счастливом неведении.

<?php
$conn
= @sql_connect ($host, $user, $pass);

if (!
$conn) {
    die (
sprintf ("Ошибка [%d]: %s", sql_errno (), sql_error ()));
}
?>

Установка уровня error_reporting в файле php.ini на E_ALL

Убедитесь, что PHP правильно сконфигурирован, то есть уровень error_reporting (отображение сообщений об ошибках) выставлено на наивысший уровень. При другой конфигурации, по крайней мере, на время отладки скриптов, многие ошибки типа "неверное регулярное выражение", "недопустимое значение" ускользнут от вашего внимания.

Обратимся ещё раз к примеру, приведённому в части "Проверка результатов вызова функций". Предположим, что error_reporting выставлен не на максимум, а, скажем, на E_ERROR.

Обратите внимание на то, как скрипт выполняет функцию do_math, но не сообщает об ошибке "деление на ноль", которая, однако, имела место (при $i=$j=0 вывода результата просто не было).

<?php
error_reporting
(E_ERROR);

mt_srand ((double)microtime() * 1000000);

function
do_math ($a, $b) {
    return ((
$a - $b) * 2) / mt_rand();
}

for (
$i = 5, $j = -5; $i > -5; $i--, $j++){
    print
$j / do_math ($i, $j) . "\n";
}
?>

Результат работы скрипта:

-5148.25
-5271
-323.75
-4931
-7713.5

-4702.5
-488.5
-928.5
-1394.75

Свои обработчики ошибок

Как правило, PHP выдаёт сообщения об ошибках непосредственно в браузер и не позволяет разработчику подавить или перехватить их. Однако в PHP4 у вас появилась возможность перехвата таких сообщений с помощью функции set_error_handler().

Функция set_error_handler() применяется для записи ошибок вашего скрипта. Теперь вы можете перехватывать все ошибки и программировать собственные обработчики - warning'и пользователей больше не побеспокоят.

В следующем примере set_error_handler() назначает обработчиком по умолчанию функцию error_handler(). В случае возникновения ошибки вызывается error_handler(), и встроенная функция error_log() регистрирует сбой в файле лога error_file.

Если происходит ошибка класса E_ERROR, работа скрипта прекращается и выводится сообщение об ошибке.

<?php

// void error_handler(string type, string message, string file, int line)
// Индивидуальный обработчик ошибок, определён функцией
// set_error_handler()

function error_handler ($type, $message, $file = __FILE__, $line = __LINE__) {
    
error_log("$message, $file, $line", 3, 'error_file');
    if (
$type & E_ERROR) {
        print
'Произошла ошибка, зарегистирована.';
        exit;
    }
}

set_error_handler('error_handler');
?>