Не слишком ли я много проверяю фунцкии на ошибки в скрипте?

Mich

Продвинутый новичёк
Подведем итог (конструктив, да):
то убедишься что я не хочу так сказать
ЗЫ. display_errors - опиум для народа
Я не буду писать конкретно
Ты хочешь узнать зачем или сказать что незачем?
не нужно это тебе
Налицо прогресс: отрицание - вопрос - утверждение.
 

demongloom

Новичок
если перехватывать все ошибки таким образом то точно екнуться можно. В принципе информационность ошибок в пхп вполне приемлема, можно конечно различные трейсеры прикрутить к выводу. Если тебе нужна перехватываемость ошибок, то лучше использовать исключения. А также написать скрипт который перехватывает ерроры (set_error_handler) и сбрасывает исключения, а ловец исключений (set_exception_handler) будет заниматься отображением инфы, трейс и т.д.
Единственное что исключение не нужно сбрасывать при E_NOTICE, E_STRICT.

Я лично такой метод применяю, весьма удобно.
 

Nitrat

Новичок
Я понял истину, но, к сожалению, не от Вас. Всем спасибо.
Можно закрывать топик.
 

FireNet

Новичок
Если не трудно, изложи ее здесь плиз. По правилам форума так положено.
 

denver

?>Скриптер
Истина проста: читайте Джорджа Шлосснеигла !!!
Nitrat
Я понимаю, дражащий, что ты, рано постигший истину, уже не станешь метать бисер перед свиньями (которые к слову старались тебе помочь, великий), но не затруднит ли тебя, о мудрый, скинуть ссылку на эту гениальную мысль по теме или хотя бы написать правильно имя твоего друга гения?
 

Nitrat

Новичок
Автор оригинала: denver
Nitrat
Я понимаю, дражащий, что ты, рано постигший истину, уже не станешь метать бисер перед свиньями (которые к слову старались тебе помочь, великий), но не затруднит ли тебя, о мудрый, скинуть ссылку на эту гениальную мысль по теме или хотя бы написать правильно имя твоего друга гения?
Мир и любовь Вам! Блаженны плачущие, ибо они утешатся, раб божий denver. За 4 рассвета до сего блаженного дня азъм есть приобрел книгу с божественным названием "Профессиональное программирование на РНР" блаженного автора Джорджа Шлосснейгла за 404 рубля (не спроста 404 =) ). И сказано там для грешников, страдающих во мраке незнаний: "Внедрение надежной проверки входных данных для функций является ключевым компонентом безопасного программирования." Сие изречение есть не только ответ на вопрос о количестве проверок.
Бог в помощь Вам.

А теперь по русски. По времени выполнения структура if(function() === false) от просто function() не отличается, замерял PEAR/Benchmark'ом при повторении в 1000 итераций. Так что set_error_handler, trigger_error и вперед.

PHP:
// отрывок кода (начало)

if (file_exists($file_name)) {
        if (is_readable($file_name)) {
            // Читаем данные из файла
            if (($data = file_get_contents($file_name)) === FALSE) {
                trigger_error ("Ошибка чтения файла", 
                               E_USER_ERROR);
                restore_error_handler();                                                  
                return FALSE;
            }
        }
        else {
            trigger_error ("Файл не доступен для чтения", E_USER_ERROR);
            restore_error_handler();                   
            return FALSE;            
        }
        if (is_writeable($file_name)) {
            // Открываем фаил для чтения и записи
            if (($file_handle = fopen($file_name, 'rb+')) === FALSE) {
                trigger_error ("Ошибка открытия файла для чтения и записи",
                               E_USER_ERROR);
                restore_error_handler();                                                  
                return FALSE;
            }
            // Проверяем корректность содержимого файла
            if (!preg_match($good_file_data, $data)) {
                // Выводим предупреждение в случае измененного содержимого
                trigger_error ("Данные в файле повреждены", E_USER_WARNING);

                // Урезаем весь файл до нулевой длины
                if (ftruncate($file_handle, 0) === FALSE) {
                    trigger_error ("Ошибка очистки файла", 
                                   E_USER_ERROR);
                    // Закрывам файл в случае ошибки
                    if (fclose($file_handle) === FALSE) {
                        trigger_error ("Ошибка закрытия файла", 
                                       E_USER_ERROR);
                        restore_error_handler();
                        return FALSE;               
                    }
                    restore_error_handler();                   
                    return FALSE;

// отрывок кода (конец)
:cool:

-~{}~ 30.09.06 00:50:

И еще надо у return скобки поставить и FALSE на false изменить.
PHP:
return(false);
:cool:
 

demongloom

Новичок
замерял PEAR/Benchmark'ом при повторении в 1000 итераций

1. производительность 1000 итераций это показатель для какого размера кода, большого или маленького, а главное является ли 1000 итераций достаточно большой нагрузкой? Все таки 0.1 или 0.2 миллисекунды все таки могут попадать в погрешность. Вот когда скажем 100000 итераций и разница скорости в двух разных вариантах кода равняется допустим 13 секундам....

2.
PHP:
if (file_exists($file_name)) {
        if (is_readable($file_name)) {
            // Читаем данные из файла
            if (($data = file_get_contents($file_name)) === FALSE) {
                trigger_error ("Ошибка чтения файла", 
                               E_USER_ERROR);
                restore_error_handler();                                                  
                return FALSE;
            }
        }
        else {
            trigger_error ("Файл не доступен для чтения", E_USER_ERROR);
            restore_error_handler();                   
            return FALSE;            
        }
        if (is_writeable($file_name)) {
            // Открываем фаил для чтения и записи
            if (($file_handle = fopen($file_name, 'rb+')) === FALSE) {
                trigger_error ("Ошибка открытия файла для чтения и записи",
                               E_USER_ERROR);
                restore_error_handler();                                                  
                return FALSE;
            }
            // Проверяем корректность содержимого файла
            if (!preg_match($good_file_data, $data)) {
                // Выводим предупреждение в случае измененного содержимого
                trigger_error ("Данные в файле повреждены", E_USER_WARNING);

                // Урезаем весь файл до нулевой длины
                if (ftruncate($file_handle, 0) === FALSE) {
                    trigger_error ("Ошибка очистки файла", 
                                   E_USER_ERROR);
                    // Закрывам файл в случае ошибки
                    if (fclose($file_handle) === FALSE) {
                        trigger_error ("Ошибка закрытия файла", 
                                       E_USER_ERROR);
                        restore_error_handler();
                        return FALSE;               
                    }
                    restore_error_handler();                   
                    return FALSE;
Конечно параноидальность удобна, но все же желательно предоставлять читабельный и более удобный для понимания код, т.к. весь его смысл тонет в нагромождении проверок.

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

PHP:
if( !file_exists($file_name) ) {
	throw new e_file_is_not_exists($file_name); }
if( !is_readable($file_name) ) {
	throw new e_file_is_not_readable($file_name); }

/*
Обычно пхп функции в случае возникновения серьезных ошибок не только 
возвращают FALSE, но и выводят сообщение об ошибке. Можно написать
через set_error_handler преобразователь ошибок в исключения, что позволяет
местами не писать дополнительные проверки, которые по сути дублируются.
Как пример: 
ПХП внутри: произошла ошибка при чтении, выводим ошибку, возвращаем "ошибка" ->
-> наш код: проверяем что вернули ошибку, выводим ошибку, возвращаем "ошибка".

В моем же случае, при преобразовании ошибок в исключения, данное дублирование ненужно.
ПХП внутри: произошла ошибка при чтении, выводим ошибку 
(-> попадаем на перехват, создаем исключение со всеми данными, сбрасываем его), 
возвращаем ошибка.
Наш код: исключение попадает в try catch блок, либо в случае отсутствия такового 
будет сбрасываться вверх по вызовам.
*/

// пример 1
// c проверкой
if(($data = file_get_contents($file_name)) === FALSE) {
	throw new e_file_error($file_name, "Ошибка понимаешь..."); }

// пример 2
// без проверки, надеямся на перехват свыше
// такой тип будет встречаться далее по тексту.
$data = file_get_contents($file_name);

// пример 3. 
// мой любимый, применяю когда нужно добавить много разносторонней информации об ошибке.
// мое исключение наследует arrayaccess, который 
// используется для реализации хранения приложений (attachments).
// позволяет не писать исключения, в конструкторе которых множество параметров, которые нужно заполнять.
try {
	$data = file_get_contents($file_name);
} catch( Exception $e ) {
	$e2 = new e_file_error($file_name); 
	$e2[] = $e;
	throw $e2;
}

if( !is_writeable($file_name) ) {
	throw new e_file_is_not_writeable($file_name); }

$file_handle = fopen($file_name, 'rb+');

if (!preg_match($good_file_data, $data)) {
	throw new e_file_data_is_corrupt($file_name); }

ftruncate($file_handle, 0);
fclose($file_handle);

Я вот только одного не понял. Для чего нужно везде restore_error_handler(); прописывать?
----
Разница между ошибками и исключениями в том, что исключения гораздо более удобный и функциональный инструмент:
1. Исключение содержит изначально в себе отладочную информацию, столь необходимую программисту: что, где и как, чем просто тупое null или false.
2. Исключение предоставляет способ для перехвата, который не конфликтует с возвращением значения функции. Сами гляньте сколько в пхп различных функций, где у каждой то один то другой метод возвращения ошибки, то false, то null то еще как. Да еще вдобавок нужно его проверять и перепроверять на каждом этапе (поскольку как еще иначе стандартную ошибку отловить?) что излишне загромождает код.

По поводу выведения ошибок, нотисов, варнингов и так далее обычным простым смертным пользователям. Думаю выводить не стоит, достаточно чтоб они отправлялись админу, но перед выкладываем когда на production, все же стоит протестировать и прогнать по всем пунктам, что бы убедиться что все работает нормально. Вообще с выводом ошибок примечательна история про Therac-25

# Аппарат Therac-25 имел много неполадок, которые, по-видимому, не причиняли вреда, и операторы научились игнорировать эти капризы. Это напоминает историю о мальчике, который кричал «волк!». Люди научились не обращать на него внимания. Частые отключения и остановки машины беспокоили операторов. Однако операторы никогда не видели никаких вредных последствий для пациентов из-за этих отключений, поэтому они научились игнорировать их. Нечто, что может иметь серьезные последствия, игнорировалось. Постоянные неполадки также демонстрировали внутреннюю нестабильность и небезопасность машины.

# Когда проявлялась неполадка, Therac-25 выдавал непонятные сообщения, которые не давали обслуживающему персоналу никаких ключей к причинам и следствиям системной ошибки. Конструкция системы не предусматривала адекватной обратной связи, и персонал не мог понять, что происходит. Информация проходила от оператора через программное обеспечение к оборудованию. Когда связь нарушалась, пользователь не имел способов узнать состояние оборудования, поскольку построение программ не позволяло опрашивать оборудование. Ошибка при вводе данных ясно показывает последствия недостатка обратной связи. Например, вместо того, чтобы просто позволить оператору включать пучок лучей после изменения параметров, программы могли быть сделаны так, чтобы облучение не могло начаться до тех пор, пока программа не опросит оборудование о его состоянии и не предоставит эту информацию оператору для проверки.
http://citforum.ru/programming/digest/scofdebug/

Далеко в лес ходить не надо. Какова информативность trigger_error ("Ошибка чтения файла", E_USER_ERROR); ?

Не сказано ни про какой файл, ни где и как произошло событие.

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

PHP:
User Error:
Exception #EFFE73DC9: e_dpf_import_is_fail {
Message: {
Import call 'config:ini_file' is fail.
}

Attachment #0: {
Exception #EF2351921: e_dpf_import_depend_is_unresolved {
Message: {
Depend (pre-) 'config:ini' is unresolved.
}

Attachment #0: {
Exception #EFFE73DC9: e_dpf_import_is_fail {
Message: {
Import call 'config:ini' is fail.
}

Attachment #0: {
Exception #EF2351921: e_dpf_import_depend_is_unresolved {
Message: {
Depend (pre-) 'config:config' is unresolved.
}

Attachment #0: {
Exception #EFFE73DC9: e_dpf_import_is_fail {
Message: {
Import call 'config:config' is fail.
}

Attachment #0: {
Exception #EF2351921: e_dpf_import_depend_is_unresolved {
Message: {
Depend (pre-) 'config:abstract_element_child' is unresolved.
}

Attachment #0: {
Exception #EFFE73DC9: e_dpf_import_is_fail {
Message: {
Import call 'config:abstract_element_child' is fail.
}

Attachment #0: {
Exception #EF0F596E8: e_file_is_not_exists {
Message: {
File 'D:\Develop\wwwroot\dpf\dpf/../packages\config\class.config_abstract_element_child`.php' is not exists.
}
 

Nitrat

Новичок
Тов. demongloom! Тов. Сталин и партия Вас нэ забудэт.

Вот когда скажем 100000 итераций и разница скорости в двух разных вариантах кода равняется допустим 13 секундам....
1. При 100000 итераций в этом коде все работает ок! =)


Скажем переписав вот так, не теряя функциональсти код выглядит гораздо удобочитабельней. Поменьше вложенных структур!
Далеко в лес ходить не надо. Какова информативность trigger_error ("Ошибка чтения файла", E_USER_ERROR); ?
2. Я пока не применял исключения при обработке ошибок, так как я написал простую функцию с выводом описания ошибки. Вот мой обработчик ошибок:

PHP:
function cnt_error_handler ($errtype, $errmsg, $filename, $linenum)
{
    switch ($errtype) {
    case E_USER_NOTICE:
        echo "<hr /><b>УВЕДОМЛЕНИЕ</b> в <b>".basename($filename)."</b>, ".
             "строка: <b>$linenum</b>: <i>$errmsg</i><hr />";
        break;
    case E_USER_WARNING:
        echo "<hr /><b>ПРЕДУПРЕЖДЕНИЕ</b> в <b>".basename($filename)."</b>, ".
             "строка: <b>$linenum</b>: <i>$errmsg</i><hr />";
        break;
    case E_USER_ERROR:
        echo "<hr /><b>ОШИБКА</b> в <b>".basename($filename)."</b>, ".
             "строка: <b>$linenum</b>: <i>$errmsg</i> <b>[Выполнение функции ".
             "прервано]</b><hr />";
        break;                
    default:
        echo "<hr /><b>НЕИЗВЕСТНАЯ ОШИБКА</b> в <b>".basename($filename).
             "</b>, строка: <b>$linenum</b>: <i>$errmsg</i><hr />";
        break;                   
    }
}

Я вот только одного не понял. Для чего нужно везде restore_error_handler(); прописывать?
3. Восстановить старый обработчик. Если в моей функции произошла ошибка, то работа скрипта ее вызвавшего не прекращается.

Разница между ошибками и исключениями в том, что исключения гораздо более удобный и функциональный инструмент:
1. Исключение содержит изначально в себе отладочную информацию, столь необходимую программисту: что, где и как, чем просто тупое null или false.
2. Исключение предоставляет способ для перехвата, который не конфликтует с возвращением значения функции. Сами гляньте сколько в пхп различных функций, где у каждой то один то другой метод возвращения ошибки, то false, то null то еще как. Да еще вдобавок нужно его проверять и перепроверять на каждом этапе (поскольку как еще иначе стандартную ошибку отловить?) что излишне загромождает код.
4. Зачем для простой фунцкии писать кучу классов исключений при ошибках, у меня просто счетчик посещений (пока тупой и не отлаженный).

Хорошая идея проверить скорость обработки исключений и просто error_handler'а.

Спс. :cool:
 

Андрейка

Senior pomidor developer
о как :)
PHP:
if ($data = file_get_contents($file_name)) {
  if ($file_handle = fopen($file_name, 'rb+')) {
     if (preg_match($good_file_data, $data)) { 
                if (ftruncate($file_handle, 0)) { 
  // do something
               }
   }
   fclose($file_handle);
  }
}
 

demongloom

Новичок
PHP:
function cnt_error_handler ($errtype, $errmsg, $filename, $linenum)
{
    switch ($errtype) {
    case E_USER_NOTICE:
        echo "<hr /><b>УВЕДОМЛЕНИЕ</b> в <b>".basename($filename)."</b>, ".
             "строка: <b>$linenum</b>: <i>$errmsg</i><hr />";
        break;
    case E_USER_WARNING:
        echo "<hr /><b>ПРЕДУПРЕЖДЕНИЕ</b> в <b>".basename($filename)."</b>, ".
             "строка: <b>$linenum</b>: <i>$errmsg</i><hr />";
        break;
    case E_USER_ERROR:
        echo "<hr /><b>ОШИБКА</b> в <b>".basename($filename)."</b>, ".
             "строка: <b>$linenum</b>: <i>$errmsg</i> <b>[Выполнение функции ".
             "прервано]</b><hr />";
        break;                
    default:
        echo "<hr /><b>НЕИЗВЕСТНАЯ ОШИБКА</b> в <b>".basename($filename).
             "</b>, строка: <b>$linenum</b>: <i>$errmsg</i><hr />";
        break;                   
    }
}
Подожди, так в чем функциональность такого обработчика, кроме как вывод данных в чуть ином виде и на русском языке?
Конечно хочется информативности, но не сонеты же должна писать ошибка.

Зачем для простой фунцкии писать кучу классов исключений при ошибках, у меня просто счетчик посещений (пока тупой и не отлаженный).
Ну для простого счетчика может и не имеет смысла, но у тебя недостаток не в проверках, а в стиле их написания, поскольку проверки загромождают код. Да и структура сложновата. Можно и так вот лаконично написать:

PHP:
if( !file_exists($file_name) ) {
    trigger_error("файл не найден $file_name", e_user_error); 
    restore_error_handler();
    return false;
}

if( !is_readable($file_name) ) {
    trigger_error("файл не читабелен $file_name", e_user_error); 
    restore_error_handler();
    return false;
}
Но в целом, заранее написанная куча классов и функций называется фреймворком, т.е. инструментарий разработчика, написанный специально для того чтоб не писать велосипед дважды, а просто брать необходимое и подключать.

--------------------------
Мой обработчик:

http://www.phpclub.ru/paste/1470
 

demongloom

Новичок
getTrace есть в exception

final function getMessage(); // message of exception
final function getCode(); // code of exception
final function getFile(); // source filename
final function getTrace(); // an array of the backtrace()
final function getTraceAsString(); // formated string of trace
 
Сверху