模拟 PDO 获取失败的情况
Simulate a PDO fetch failure situation
符合 php 文档,PDO 方法 fetch() return 值 FALSE
两者都没有找到记录 AND 失败(例如,当数据库访问出现问题时)。
假设,我将 PHP 错误报告系统设置为在失败时抛出异常:
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
我需要一个案例,一个fetch()
方法会抛出异常的情况。为什么?因为我想检查,100% 确定 fetch()
在失败时抛出异常,而不仅仅是 return FALSE
在失败时抛出异常。
如果是这样的话,那么我确实会考虑由 fetch()
编辑的 FALSE
return,因为在数据库中找不到任何记录 table.
所以,我的问题是:您知道模拟 fetch()
方法失败情况的方法吗?
谢谢。
P.S.:我的问题的答案将帮助我找到另一个问题的答案:
编辑 1:
我还准备了一个例子,来展示我是如何处理异常的。这是关于一个简单的 sql 查询,从 users
table:
中获取用户
<?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();
}
我使用了以下创建 table 语法:
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;
和以下 table 值:
INSERT INTO `users` (`id`, `name`)
VALUES
(1,'Sarah'),
(2,'John');
因此,table 看起来像这样:
id name
--------
1 Sarah
2 John
除非循环需要 PDOStatement,否则可以在 method/function 中使用 fetchAll 来获得所需的结果。只需执行 fetchAll 而不是 fetch,检查是否为 false,并根据需要检查 return。在这种情况下,您的查询应确保只有 1 行是 returned.
像这样;
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 也有类似的问题。
以上会return;
- 查询失败时为 false
- 如果未找到任何行,则获取一个空数组
- 如果找到合适的结果集进行提取
- 如果找不到,则为 fetchColumn 为 null
- 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
您可以做更多的事情来改进上述内容,但希望您能理解。
用PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
fetch如果有错误总是会抛出异常。您可以在 catch 块中处理它们,而不管它返回什么。因此,如果您没有捕获异常并且它返回 false,您可以放心地假设它是由于一个空集造成的。这是处理 PDO 错误的一种非常有效的方法。要回答您的问题,有很多方法可以模拟错误。最基本的是不正确的查询语法。您还可以尝试绑定一个不存在的参数,绑定错误数量的参数等。您使用此方案的时间越长,您看到的 errors/exceptions 类型就会越多。它非常有效,因为每个错误都包含一条详细消息以帮助您调试它。
最后,我找到了一个案例,它允许我测试,如果 PDOStatement::fetch
确实会在失败时抛出异常。
学分:
文章Taking advantage of PDO’s fetch modes就呈现了这样一种情况。它基于使用 PDOStatement::fetchAll
和 PDO::FETCH_KEY_PAIR
常量作为参数传递。
测试:
所以,我运行自己测试一下。但是我改用了 PDOStatement::fetch
方法。根据定义,PDO::FETCH_KEY_PAIR
常量要求数据源 table 仅包含两列。在我的测试中,我定义了三个 table 列。 PDOStatement::fetch
已将这种情况识别为失败并抛出异常:
SQLSTATE[HY000]: General error: PDO::FETCH_KEY_PAIR fetch mode
requires the result set to contain extactly 2 columns.
结论:
PDOStatement::fetch
returnsFALSE
,如果没有找到记录。
PDOStatement::fetch
抛出 - 实际上 - 失败时的异常。
备注:
- 相反,如果没有找到记录,
PDOStatement::fetchAll
returns 一个空数组。
PDO::FETCH_KEY_PAIR
常量未记录在 PDOStatement::fetch 官方页面上。
P.S:
我要感谢所有试图帮助我找到问题答案的用户。我很感激你!
用于测试的代码:
<?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();
}
创建 table 语法:
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');
Table 值:
id name phone
-----------------
1 Sarah 12345
2 John 67890
符合 php 文档,PDO 方法 fetch() return 值 FALSE
两者都没有找到记录 AND 失败(例如,当数据库访问出现问题时)。
假设,我将 PHP 错误报告系统设置为在失败时抛出异常:
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
我需要一个案例,一个fetch()
方法会抛出异常的情况。为什么?因为我想检查,100% 确定 fetch()
在失败时抛出异常,而不仅仅是 return FALSE
在失败时抛出异常。
如果是这样的话,那么我确实会考虑由 fetch()
编辑的 FALSE
return,因为在数据库中找不到任何记录 table.
所以,我的问题是:您知道模拟 fetch()
方法失败情况的方法吗?
谢谢。
P.S.:我的问题的答案将帮助我找到另一个问题的答案:
编辑 1:
我还准备了一个例子,来展示我是如何处理异常的。这是关于一个简单的 sql 查询,从 users
table:
<?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();
}
我使用了以下创建 table 语法:
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;
和以下 table 值:
INSERT INTO `users` (`id`, `name`)
VALUES
(1,'Sarah'),
(2,'John');
因此,table 看起来像这样:
id name
--------
1 Sarah
2 John
除非循环需要 PDOStatement,否则可以在 method/function 中使用 fetchAll 来获得所需的结果。只需执行 fetchAll 而不是 fetch,检查是否为 false,并根据需要检查 return。在这种情况下,您的查询应确保只有 1 行是 returned.
像这样;
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 也有类似的问题。
以上会return;
- 查询失败时为 false
- 如果未找到任何行,则获取一个空数组
- 如果找到合适的结果集进行提取
- 如果找不到,则为 fetchColumn 为 null
- 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
您可以做更多的事情来改进上述内容,但希望您能理解。
用PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
fetch如果有错误总是会抛出异常。您可以在 catch 块中处理它们,而不管它返回什么。因此,如果您没有捕获异常并且它返回 false,您可以放心地假设它是由于一个空集造成的。这是处理 PDO 错误的一种非常有效的方法。要回答您的问题,有很多方法可以模拟错误。最基本的是不正确的查询语法。您还可以尝试绑定一个不存在的参数,绑定错误数量的参数等。您使用此方案的时间越长,您看到的 errors/exceptions 类型就会越多。它非常有效,因为每个错误都包含一条详细消息以帮助您调试它。
最后,我找到了一个案例,它允许我测试,如果 PDOStatement::fetch
确实会在失败时抛出异常。
学分:
文章Taking advantage of PDO’s fetch modes就呈现了这样一种情况。它基于使用 PDOStatement::fetchAll
和 PDO::FETCH_KEY_PAIR
常量作为参数传递。
测试:
所以,我运行自己测试一下。但是我改用了 PDOStatement::fetch
方法。根据定义,PDO::FETCH_KEY_PAIR
常量要求数据源 table 仅包含两列。在我的测试中,我定义了三个 table 列。 PDOStatement::fetch
已将这种情况识别为失败并抛出异常:
SQLSTATE[HY000]: General error: PDO::FETCH_KEY_PAIR fetch mode requires the result set to contain extactly 2 columns.
结论:
PDOStatement::fetch
returnsFALSE
,如果没有找到记录。PDOStatement::fetch
抛出 - 实际上 - 失败时的异常。
备注:
- 相反,如果没有找到记录,
PDOStatement::fetchAll
returns 一个空数组。 PDO::FETCH_KEY_PAIR
常量未记录在 PDOStatement::fetch 官方页面上。
P.S:
我要感谢所有试图帮助我找到问题答案的用户。我很感激你!
用于测试的代码:
<?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();
}
创建 table 语法:
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');
Table 值:
id name phone
-----------------
1 Sarah 12345
2 John 67890