webew
Войти » Регистрация
 
PHP

Локализация ошибок в PHP-коде

30 октября 2008, 14:41
Автор: 1234ru
Проблему опишу сразу на примере.

При отправке SQL-запроса с помощью функции mysql_query() для своего же блага следует организовывать обработку ошибок, т.е. для каждого запроса нужно писать нечто вида

mysql_query() or die(mysql_error()); // или что-то подобное


Каждый раз это писать неудобно, поэтому можно сделать маленькую функцию-"обертку":

function mysql_q($sql) {
    return mysql_query($sql) or die(mysql_error());
}


Всё бы хорошо, да только вот при использовании такой функции исезает возможность понять, в каком конкретно месте программы был отправлен ошибочный SQL-запрос. Ошибка всегда будет относиться к строке кода, находящейся внутри объявления функции mysql_q() - той, где она непосредственно происходит, т.е. всегда в одном и том же месте:

<?php

$wrong_sql = 'SQL-запрос с ошибкой';
$result = mysql_q($wrong_sql); // ошибка будет относиться НЕ к этой строке кода, хотя интересна именно она

function mysql_q($sql) {
    return mysql_query($sql) or die(mysql_error()); // вот к какой строке будет относиться ошибка
}

?>


Можно ли как-нибудь вытащить номер строки "из-под" функции mysql_q(), т.е узнать, из какого места этой функции был передан неправильный параметр?
(может быть, это какие-нибудь классы позволяют?)
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
NO USERPIC

rgbeast

Можно только обойти добавлением аргументов к функции

function mysql_q($sql, $LINE=0, $FILE='') {
...
}


и вызывать ее mysql_q($sql, __LINE__, __FILE__);
31.10.2008, 01:02
Ответить
NO USERPIC

alextop

Можно также обрабатывать ошибки с помощью исключений и уникальных сообщений.
Т.е.
throw new Exception("Error in ".__FILE__.", line ".__LINE__, [code])

А потом отлавливать место либо по сообщению или коду, либо (если сообщения и коды не используются) по debug_backtrace() (как вариант debug_print_backtrace()). Но дебаг больше для логирования и отладки годится.
31.10.2008, 02:38
Ответить

1234ru

alextop, skyboy, спасибо. debug_backtrace() - то что нужно.

Еще такой вопрос: debug_backtrace() возвращает массив вида

Array
(
    [0] => Array
        (
            [file] =>
            [line] =>
            [function] => mysql_q
          ...
        )
)

Почему формируется именно вложенный массив, а не обычный? (в котором сразу line, file и т.д.) В каком случае будет не только первый элемент (с индексом 0), но и последующие?

Вопрос, видимо, звучит непонятно, но мне это важно, т.к. хочется знать, можно ли с уверенностью писать что-то типа
function mysql_q($sql) {
    $debug = debug_backtrace();
    return mysql_query($sql) or die('<b>Error at line '.$debug[0]['line'].'</b>'.mysql_error());
}

Т.е. не собьется ли нумерация внутри возращаемого debug_backtrace() массива (в противном случае номер строки может относиться к чему-нибудь другому, а не к той строке, где передавался аргумент; надеюсь, вы меня поняли).
То, что не убивает нас, делает нас инвалидами.
02.11.2008, 14:04
Ответить
NO USERPIC

rgbeast

Это именно трассировка вызовов функций. Пусть функция a() вызвала функцию b(), а последняя вызвала функцию c(). backtrace будет содержать полный набор.
02.11.2008, 14:27
Ответить

1234ru

Протестировал и выяснил, что массив нумеруется согласно уровням вложенности, начиная с конца.
Т.е. код

function a() {
    b();
}

function b() {
    c();
}

function c() {
    return print_r(debug_backtrace(), 1);
}

echo a();


, выведет такой backtrace():

Array (
    [0] => массив для функции c
    [1] => массив для функции b
    [2] => массив для функции a
)


В то же время, код

function d($x) {
    return $x;
}

function e() {
    return print_r(debug_backtrace(), 1);
}

echo d(e());


выведет лишь массив для функции e(), но не для e() и d().

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


Интересно, кстати, много ли ресурсов тратится на трассировку. Т.е. имеет ли смысл вместо

function mysql_q($sql) {
    $debug = debug_backtrace();
    return mysql_query($sql) or die('<b>Error at line '.$debug[0]['line'].'</b>'.mysql_error());
}


делать

function mysql_q($sql) {
    if ($result = mysql_query($sql)) {
        return $result;
    }
    else {
        $debug = debug_backtrace();
        $message = '<b>Error at line '.$debug[0]['line'].'</b>'.mysql_error();
        die($message);
    }
}


?
То, что не убивает нас, делает нас инвалидами.
12.11.2008, 04:44
Ответить
NO USERPIC

rgbeast

Количесто ресурсов, затрачиваемых на трассировку не имеет значения, так как трассировка происходит только в случае ошибки. Стек вызова функций, PHP хранит в любом случае.
12.11.2008, 04:54
Ответить

1234ru

Цитата:
трассировка происходит только в случае ошибки

Почему только в случае ошибки? Код

<?php
function x() { return print_r(debug_backtrace(), 1); }
echo x();
?>

выведет непустой массив.
Что-то я не понял...
То, что не убивает нас, делает нас инвалидами.
12.11.2008, 22:06
Ответить
NO USERPIC

alextop

Потому что она вызывается по условию задачи только в случае ошибки.
Изначально задача как звучала?
Цитата:
При отправке SQL-запроса с помощью функции mysql_query() для своего же блага следует организовывать обработку ошибок

Вот и обрабатывается именно ситуация ошибки в БД.
Можно и просто так её вызывать, но зачем?...
12.11.2008, 22:28
Ответить

1234ru

Я имел в виду такой вариант:

function mysql_q($sql) {
    $debug = debug_backtrace();
    return mysql_query($sql) or die('<b>Error at line '.$debug[0]['line'].'</b>'.mysql_error());
}

Тут для компактности кода сделано так, что она как раз вызывается в любом случае. я в своем предыдущем сообщении и хотел узнать, имеет ли смысл избавляться от этого через if, чтобы зря не вызывать, или же компьютер это не почувствует.
То, что не убивает нас, делает нас инвалидами.
12.11.2008, 22:58
Ответить
NO USERPIC

alextop

Сорри, недопонял. Конечно нужно вызывать только при ошибке. Нечего зря гонять движок.
Каждый скрипт надо мысленно себе представлять в огромном внешнем цикле, который выполняется бесконечное число раз (вызовы страничек от пользователей). И поэтому нужно ставить целью минимизировать время работы такого цикла, т.е. убирать все лишнее, не делать ненужных телодвижений, оптимизировать код и проч. Компактность кода это хорошо, но первично должна быть эффективность работы скрипта. Если, конечно, ты претендуешь на какой-то реальный траффик и светлое будущее :)
13.11.2008, 00:09
Ответить

1234ru

В целом, согласен. Я - за производительный код.

Разницу в производительности можно получить, например, тестированием:

<?php

function a() { return print_r(debug_backtrace(), 1); }
function b() { ; }

$n = 100000;

$start_with = microtime(1); // PHP 5
for ($i = 1; $i < $n; $i++) { a(); }
echo $with_backtrace = microtime(1) - $start_with; // для 100 000 итераций ~1.53 сек  

echo "<br/>";

$start_without = microtime(1);
for ($i = 1; $i < $n; $i++) { b(); }
echo $without_backtrace = microtime(1) - $start_without; // ~0.09 сек - более чем на порядок меньше

?>

Хотя, есть мнение, что преждевременная оптимизация - корень всех зол. На практике это означает, что временные затраты на разбор более эффективного, но из-за этого более сложного кода могут не окупиться выигрышем в производительности (например, для нескольких итераций, которые реально будут происходить при каждом выполнении прогрммы, будет на уровне десятков микросекунд - это вряд ли кто-то почувствует).
То, что не убивает нас, делает нас инвалидами.
13.11.2008, 02:23
Ответить
NO USERPIC

skyboy

функция называется debug_backtrace, возвращает массив, каждый элемент которого - очередной уровень вложенности в стеке вызовов. подэлементами являются и имя файла, и номер строки, и даже(si!) дамп переменных-аргументов функции(если внутри функции произошел очередной вызов) или список имен include'ных файлов.
Цитата:
Но дебаг больше для логирования и отладки годится.

Ну, вопрошающему, видимо, как раз для отладки и надо, правда?
[updated]
Сразу не заметил упоминание debug_backtrace. Сорри за дубль.
01.11.2008, 12:30
Ответить
Добавить комментарий
Отображение комментариев: Древовидное | Плоское
© 2008—2017 webew.ru, связаться: x собака webew.ru
Сайт использует Flede и соответствует стандартам WAI-WCAG 1.0 на уровне A.
Rambler's Top100