Отчеты об ошибках подготовленных отчетов MySQLi

Я пытаюсь разобраться с MySQli, и меня смущают сообщения об ошибках. Я использую возвращаемое значение инструкции MySQLi 'prepare' для обнаружения ошибок при выполнении SQL, например:

$stmt_test =  $mysqliDatabaseConnection->stmt_init();
if($stmt_test->prepare("INSERT INTO testtable VALUES (23,44,56)"))
{
 $stmt_test->execute();
 $stmt_test->close();
}
else echo("Statement failed: ". $stmt_test->error . "<br>");

Но является ли возвращаемое значение оператора подготовки только обнаружением ошибки при подготовке оператора SQL и не обнаружением ошибок выполнения? Если это так, я должен изменить свою строку выполнения, чтобы пометить ошибки, как это:

if($stmt_test->execute()) $errorflag=true;

И затем, чтобы быть в безопасности, я также должен сделать следующее после выполнения оператора:

if($stmt_test->errno) {$errorflag=true;}

... Или я был в порядке с самого начала, и возвращаемое значение в операторе подготовки MySQLi фиксирует все ошибки, связанные с полным выполнением запроса, который он определяет?

Спасибо С


person Columbo    schedule 31.03.2010    source источник
comment
Почему вы используете prepare/execute() вместо query(), когда в строке запроса нет переменной части? Или это просто упрощенный пример?   -  person VolkerK    schedule 31.03.2010
comment
Да, прости. Это было упрощено, чтобы показать, как мне было трудно понять, где получить окончательный отчет об ошибках из подготовленного отчета.   -  person Columbo    schedule 31.03.2010


Ответы (3)


Каждый метод mysqli может дать сбой. Вы должны протестировать каждое возвращаемое значение. В случае неудачи подумайте, имеет ли смысл продолжать работу с объектом, находящимся не в том состоянии, в котором вы ожидаете. (Возможно, не в безопасном состоянии, но я думаю, что это не проблема.)

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

$stmt = $mysqli->prepare("INSERT INTO testtable VALUES (?,?,?)");
// prepare() can fail because of syntax errors, missing privileges, ....
if ( false===$stmt ) {
  // and since all the following operations need a valid/ready statement object
  // it doesn't make sense to go on
  // you might want to use a more sophisticated mechanism than die()
  // but's it's only an example
  die('prepare() failed: ' . htmlspecialchars($mysqli->error));
}

$rc = $stmt->bind_param('iii', $x, $y, $z);
// bind_param() can fail because the number of parameter doesn't match the placeholders in the statement
// or there's a type conflict(?), or ....
if ( false===$rc ) {
  // again execute() is useless if you can't bind the parameters. Bail out somehow.
  die('bind_param() failed: ' . htmlspecialchars($stmt->error));
}

$rc = $stmt->execute();
// execute() can fail for various reasons. And may it be as stupid as someone tripping over the network cable
// 2006 "server gone away" is always an option
if ( false===$rc ) {
  die('execute() failed: ' . htmlspecialchars($stmt->error));
}

$stmt->close();

Всего несколько заметок шесть лет спустя...

Расширение mysqli прекрасно способно сообщать об операциях, которые приводят к коду ошибки (mysqli), отличному от 0, через исключения, см. mysqli_driver::$report_mode.
die() действительно, очень грубо, и я бы больше не стал использовать его даже для таких примеров, как этот.
Так что, пожалуйста, только уберите тот факт, что каждый и каждый (mysql) операция может завершиться неудачно по ряду причин; даже если одна и та же вещь тысячу раз удавалась раньше....

person VolkerK    schedule 31.03.2010
comment
Спасибо. Я только что сделал несколько тестов и вижу, что вы говорите. Если я создам запрос, который вставляет повторяющееся значение первичного ключа в таблицу, то проверка только подготовки не покажет, что вставка не удалась. С другой стороны, если я не проверю подготовку, то выполнение никогда не произойдет (я получу некоторые предупреждения, ЕСЛИ предупреждения включены). Так что я вижу, что ты прав. Спасибо. - person Columbo; 31.03.2010
comment
О да, ограничения — прекрасный пример. Если возможно, я бы дал вопросу еще один +1 только за это ;-) - person VolkerK; 31.03.2010
comment
если bind_param() завершается ошибкой из-за того, что количество элементов в строке определения типа не соответствует количеству переменных связывания. это вызывает ошибку E_WARNING. и вы не найдете эту ошибку в свойстве $stmt-›error - person Accountant م; 16.01.2017

Полнота

Вам нужно проверить как $mysqli, так и $statement. Если они ложны, вам нужно вывести $mysqli->error или $statement->error соответственно.

Эффективность

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

Пример использования 1. Простой сценарий

# This is in a simple command line script
$mysqli = new mysqli('localhost', 'buzUser', 'buzPassword');
$q = "UPDATE foo SET bar=1";
($statement = $mysqli->prepare($q)) or trigger_error($mysqli->error, E_USER_ERROR);
$statement->execute() or trigger_error($statement->error, E_USER_ERROR);

Пример использования 2. Приложение

# This is part of an application
class FuzDatabaseException extends Exception {
}

class Foo {
  public $mysqli;
  public function __construct(mysqli $mysqli) {
    $this->mysqli = $mysqli;
  }
  public function updateBar() {
    $q = "UPDATE foo SET bar=1";
    $statement = $this->mysqli->prepare($q);
    if (!$statement) {
      throw new FuzDatabaseException($mysqli->error);
    }

    if (!$statement->execute()) {
      throw new FuzDatabaseException($statement->error);
    }
  }
}

$foo = new Foo(new mysqli('localhost','buzUser','buzPassword'));
try {
  $foo->updateBar();
} catch (FuzDatabaseException $e)
  $msg = $e->getMessage();
  // Now send warning emails, write log
}
person cmc    schedule 13.12.2013
comment
1. die() никогда не следует использовать. 2. mysqli может сама генерировать исключения, см. mysqli_report() 2. Написание кода для отправки предупреждений по электронной почте, записи журнала после каждой функции, управляемой базой данных, ужасно избыточно - person Your Common Sense; 13.12.2013
comment
В любом случае вы никогда не должны использовать API mysqli как есть, а только в какой-то библиотеке более высокого уровня. - person Your Common Sense; 13.12.2013
comment
Опять бред. Вы делаете именно это, когда вам нужна производительность, небольшой объем памяти и несколько зависимостей. - person cmc; 13.12.2013
comment
Упоминание как $mysqli-›error, так и $statement-›error просто спасло мой день, спасибо^^ - person Fernando Silva; 03.01.2014
comment
@FernandoSilva Рад слышать, что Фернандо, и ты только что сделал меня! - person cmc; 07.01.2014
comment
@YourCommonSense ну, указатель mysqli_report был интересен. Тем не менее, он не хочет писать код, который полагается на глобальную настройку конфигурации для сообщения об ошибках. - person cmc; 09.01.2014
comment
@YourCommonSense Я скажу вам, что trigger_error - лучшее решение, потому что оно дает вам номер строки и код выхода (отредактированный ответ), но я утверждаю, что это никогда не является ненужным ограничением. - person cmc; 09.01.2014
comment
@YourCommonSense Для приложения, да, вероятно, было бы хорошо уменьшить шаблон, но как лучше всего это сделать, зависит от приложения. Пример, безусловно, является хорошей отправной точкой. - person cmc; 09.01.2014

Не уверен, что это отвечает на ваш вопрос или нет. Извините, если нет

Чтобы получить сообщение об ошибке из базы данных mysql о вашем запросе, вам нужно использовать объект подключения в качестве фокуса.

so:

echo $mysqliDatabaseConnection->error

будет повторять ошибку, отправленную из mysql по вашему запросу.

надеюсь, это поможет

person andyface    schedule 31.03.2010
comment
Спасибо. Поэтому, если я запрошу ошибку для фактического объекта соединения, он выдаст мне последнюю ошибку для этого соединения. Поскольку выполнение будет успешным только в том случае, если все предыдущие шаги были успешными, это скажет мне, все ли прошло хорошо. Я полагаю, что тот же результат можно было бы получить, просто проверив ошибку для команды выполнения, как это одобрено ниже полковником Шрапнелем. Правильно ли я считаю, что проверка флага успеха/неудачи оператора подготовки не имеет реальной цели? - person Columbo; 31.03.2010
comment
Я думаю, что у вас уже есть ответ выше, но вы все равно ответите из вежливости. По сути, подготовка может завершиться ошибкой, как сказал VolekrK, но она не вернет ошибку mysql. Поэтому вам нужно выяснить, почему подготовка не удалась, получив ошибку подключения mysql, которая даст вам указание на то, где не удалось выполнить запрос в вашем операторе подготовки. Я не уверен в ошибке выполнения команды. - person andyface; 06.04.2010