如何处理交易时如何处理错误?

时间:2016-07-26 00:19:12

标签: php mysql pdo

这是我的代码:

try {
    $dbh_con->beginTransaction();

        $stmt1 = $dbh_conn->prepare("UPDATE activate_account_num SET num = num + 1");
        $stmt1->execute();

        $stmt2 = $dbh_con->prepare("SELECT user_id FROM activate_account WHERE token = ?");
        $stmt2->execute(array($token));
        $num_rows = $stmt2->fetch(PDO::FETCH_ASSOC);

        if ( $num_rows['user_id'] ){
            $_SESSION['error'] = 'all fine';

        } else {
            $_SESSION['error'] = 'token is invalid';
        }

    $dbh_con->commit();

    header('Location: /b.php');
    exit();

} catch(PDOException $e) {

    $dbh_con->rollBack();

    $_SESSION['error'] = 'something is wrong';
    header('Location: /b.php');
    exit();
}

如您所见,我的脚本在发生异常时回滚所有查询。但是if ( $num_rows['user_id'] ){false时,它不会回滚。那么,当条件为'token is invalid'时,如何回滚查询并保留错误false

3 个答案:

答案 0 :(得分:4)

只是抛出一个异常并抓住它就像你已经做的那样。但是没有一个catch语句有两个:

try {
    $dbh_con->beginTransaction();

        $stmt1 = $dbh_conn->prepare("UPDATE activate_account_num SET num = num + 1");
        $stmt1->execute();

        $stmt2 = $dbh_con->prepare("SELECT user_id FROM activate_account WHERE token = ?");
        $stmt2->execute(array($token));
        $num_rows = $stmt2->fetch(PDO::FETCH_ASSOC);

        if ( $num_rows['user_id'] ){
            $_SESSION['error'] = 'all fine';

        } else {
            throw new \Exception('token is invalid');
        }

    $dbh_con->commit();

    header('Location: /b.php');
    exit();

} catch(PDOException $e) {

$dbh_con->rollBack();

$_SESSION['error'] = 'something is wrong';
header('Location: /b.php');
exit();
} catch(Exception $e) {

    $dbh_con->rollBack();

    $_SESSION['error'] = 'token is invalid';
    header('Location: /b.php');
    exit();
}

答案 1 :(得分:1)

您的操作顺序不佳。在验证令牌之前,您正在对数据库进行更改。这是糟糕的安全设计。始终首先验证所有输入,然后才进行更改。

其次,获取令牌的SELECT查询不需要成为事务的一部分。回滚SELECT没有任何影响,因为此类查询不会对数据库进行任何更改。所以我会这样做

try{
   select token
   if token is not found, set error and exit

   begin transaction
   update active_account_num
   ...other queries?
   end transaction and commit

   set success message, set header & exit
}catch{
   rollback
   set error message, set header & exit
}

在这种情况下,因为只有一个查询可以更改数据库,所以甚至不需要事务。

答案 2 :(得分:1)

如果在确保表中包含感兴趣的值

之后进行更新,则更容易
try {
    $stmt2 = $dbh_con->prepare("SELECT user_id FROM activate_account WHERE token = ?");
    $stmt2->execute(array($token));
    $num_rows = $stmt2->fetch(PDO::FETCH_ASSOC);

    if ( $num_rows['user_id'] ){
        $dbh_con->beginTransaction();
        $stmt1 = $dbh_conn->prepare("UPDATE activate_account_num SET num = num + 1");
        $stmt1->execute();

        $_SESSION['error'] = 'all fine';
        $dbh_con->commit();

    } else {
        $_SESSION['error'] = 'token is invalid';
        /* no transaction here, nothing to rollback */
    }

    header('Location: /b.php');
    exit();

} catch(PDOException $e) {

    $dbh_con->rollBack();

    $_SESSION['error'] = 'something is wrong';
    header('Location: /b.php');
    exit();
}

对exit()的调用也是多余的,可以删除。

相关问题