重新抛出部分捕获的异常?

4

我有一些代码,期望在向远程数据库插入数据时偶尔会出现唯一索引冲突。由于我需要一个随机生成但易于人类阅读的值,因此无法避免此冲突。不幸的是,当这种情况发生时,PDO只会抛出通用的PDOException异常,因此我需要一个包含以下内容的循环:

do {
        ...
   } catch (PDOException $e) {
       if ($e->getCode() === '23000') {
          continue; // have another go
      } else {
          throw new Exception("...", 1, $e); // reraise
      }
} while(there was an exception);

我认为错误捕获机制比搜索后插入记录更好,因为在一个四位数代码和仅有几百个活动代码的情况下,会发生的冲突很少。
Stack Overflow上的问题,例如最佳实践:捕获并重新抛出异常?将PHP异常重新抛到更高层的catch块中表明即使在PHP中,这也是不良行为。我既捕获了一个我99%确定会重新抛出的异常,又将异常用于流程控制。
在PHP中是否有更好的方法来解决这个问题,或者我唯一的选择是在数据库中使用存储过程,在失败时返回错误代码而不是异常?

使用 switch 怎么样? - Darren
@Darren,我在哪里可以使用switch?如果我只有一个条件加上默认的switch在异常处理程序中,那就是我已经拥有的if语句的冗长形式。但我看不出如何用switch替换循环。抱歉,我很困惑。 - Code Abominator
2个回答

3
如果您想重新抛出相同的异常,为什么不直接使用以下代码:
throw $e;

我可以这样做。但这不是问题所在。我想知道是否有更好的方法来捕获我关心的那个错误代码。 - Code Abominator

2
我的结论是,在PHP中确实没有更好的方法来完成这个任务,因此我将逻辑移到MySQL中,这样会稍微好看一些。我使用错误处理而不是搜索,因为在正常情况下没有碰撞的情况下,这样更快。
首先进行搜索会导致PHP变慢(两个或更多的查询),对于SQL来说,在正常情况下会稍微慢一些(一个额外的查询),但如果有很多碰撞,那么首先执行搜索会比插入失败更快。
DECLARE CONTINUE HANDLER FOR 1062 -- duplicate key
    SET @dupe = 1;

SET @dupe = 0;
SET @attemptsLeft = 5;
REPEAT
    SET @user_code = randomString(4);
    INSERT INTO mytable(expires, user_code, users_id)VALUES(p_expires, @user_code, p_users_id);
    SET @attemptsLeft = @attemptsLeft - 1;
    -- if there's a dupe we get the continue handler above
UNTIL @dupe = 0 OR @attemptsLeft <= 0 END REPEAT;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接