【问题标题】:Simulate a PDO fetch failure situation模拟 PDO 获取失败情况
【发布时间】:2018-03-02 18:18:27
【问题描述】:

根据 php 文档,PDO 方法 fetch() 在没有找到记录时返回值 FALSE,在失败时(例如,当数据库访问出现问题时)。

假设,我将 PHP 错误报告系统设置为在失败时抛出异常:

PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION

我需要一个案例,fetch() 方法会抛出异常的情况。为什么?因为我要检查,所以要 100% 确定 fetch() 在失败时抛出异常,而不仅仅是在失败时返回 FALSE

如果是这样的话,我确实会认为fetch()返回的FALSE是在db表中没有找到任何记录的结果。

所以,我的问题是:你知道模拟fetch() 方法失败情况的方法吗?

谢谢。

P.S.:我的问题的答案将帮助我找到其他问题的答案:PHP PDO fetch returns FALSE when no records found AND on failure


编辑 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();
}

我使用了以下创建表语法:

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

【问题讨论】:

  • 启用基于异常的错误报告
  • 手册说:“这个函数在成功时的返回值取决于获取类型。在所有情况下,失败时返回 FALSE。”。我希望它在没有结果时返回NULL。因此可以进行两种不同的测试:if ($result === FALSE) ... 测试失败,if (!isset($result)) ....if (is_null($result)) 测试没有记录。
  • 谢谢,@teresko。我已经有了。但是,由于 php.net 说,失败时返回 FALSE,我真的不知道,如果失败时抛出异常,而不是 FALSE。
  • 好吧,在这种情况下处理异常是要走的路。试试吧。它会起作用的。
  • @MasivuyeCokile 终于找到了一个案例,让我模拟了PDOStatement::fetch()的失败情况。我发布了一个答案。再次感谢您的 cmets!

标签: php mysql pdo exception-handling fetch


【解决方案1】:

最后,我找到了一个案例,让我可以测试PDOStatement::fetch 是否确实会在失败时抛出异常。

学分:

Taking advantage of PDO’s fetch modes 的文章提出了这样一种情况。它基于使用PDOStatement::fetchAllPDO::FETCH_KEY_PAIR 常量作为参数传递。

测试:

所以,我自己进行了测试。但我改用PDOStatement::fetch 方法。根据定义,PDO::FETCH_KEY_PAIR 常量要求数据源表仅包含两列。在我的测试中,我定义了三个表格列。 PDOStatement::fetch 已将此情况识别为失败并抛出异常:

SQLSTATE[HY000]:一般错误:PDO::FETCH_KEY_PAIR 获取模式 要求结果集正好包含 2 列。

结论:

  • PDOStatement::fetch 返回FALSE,如果没有找到记录。
  • PDOStatement::fetch 在失败的情况下抛出 - 实际上 - 异常。

注意事项:

  • 如果没有找到记录,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

【讨论】:

    【解决方案2】:

    使用PDO::ATTR_ERRMODE =&gt; PDO::ERRMODE_EXCEPTION fetch 如果有错误总是会抛出异常。你可以在你的 catch 块中处理这些,它返回什么并不重要。因此,如果您没有捕获异常并且它返回 false,您可以放心地假设它是由于空集引起的。这是处理 PDO 错误的一种非常有效的方法。要回答您的问题,有很多方法可以模拟错误。最基本的是不正确的查询语法。您还可以尝试绑定不存在的参数,绑定错误数量的参数等。使用此方案的时间越长,您将看到的错误/异常类型越多。它非常有效,因为每个错误都包含一个详细的消息来帮助您调试它。

    【讨论】:

    • 谢谢你,pucky。很抱歉我迟到的答案。您描述了模拟preparebind() 的方法。还可以模拟execute() 故障。事实上,所有这些方法都会在失败时抛出异常。现在,谜团是:fetch() 是否也会在失败时引发异常? >>
    • >> 这就是我正在努力寻找的。基本上,我想准确地模拟这样的情况......你说“_如果有错误,它将抛出异常并且根本不返回”。你能100%向我确认吗?如果是的话,你能告诉我一个例子吗?我会而且我会给出我全部的 385 个声誉来找出一个例子。
    • 我还发布了我的问题的第三个版本:Does PDO fetch() method throw an exception on failure?。我收到了一个答案,指出失败时不会引发异常。更有趣的是:fetch() 的情况下似乎没有失败的情况。
    • 我稍微更新了答案,以便更清楚地了解异常。如果异常返回 false,这取决于您如何处理异常,但这是区分这两种情况的 100% 有效方法。
    • 谢谢你,pucky。请允许我明天读。现在我很累。我也看到了,你也回答了我的另一个问题。所以,直到明天。
    【解决方案3】:

    除非您需要 PDOStatement 进行循环,否则您可以在方法/函数中使用 fetchAll 来获取您正在寻找的结果。只需执行 fetchAll 而不是 fetch,检查是否为 false,并根据需要返回。在这种情况下,您的查询应确保只返回 1 行。

    类似的东西;

    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 也有类似的问题。

    上面会返回;

    • 查询失败时为 false
    • 如果未找到行,则为获取的空数组
    • 找到适当的结果集以获取(如果找到)
    • 如果未找到,则 fetchColumn 为空
    • 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
    

    您可以做更多的事情来改进上述内容,但希望您能明白这一点。

    【讨论】:

    • 您的帖子没有回答问题,也没有回答问题的先前版本。
    • 我的帖子是为了回应他在上述问题中的cmets。具体来说,他制作的最后 2 个。
    • @user2430012 非常感谢您的回答!在fetch() 问题(以及我最近的 2 个 cmets)的背景下,我个人认为您的答案是合格的。
    • @user2430012 现在,基于我的问题的第三个版本:Does PDO fetch() method throw an exception on failure?,我倾向于 99% 的人认为,fetch() 的情况下没有失败情况 :-) 我真的感谢您给我一个好的答案!
    • 不客气。补充一点,无论使用什么 PDO::ATTR_ERRMOD , fetch() 都不会抛出异常。它总是在失败时返回 false。即使您将 stmt 包装在 try/catch 块中,然后调用 fetch(),它仍然会返回 false。 fetchAll() 也会在失败时返回 false,但我从来没有真正让它返回 false。即使在此评论的示例中,它也会返回一个空数组。
    猜你喜欢
    • 1970-01-01
    • 2011-07-19
    • 2013-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 1970-01-01
    相关资源
    最近更新 更多