这是我的代码:
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
?
答案 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()的调用也是多余的,可以删除。