Обработка ошибок mysqli

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

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

Ошибка соединения с базой данных

Сразу приведём пример обработки ошибки с соединением с базой данных:

<?php 
   $host = 'localhost'; // адрес сервера
   $db_name = 'database'; // имя базы данных
   $user = 'user'; // имя пользователя
   $password = 'password'; // пароль

   // создание подключения к базе   
      $connection = mysqli_connect($host, $user, $password, $db_name);

   // проверка правильности подключения
      if(!$connection){ // при соединении с базой данных возникла ошибка
         echo 'Ошибка соединения: ' . mysqli_connect_error() . '<br>';
         echo 'Код ошибки: ' . mysqli_connect_errno();
      }else{ // соединение было установлено успешно
         // здесь можно делать запрос к базе, 
         // потому что соединение успешно установлено
      }
?>

В этом примере можно заметить функцию mysqli_connect_error. Она выводит текстовое описание ошибки подключения (на английском языке). В отличии от неё функция mysqli_connect_errno выводит числовой код ошибки, к примеру «1045».

Ошибка запроса к базе

Теперь попробуем доработать пример из предыдущего параграфа и добавить запрос к базе данных. Не будем вдаваться в детали, что будет записано в тексте SQL запроса, просто предположим, что он может завершиться ошибкой. К примеру, из-за неожиданного отключения сервера с базой данных от сети. В примере добавим проверку на наличие ошибок при выполнении запроса:

<?php 
   $host = 'localhost'; // адрес сервера
   $db_name = 'database'; // имя базы данных
   $user = 'user'; // имя пользователя
   $password = 'password'; // пароль

   // создание подключения к базе   
      $connection = mysqli_connect($host, $user, $password, $db_name);

      if(!$connection){ // проверка правильности подключения
         echo 'Ошибка соединения: ' . mysqli_connect_error() . '<br>';
         echo 'Код ошибки: ' . mysqli_connect_errno();
      }else{ // подключение успешно установлено

         // текст SQL запроса, который будет передан базе
            $query = 'SELECT * FROM `USERS`';

         // выполняем запрос к базе данных
            $result = mysqli_query($connection, $query);

            if(!$result){ // запрос завершился ошибкой
               echo 'Ошибка запроса: ' . mysqli_error($connection) . '<br>';
               echo 'Код ошибки: ' . mysqli_errno($connection);
            }else{ // запрос успешно выполнился
               while($row = $result->fetch_assoc()){
                  // обрабатываем полученные данные
               }
            }
         // закрываем соединение с базой
            mysqli_close($connection);
      }
?>

В этом примере есть две функции, которые работают с ошибками базы. Функция mysqli_error возвращает описание ошибки запроса (на английском языке), а функция mysqli_errno возвращает числовой код ошибки, к примеру, «1193».

Обратите внимание, что все функции обработки ошибок в этой статье (mysqli_connect_error, mysqli_connect_errno, mysqli_error, mysqli_errno) возвращают информацию только о последней ошибке. Но ошибок может быть несколько.

Была ли статья полезной?

Была ли эта статья полезна?

Есть вопрос?

хостинг для сайтов

Закажите недорогой хостинг

Заказать

всего от 290 руб

Время на прочтение
10 мин

Количество просмотров 14K

image

Небольшой дисклеймер от переводчика: в РНР сложилась парадоксальная ситуация. Сам язык ушёл далеко вперёд, но изучают его по жутко устаревшим материалам. Собственно, постоянный кринж от кода на Тостере (как в вопросах, так и в ответах) и побудил к переводу данной статьи.

Кроме того, переводчик, также как и вы, считает, что PDO является более продвинутым API для работы с БД, чем mysqli. Но поскольку новички в подавляющем большинстве всё равно начинают с mysqli, то нужен хотя бы один нормальный материал по этому расширению. Не можешь противостоять — возглавь!

Не говоря уже о том, что в последнее время mysqli была сильно улучшена, и из совершенно неюзабельной превратилась в довольно сносную библиотеку, в которой из принципиальных отличий от PDO осталось разве что отсутствие именованных плейсхолдеров. Так что даже (особенно) если вы учили РНР 20 лет назад и всё знаете вдоль и поперёк, то всё равно сможете найти для себя что-то новое.

Соединение

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

Если ваш код является обычным процедурным РНР, то вот простой пример соединения:

$host     = '127.0.0.1';
$db       = 'test';
$user     = 'root';
$password = '';
$port     = 3306;
$charset  = 'utf8mb4';

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$db = new mysqli($host, $user, $password, $db, $port);
$db->set_charset($charset);
$db->options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1);

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

  • устанавливая правильный режим информирования об ошибках, мы избавляемся от непонятных ошибок типа mysqli_fetch_assoc() expects parameter... / Call to a member function bind_param()..., получая вместо них актуальные сообщения об ошибках от MySQL.
    • кроме того, режим исключений значительно упростит код, в котором не нужно будет писать проверку успешности выполнения каждой функции
  • установка правильного значения charset сразу избавит нас от целого класса проблем, связанных с кодировками, таких как крокозябры/вопросики вместо текста, пустой результат json_encode(), проблемы с сохранением эмодзи, и т.д.
  • с безопасностью не шутят, и сообщения об ошибках ни в коем случае нельзя вываливать прямо в браузер, как это принято во всех устаревших руководствах
  • возвращение из бд любых типов данных в виде строк выглядит в наше время атавизмом. И хотя подготовленные запросы уже возвращают данные типизованными, при использовании функции query() по умолчанию все типы данных возвращаются в виде строк. Чтобы это исправить, на помощь приходит настройка MYSQLI_OPT_INT_AND_FLOAT_NATIVE

Объектный и процедурный интерфейсы

Небольшое, но важное замечание: у mysqli есть одно уникальное свойство: каждая функция может быть вызвана как через объектный, так и через процедурный синтаксис. То есть любое действие может быть выполнено как через вызов функции, так и через обращение к методу объекта:

mysqli_query($mysqli, $query); // процедурный синтаксис
$mysqli->query($query); // объектный синтаксис

Единственное различие заключается в том, что для объектного синтаксиса мы берём параметр функции (например $mysqli), добавляем объектный оператор (->) и дальше пишем собственно имя метода, выкидывая избыточное «mysqli_». Отдельно отмечу, что вам не нужно знать ООП чтобы использовать объектный синтаксис: это просто другой способ вызвать ту же самую функцию.

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

Учитывая, что объектный синтаксис является более коротким, без постоянных повторений (ср. mysqli_stmt_get_result($stmt) и $stmt->get_result()) я настоятельно рекомендую именно его, так что именно объектный синтаксис будет использоваться в этом руководстве.

Выполнение запросов, в которых используются переменные. Подготовленные выражения

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

Почему надо использовать подготовленные запросы? По той простой причине, что если мы добавляем данные прямо в запрос, то они могут повредить его. При этом последствия варьируются от синтаксических ошибок до SQL инъекций. В отличие от печально известного «экранирования», которое работает только для строк, и которое легко можно забыть, использовать неправильно или посчитать ненужным, подготовленные запросы позволяют нам сформулировать простую, но стопроцентно безопасную инструкцию из трёх шагов:

  1. Подготавливаем запрос, добавляя знаки вопроса, ?, там где раньше была переменная
  2. Привязываем сами переменные к этому подготовленному выражению, указывая для каждой её тип
  3. Выполняем запрос

Вот простой пример запроса INSERT:

$stmt = $db->prepare("INSERT INTO users (email, password) VALUES (?,?)");
$stmt->bind_param("ss", $email, $password_hash);
$stmt->execute();

Как можно видеть, тут нет ничего сложного, те самые три шага, описанные выше.

Давайте рассмотрим подготовленный запрос подробнее, на примере запроса UPDATE:

$sql = "UPDATE users SET name=?, email=?, password=? WHERE id=?";
$stmt= $conn->prepare($sql);
$stmt->bind_param("sssi", $name, $email, $password, $id);
$stmt->execute();

Что здесь происходит?

$sql = "UPDATE users SET name=?, email=?, password=? WHERE id=?";

Как это было описано выше, сначала мы заменяем все переменные запросе на вопросительные знаки.

ВАЖНО: нельзя добавлять к знакам вопроса кавычки — вы добавляете плейсхолдеры, а не строки.

$stmt= $conn->prepare($sql);

Дальше мы подготавливаем запрос. Идея тут очень остроумная: для того чтобы исключить даже теоретическую возможность инъекции, сам запрос и данные для него едут на сервер по отдельности. Именно это мы здесь и видим: вызов prepare() отправляет на сервер сначала сам запрос, без данных. При этом создаётся специальная переменная $stmt, содержащая экземпляр класса mysqli_statement и дальше мы будем работать именно с ней.

$stmt->bind_param("sssi", $name, $email, $password, $id);

Дальше наши переменные должны быть привязаны к подготовленному выражению. Данный вызов состоит из двух частей: сначала идёт строка, в которой перечислены типы передаваемых переменных, а затем и сами переменные. В mysqli вам необходимо указывать тип для каждой переменной. Это делается с помощью одной буквы для каждой переменной. Количество букв должно всегда соответствовать количеству переменных. Возможных типов четыре:

  • i для целых чисел;
  • d для чисел с плавающей запятой;
  • s для строк;
  • b для блобов.

И теперь вы можете сказать, что запись «sssi» означает «у нас будет 3 переменных строкового типа и одна — целочисленного». Затем в функцию передаются все переменные, 4 штуки.

Совет: MySQL с радостью принимает любые данные как строки, так что не нужно сходить с ума пытаясь найти подходящий тип для каждой переменной. Можно просто использовать «s» для всех.

$stmt->execute();

И наконец, запрос выполняется. Переменные отправляются в БД и запрос выполняется.

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

Кстати, начиная с PHP 8.1, bind_param можно не использовать, отправив вместо этого все переменные в execute() в виде массива:

    $stmt = $db->prepare("INSERT INTO users (email, password) VALUES (?,?)");
    $stmt->execute([$email, $password_hash]);

В этом случае все переменные будут переданы как строки.

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

$sql = "DELETE FROM users WHERE id=?";
$stmt= $conn->prepare($sql);
$stmt->bind_param("s", $id);
$stmt->execute();

Выполнение запросов SELECT через подготовленные выражения

Точно так же как это было описано выше, мы подготавливаем запрос с вопросительными знаками, затем привязываем переменные и выполняем запрос. Однако для запросов SELECT нам понадобится один дополнительный шаг, получение переменной типа mysqli_result, которую можно будет использовать для выборки полученных строк:

$stmt = $db->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();

здесь функция get_result() возвращает экземпляр класса mysqli_result, который может использоваться для того чтобы получать возвращённые запросом строки в виде массивов или объектов.

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

Получение результатов запроса

Для получения результата запроса используется переменная, являющаяся экземпляром класса mysqli_result. Все функции, получающие данные из запроса, работают с этой переменной.

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

  • fetch_row() которая возвращает нумерованный массив;
  • fetch_assoc() которая возвращает ассоциативный массив;
  • fetch_object() которая возвращает объект.

Получение одной строки

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

$stmt = $db->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$username = $row['username'];

Получение нескольких строк в цикле

Что интересно, для получения нескольких строк служит та же самая функция, что и для получения одной строки. И всё благодаря одной маленькой детали: когда мы получаем строку с помощью одной из этих функций, внутренний указатель в возвращённом базой наборе строк перемещается на одну позицию, и поэтому следующее обращение к той же самой функции возвращает следующую строку — и так далее, пока строки не кончатся. Если строк больше нет, то функция fetch_* вернёт null, что позволит нам использовать цикл while для перебора всех строк:

$users = [];
$sql = "SELECT * FROM users ORDER BY id DESC LIMIT 0, 10";
$result = $db->query($sql);
while ($row = $result->fetch_assoc()) {
    $users[] = $row;
}

Здесь мы получили все полученные из БД строки в массив $users.

Подсказка: в mysqli есть удобная функция, которпя сразу возвращает все строки в виде массива: mysqli_fetch_all(). Вот только по какой-то причине она по умолчанию использует fetch_row() для наполнения массива, так что если вам нужны строки в виде ассоциативных массивов, то надо использовать параметр MYSQLI_ASSOC:

$sql = "SELECT * FROM categories";
$result = $db->query($sql);
$data = $result->fetch_all(MYSQLI_ASSOC);

Выполнение запросов без переменных

Если запрос целиком прописан прямо в коде, то есть в нем не используются никакие переменные PHP, то для выполнения запроса можно воспользоваться функцией query(), которая сразу возвращает объект класса mysqli_result, и в результате мы получим гораздо более простой код, например

$menu = $db->query("SELECT * FROM menu")->fetch_all(MYSQLI_ASSOC);
$count = $db->query("SELECT count(*) FROM users")->fetch_row()[0];

Простая функция-хелпер

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

function prepared_query($mysqli, $sql, $params, $types = "")
{
    $types = $types ?: str_repeat("s", count($params));
    $stmt = $mysqli->prepare($sql);
    $stmt->bind_param($types, ...$params);
    $stmt->execute();
    return $stmt;
}

Эта функция может быть добавлена в файл, в котором прописано соединение с БД и таким образом она будет доступна везде, где требуется mysqli, позволяя писать такие же аккуратные однострочники:

$sql = "SELECT * FROM menu WHERE section=?";
$menu = prepared_query($db, $sql, [$section])->get_result()->fetch_all(MYSQLI_ASSOC);
$sql = "SELECT * FROM users WHERE email=?";
$count = prepared_query($db, $sql, [$email])->get_result()->fetch_assoc();

Обработка ошибок

Обработка ошибок в запросах является очень важной темой, но её реализация вас удивит: в общем случае ошибки mysqli никак обрабатывать не нужно! Несмотря на то, что написано в бесчисленных примерах и руководствах по РНР, как правило, вы не должны писать никакого кода, обрабатывающего ошибки в запросах. Это звучит очень непривычно, но на самом деле именно так и надо делать. Если подумать, то для большинства ошибок надо только сообщить об их возникновении. И mysqli/PHP отлично справляются с этим сами, никакой помощи с вашей стороны им для этого не требуется. Следовательно, вы и не должны писать код, который проверяет результат выполнения запроса — в случае ошибки mysqli сообщит о ней автоматически, благодаря функции mysqli_report() о которой шла речь выше. Таким образом, все ошибки взаимодействия с БД будут обрабатываться единообразно, точно так же, как и все остальные ошибки РНР, что естественно является очень удобным с точки зрения обработки, которую можно выполнять в одном месте, а не разбрасывать по коду отдельно для каждого запроса. Подробнее почитать про правильный подход к обработке ошибок можно в статье Обработка ошибок в PHP (en).

В тех редких случаях, когда вам действительно надо обработать ошибку, то есть выполнить какое-то определённое действие в случае ошибки, а не просто сообщить о её появлении, запрос(ы) можно обернуть в try..catch.

Количество строк, которые вернул запрос SELECT

На самом деле нет ни одной причины использовать привычную функцию mysqli_num_rows(). Если подумать, то вы всегда можете использовать сами полученные данные для ответа на вопрос, были получены какие-то данные, или нет:

$user = $result->fetch_assoc();
if ($user) {
    // found!
}

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

Но конечно же надо помнить, что код ни в коем случае не должен запрашивать из БД больше строк, чем требуется на одной странице. Это относится как к запросам, которые получают определенные строки из БД для последующей обработки, так и — в особенности — к запросам, которые служат только для получения количества строк. Во втором случае вместо запроса самих строк необходимо запросить только их количество, запросом SELECT count(*) ....

Количество строк, затронутых при изменении данных

В отличие от предыдущего, количество строк, затронутых запросами INSERT, UPDATE и DELETE может быть довольно полезным. Что интересно, в mysqli есть не одна, целых две возможности получить эту цифру.

Одна из них — это привычное свойство affected_rows:

$db->query("DELETE FROM users");
echo $db->affected_rows();

Но есть еще одна, уникальная функция mysqli_info(), которая возвращает отдельно количество найденных и затронутых строк. Во всех других драйверах, включая PDO, вы можете получить либо то, либо другое, но не всё вместе. Хотя эта функция возвращает строку, её несложно распарсить, и получить аккуратный массив:

$db->query("update test set i=2");
$pattern = '~Rows matched: (?<matched>\d+)  Changed: (?<changed>\d+)  Warnings: (?<warnings>\d+)~';
preg_match($pattern, $db->info, $matches);
$info = array_filter($matches, "is_string", ARRAY_FILTER_USE_KEY);

mysqli_error

(PHP 5, PHP 7, PHP 8)

mysqli::$errormysqli_errorReturns a string description of the last error

Description

Object-oriented style

Procedural style

mysqli_error(mysqli $mysql): string

Return Values

A string that describes the error. An empty string if no error occurred.

Examples

Example #1 $mysqli->error example

Object-oriented style

<?php
$mysqli
= new mysqli("localhost", "my_user", "my_password", "world");/* check connection */
if ($mysqli->connect_errno) {
printf("Connect failed: %s\n", $mysqli->connect_error);
exit();
}

if (!

$mysqli->query("SET a=1")) {
printf("Error message: %s\n", $mysqli->error);
}
/* close connection */
$mysqli->close();
?>

Procedural style

<?php
$link
= mysqli_connect("localhost", "my_user", "my_password", "world");/* check connection */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

if (!

mysqli_query($link, "SET a=1")) {
printf("Error message: %s\n", mysqli_error($link));
}
/* close connection */
mysqli_close($link);
?>

The above examples will output:

Error message: Unknown system variable 'a'

See Also

  • mysqli_connect_errno() — Returns the error code from last connect call
  • mysqli_connect_error() — Returns a description of the last connection error
  • mysqli_errno() — Returns the error code for the most recent function call
  • mysqli_sqlstate() — Returns the SQLSTATE error from previous MySQL operation

information at saunderswebsolutions dot com

17 years ago

The mysqli_sql_exception class is not available to PHP 5.05

I used this code to catch errors
<?php
$query
= "SELECT XXname FROM customer_table ";
$res = $mysqli->query($query);

if (!

$res) {
printf("Errormessage: %s\n", $mysqli->error);
}
?>
The problem with this is that valid values for $res are: a mysqli_result object , true or false
This doesn't tell us that there has been an error with the sql used.
If you pass an update statement, false is a valid result if the update fails.

So, a better way is:
<?php
$query
= "SELECT XXname FROM customer_table ";
$res = $mysqli->query($query);

if (!

$mysqli->error) {
printf("Errormessage: %s\n", $mysqli->error);
}
?>

This would output something like:
Unexpected PHP error [mysqli::query() [<a href='function.query'>function.query</a>]: (42S22/1054): Unknown column 'XXname' in 'field list'] severity [E_WARNING] in [G:\database.php] line [249]

Very frustrating as I wanted to also catch the sql error and print out the stack trace.

A better way is:

<?php
mysqli_report
(MYSQLI_REPORT_OFF); //Turn off irritating default messages$mysqli = new mysqli("localhost", "my_user", "my_password", "world");$query = "SELECT XXname FROM customer_table ";
$res = $mysqli->query($query);

if (

$mysqli->error) {
try {
throw new
Exception("MySQL error $mysqli->error <br> Query:<br> $query", $msqli->errno);
} catch(
Exception $e ) {
echo
"Error No: ".$e->getCode(). " - ". $e->getMessage() . "<br >";
echo
nl2br($e->getTraceAsString());
}
}
//Do stuff with the result
?>
Prints out something like:
Error No: 1054
Unknown column 'XXname' in 'field list'
Query:
SELECT XXname FROM customer_table

#0 G:\\database.php(251): database->dbError('Unknown column ...', 1054, 'getQuery()', 'SELECT XXname F...')
#1 G:\data\WorkSites\1framework5\tests\dbtest.php(29): database->getString('SELECT XXname F...')
#2 c:\PHP\includes\simpletest\runner.php(58): testOfDB->testGetVal()
#3 c:\PHP\includes\simpletest\runner.php(96): SimpleInvoker->invoke('testGetVal')
#4 c:\PHP\includes\simpletest\runner.php(125): SimpleInvokerDecorator->invoke('testGetVal')
#5 c:\PHP\includes\simpletest\runner.php(183): SimpleErrorTrappingInvoker->invoke('testGetVal')
#6 c:\PHP\includes\simpletest\simple_test.php(90): SimpleRunner->run()
#7 c:\PHP\includes\simpletest\simple_test.php(498): SimpleTestCase->run(Object(HtmlReporter))
#8 c:\PHP\includes\simpletest\simple_test.php(500): GroupTest->run(Object(HtmlReporter))
#9 G:\all_tests.php(16): GroupTest->run(Object(HtmlReporter))

This will actually print out the error, a stack trace and the offending sql statement. Much more helpful when the sql statement is generated somewhere else in the code.

se (at) brainbits (dot) net

17 years ago

The decription "mysqli_error -- Returns a string description of the LAST error" is not exactly that what you get from mysqli_error. You get the error description from the last mysqli-function, not from the last mysql-error.

If you have the following situation

if (!$mysqli->query("SET a=1")) {
$mysqli->query("ROLLBACK;")
printf("Errormessage: %s\n", $mysqli->error);
}

you don't get an error-message, if the ROLLBACK-Query didn't failed, too. In order to get the right error-message you have to write:

if (!$mysqli->query("SET a=1")) {
printf("Errormessage: %s\n", $mysqli->error);
$mysqli->query("ROLLBACK;")
}

callforeach at gmail dot com

8 years ago

I had to set mysqli_report(MYSQLI_REPORT_ALL) at the begin of my script to be able to catch mysqli errors within the catch block of my php code.

Initially, I used the below code to throw and subsequent catch mysqli exceptions

<?php
try {
$mysqli = new mysqli('localhost','root','pwd','db');
if (
$mysqli->connect_errno)
throw new
Exception($mysqli->connect_error);

} catch (

Exception $e) {
echo
$e->getMessage();
}
I realized the exception was being thrown before the actual throw statement and hence the catch block was not being called.My current code looks like
mysqli_report
(MYSQLI_REPORT_ALL) ;
try {
$mysqli = new mysqli('localhost','root','pwd','db');
/* I don't need to throw the exception, it's being thrown automatically */} catch (Exception $e) {
echo
$e->getMessage();
}
This works fine and I'm able to trap all mysqli errors

asmith16 at littlesvr dot ca

9 years ago

Please note that the string returned may contain data initially provided by the user, possibly making your code vulnerable to XSS.

So even if you escape everything in your SQL query using mysqli_real_escape_string(), make sure that if you plan to display the string returned by mysqli_error() you run that string through htmlspecialchars().

As far as I can tell the two escape functions don't escape the same characters, which is why you need both (the first for SQL and the second for HTML/JS).

abderrahmanekaddour dot aissat at gmail dot com

1 year ago

<?php// The idea is the add formated errors information for developers to easier bugs detection.$myfile = fopen("database_log.log", "r");
$db = new mysqli("localhost", "root","root","data");
if(!
$db->query("SELECT")){
$timestamp = new DateTime();
$data_err = " {
\"title\": \" Select statement error \",
\"date_time\": "
.$timestamp->getTimestamp().",
\"error\":\" "
.$db->error." \"
} "
; // Do more information
fwrite($myfile, $data_err); // writing data
}
// In separate file do file read and format it for good visual.$db->close();
fclose($myfile);
?>

information at saunderswebsolutions dot com

17 years ago

Hi, you can also use the new mysqli_sql_exception to catch sql errors.
Example:
<?php
//set up $mysqli_instance here..
$Select = "SELECT xyz FROM mytable ";
try {
$res = $mysqli_instance->query($Select);
}catch (
mysqli_sql_exception $e) {
print
"Error Code <br>".$e->getCode();
print
"Error Message <br>".$e->getMessage();
print
"Strack Trace <br>".nl2br($e->getTraceAsString());
}
?>
Will print out something like
Error Code: 0
Error Message
No index used in query/prepared statement select sess_value from frame_sessions where sess_name = '5b85upjqkitjsostvs6g9rkul1'
Strack Trace:
#0 G:\classfiles\lib5\database.php(214): mysqli->query('select sess_val...')
#1 G:\classfiles\lib5\Session.php(52): database->getString('select sess_val...')
#2 [internal function]: sess_read('5b85upjqkitjsos...')
#3 G:\classfiles\includes.php(50): session_start()
#4 G:\tests\all_tests.php(4): include('G:\data\WorkSit...')
#5 {main}

Anonymous

3 years ago

mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
$this->connection = mysqli_connect($hostname,$username,$password, $dbname);
} catch (Exception $e) {
echo "Errno: " . mysqli_connect_errno() . PHP_EOL;
echo "Text error: " . mysqli_connect_error() . PHP_EOL;
exit;
}

Библиотека mysqli — своеобразный мостик между процедурным и объектно-ориентированным стилем программирования. Поэтому она поддерживает два подхода обработки ошибок: процедурный и через исключения.

Первый, процедурный подход — вы проверяете результат выполнения каждой функции и если возвращается false, получаете сообщение при помощи функции mysqli_error()

$db = mysqli_connect("localhost", "user", "...", "test");
if (mysqli_connect_errno()) {
  echo "Ошибка установки соединения" . mysqli_connect_error();
  exit();
}
$sql = "INSERT INTO users (login, email, password, datetime) VALUES (?, ?, ?, ?)";
$stmt = mysqli_prepare($db, $sql);
if(!$stmt) {
  echo "Ошибка подготовки запроса: " . mysqli_error($db);
  exit();
}
if(!mysqli_stmt_bind_param($stmt, 'ssss', $login, $email, $password, $datetime)) {
  echo "Ошибка связывания параметров: " . mysqli_error($db);
  exit();
}
if(!mysqli_stmt_execute($stmt)) {
  echo "Ошибка выполнения запроса: " . mysqli_error($db);
  exit();
}
mysqli_stmt_close($stmt);
mysqli_close($db);

Разумеется это не очень удобно, особенно, в объектно-ориентированном коде, поэтому mysqli позволяет переключиться в режим генерации исключений (их несколько типов, в режиме отладки наиболее удобно использовать MYSQLI_REPORT_ALL). Задать режим можно при помощи функции mysqli_report()

mysqli_report(MYSQLI_REPORT_ALL); 

try {
  $db = mysqli_connect("localhost", "user", "...", "test");

  $sql = "INSERT INTO users (login, email, password, datetime) VALUES (?, ?, ?, ?)";
  $stmt = mysqli_prepare($db, $sql);
  mysqli_stmt_bind_param($stmt, 'ssss', $login, $email, $password, $datetime);
  mysqli_stmt_execute($stmt);
  mysqli_stmt_close($stmt);
  mysqli_close($db);
} catch (Exception $e) {
  echo $e->getMessage();
} 

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

I started using OO-MySQLi after procedural MySQL and I have a problem.

In production environment my system displays all errors as a custom page.
MySQLi errors is an «error» too and I want catch them, but in documentation described only one way to do this:

if (!$mysqli->query("SET a=1")) {
    exit('An error occurred: ' . $mysqli->error);
}

(just for example).
This is a very bad way, because in my system I’m doing many things when error occurred, like logging, calling events, etc.

Of course, I can extend mysqli class, for example:

class myMysqli {
    public function __construct(/* ... */)
    {
        parent::__construct(/* ... */);
    }

    public function query(/* .. */)
    {
        parent::query(/* ... */);

        if($this->errno !== 0)
        {
            // An error occurred
        }
    }
}
$mysqli = new myMysqli(/* ... */);
$mysqli->query(/* ... */);

But this way I need to extend almost ALL methods where error can occur.
Moreover, MySQLi provides prepared statements (mysqli_stmt class) that has its own errors!

Can you know a better way to handle MySQLi errors?
Thank you in advance.


Added

About exceptions:
Do I understand correctly that with exceptions I need do something like this:

try {
    $mysqli->query(/* ... */);
} catch (Exception $e) {
    // An error occurred
}

But this is similar to

if(!$mysqli->query(/* ... */))
{
    // An error occured
} 

What a difference?

Понравилась статья? Поделить с друзьями:
  • Обработка ошибок discord py
  • Обозначение ошибок на принтере
  • Оборудование для чтения ошибок авто
  • Образец постановление о технической ошибке
  • Обработка ошибок curl php