В соответствии с php-документами метод PDO fetch () возвращает значение FALSE
как при отсутствии записей, так и при сбое (например, когда что-то пойдет не так в отношении доступа к базе данных).
Предположим, я установил систему отчетов об ошибках PHP для исключения исключений при сбое:
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
Мне нужен случай, ситуация, в которой метод fetch()
генерирует исключение. Зачем? Поскольку я хочу проверить, чтобы быть уверенным на 100%, что fetch()
выдает исключение при ошибке и не просто возвращает FALSE
при ошибке.
Если бы это было так, то я бы действительно рассмотрел FALSE
возвращенный fetch()
за того, что не нашел записей в таблице db.
Итак, мой вопрос будет: знаете ли вы способ моделирования ситуации сбоя для метода fetch()
?
Спасибо.
PS : Ответ на мой вопрос поможет мне найти ответ на мой другой вопрос: PHP PDO fetch возвращает FALSE, когда нет записей и ошибок
Изменить 1:
Я также подготовил пример, чтобы показать, как я обрабатываю исключения. Речь идет о простом sql-запросе, который выводит пользователя из таблицы users
:
<?php // Activate error reporting. error_reporting(E_ALL); ini_set('display_errors', 1); try { // Create a PDO instance as db connection to a MySQL db. $connection = new PDO( 'mysql:host=localhost;port=3306;dbname=mydb;charset=utf8' , 'myuser' , 'mypass' , array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => FALSE, PDO::ATTR_PERSISTENT => TRUE ) ); // Define the sql statement. $sql = 'SELECT * FROM users WHERE name = :name'; /* * Prepare and validate the sql statement. * * -------------------------------------------------------------------------------- * If the database server cannot successfully prepare the statement, PDO::prepare() * returns FALSE or emits PDOException (depending on error handling settings). * -------------------------------------------------------------------------------- */ $statement = $connection->prepare($sql); if (!$statement) { throw new UnexpectedValueException('The sql statement could not be prepared!'); } // Bind the input parameter to the prepared statement. $bound = $statement->bindValue(':name', 'Sarah', PDO::PARAM_STR); // Validate the binding of the input parameter. if (!$bound) { throw new UnexpectedValueException('An input parameter can not be bound!'); } /* * Execute the prepared statement. * * ------------------------------------------------------------------ * PDOStatement::execute returns TRUE on success or FALSE on failure. * ------------------------------------------------------------------ */ $executed = $statement->execute(); if (!$executed) { throw new UnexpectedValueException('The prepared statement can not be executed!'); } /* * Fetch and validate the result set. * * ========================================================= * Note: * ========================================================= * PDOStatement::fetch returns FALSE not only on failure, * but ALSO when no record is found! * * Instead, PDOStatement::fetchAll returns FALSE on failure, * but an empty array if no record is found. This is the * natural, desired behaviour. * ========================================================= */ $resultset = $statement->fetch(PDO::FETCH_ASSOC); if ($resultset === FALSE) { throw new UnexpectedValueException('Fetching data failed!'); } // Display the result set. var_dump($resultset); echo '<pre>' . print_r($resultset, TRUE) . '</pre>'; // Close connection. $connection = NULL; } catch (PDOException $exc) { echo '<pre>' . print_r($exc, TRUE) . '</pre>'; exit(); } catch (Exception $exc) { echo '<pre>' . print_r($exc, TRUE) . '</pre>'; exit(); }
-<?php // Activate error reporting. error_reporting(E_ALL); ini_set('display_errors', 1); try { // Create a PDO instance as db connection to a MySQL db. $connection = new PDO( 'mysql:host=localhost;port=3306;dbname=mydb;charset=utf8' , 'myuser' , 'mypass' , array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => FALSE, PDO::ATTR_PERSISTENT => TRUE ) ); // Define the sql statement. $sql = 'SELECT * FROM users WHERE name = :name'; /* * Prepare and validate the sql statement. * * -------------------------------------------------------------------------------- * If the database server cannot successfully prepare the statement, PDO::prepare() * returns FALSE or emits PDOException (depending on error handling settings). * -------------------------------------------------------------------------------- */ $statement = $connection->prepare($sql); if (!$statement) { throw new UnexpectedValueException('The sql statement could not be prepared!'); } // Bind the input parameter to the prepared statement. $bound = $statement->bindValue(':name', 'Sarah', PDO::PARAM_STR); // Validate the binding of the input parameter. if (!$bound) { throw new UnexpectedValueException('An input parameter can not be bound!'); } /* * Execute the prepared statement. * * ------------------------------------------------------------------ * PDOStatement::execute returns TRUE on success or FALSE on failure. * ------------------------------------------------------------------ */ $executed = $statement->execute(); if (!$executed) { throw new UnexpectedValueException('The prepared statement can not be executed!'); } /* * Fetch and validate the result set. * * ========================================================= * Note: * ========================================================= * PDOStatement::fetch returns FALSE not only on failure, * but ALSO when no record is found! * * Instead, PDOStatement::fetchAll returns FALSE on failure, * but an empty array if no record is found. This is the * natural, desired behaviour. * ========================================================= */ $resultset = $statement->fetch(PDO::FETCH_ASSOC); if ($resultset === FALSE) { throw new UnexpectedValueException('Fetching data failed!'); } // Display the result set. var_dump($resultset); echo '<pre>' . print_r($resultset, TRUE) . '</pre>'; // Close connection. $connection = NULL; } catch (PDOException $exc) { echo '<pre>' . print_r($exc, TRUE) . '</pre>'; exit(); } catch (Exception $exc) { echo '<pre>' . print_r($exc, TRUE) . '</pre>'; exit(); }
Я использовал следующий синтаксис таблицы создания:
DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=124 DEFAULT CHARSET=utf8;
и следующие значения таблицы:
INSERT INTO `users` (`id`, `name`) VALUES (1,'Sarah'), (2,'John');
Итак, таблица выглядит так:
id name -------- 1 Sarah 2 John
Наконец, я нашел случай, который позволил мне проверить, если PDOStatement::fetch
действительно выдаст исключение при PDOStatement::fetch
.
В статье Воспользовавшись режимами выборки PDO, возникает такая ситуация. Он основан на использовании PDOStatement::fetchAll
с константой PDO::FETCH_KEY_PAIR
переданной как аргумент.
Итак, я сам проверил тест. Но вместо этого я использовал метод PDOStatement::fetch
. Для определения константа PDO::FETCH_KEY_PAIR
требует, чтобы таблица источника данных содержала только два столбца. В моем тесте я определил три столбца таблицы. PDOStatement::fetch
признал эту ситуацию неудачей и PDOStatement::fetch
исключение:
SQLSTATE [HY000]: общая ошибка: режим выборки PDO :: FETCH_KEY_PAIR требует, чтобы результирующий набор содержал не более 2 столбцов.
PDOStatement::fetch
возвращает FALSE
, если записи не найдены. PDOStatement::fetch
throws – действительно – исключение в случае сбоя. PDOStatement::fetchAll
возвращает пустой массив, если записи не найдены. PDO::FETCH_KEY_PAIR
не зарегистрирована на официальной странице PDOStatement :: fetch . Я хочу поблагодарить всех пользователей, которые пытались помочь мне найти ответ на мой вопрос. У вас есть моя оценка!
<?php // Activate error reporting. error_reporting(E_ALL); ini_set('display_errors', 1); try { // Create a PDO instance as db connection to a MySQL db. $connection = new PDO( 'mysql:host=localhost;port=3306;dbname=tests;charset=utf8' , 'root' , 'root' , array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => FALSE, PDO::ATTR_PERSISTENT => TRUE ) ); // Define the sql statement. $sql = 'SELECT * FROM users WHERE name = :name'; /* * Prepare the sql statement. * * -------------------------------------------------------------------------------- * If the database server cannot successfully prepare the statement, PDO::prepare() * returns FALSE or emits PDOException (depending on error handling settings). * -------------------------------------------------------------------------------- */ $statement = $connection->prepare($sql); // Validate the preparation of the sql statement. if (!$statement) { throw new UnexpectedValueException('The sql statement could not be prepared!'); } // Bind the input parameter to the prepared statement. $bound = $statement->bindValue(':name', 'Sarah', PDO::PARAM_STR); // Validate the binding of the input parameter. if (!$bound) { throw new UnexpectedValueException('An input parameter can not be bound!'); } /* * Execute the prepared statement. * * ------------------------------------------------------------------ * PDOStatement::execute returns TRUE on success or FALSE on failure. * ------------------------------------------------------------------ */ $executed = $statement->execute(); // Validate the execution of the prepared statement. if (!$executed) { throw new UnexpectedValueException('The prepared statement can not be executed!'); } // Fetch the result set. $resultset = $statement->fetch(PDO::FETCH_KEY_PAIR); // If no records found, define the result set as an empty array. if ($resultset === FALSE) { $resultset = []; } // Display the result set. var_dump($resultset); // Close connection. $connection = NULL; } catch (PDOException $exc) { echo '<pre>' . print_r($exc->getMessage(), TRUE) . '</pre>'; exit(); } catch (Exception $exc) { echo '<pre>' . print_r($exc->getMessage(), TRUE) . '</pre>'; exit(); }
DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL, `phone` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
INSERT INTO `users` (`id`, `name`, `phone`) VALUES (1,'Sarah','12345'), (2,'John','67890');
id name phone ----------------- 1 Sarah 12345 2 John 67890
С PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
выборка всегда будет генерировать исключение, если есть ошибка. Вы можете обращаться с теми, кто находится в вашем блоке catch, и не имеет значения, что он вернул. Поэтому, если вы не заметили исключение и вернули false, вы можете с уверенностью предположить, что это было из-за пустого набора. Это очень эффективный способ обработки ошибок PDO. Чтобы ответить на ваш вопрос, существует множество способов моделирования ошибки. Самый простой – это неправильный синтаксис запроса. Вы также можете попытаться связать параметр, которого нет, связать неправильное количество параметров и т. Д. Чем дольше вы используете эту схему, тем больше типов ошибок / исключений вы увидите. Это очень эффективно, поскольку каждая ошибка содержит подробное сообщение, которое поможет вам отладить его.
Если вам не нужен PDOStatement для цикла, вы можете использовать fetchAll в методе / функции, чтобы получить результат, который вы ищете. Просто сделайте fetchAll вместо выборки, проверьте значение false и при необходимости верните. В этом случае ваш запрос должен убедиться, что возвращается только одна строка.
Что-то вроде этого;
function fetch(\PDOStatement $pdo_stmt) { // use fetchAll as an empty result set is returned by PDO as false using fetch() $result = $pdo_stmt->fetchAll(\PDO::FETCH_ASSOC); if ($result !== false) { return !empty($result) ? $result[0] : []; } return false; } function fetchColumn(\PDOStatement $pdo_stmt) { // this will return false if no rows or not found... $result = $pdo_stmt->fetchColumn(); if (empty($pdo_stmt->errorInfo())) { return $result !== false ? $result : null; } return false; }
Обратите внимание, что fetchColumn имеет аналогичную проблему.
Вышеизложенное вернется;
Сканируя образец кода, вы можете реализовать его так;
$sql = 'SELECT * FROM users WHERE name = :name'; $stmt = $connection->prepare($sql); $stmt->bindValue(':name', 'Sarah'); $executed = $stmt->execute(); if (!$executed) { throw new UnexpectedValueException('The prepared statement can not be executed!'); } $result_set = fetch($stmt); if ($result_set === false) { throw new UnexpectedValueException('Fetching data failed!'); } // handle result set
Вы можете сделать больше, чтобы улучшить выше, но, надеюсь, вы получите эту идею.