mysqli

时间:2015-05-28 09:25:36

标签: php mysqli

在尝试构建健壮的数据库代码(表锁定,事务等)时,我总是被需要完成的大量代码所困扰。

例如,两个准备好的语句中的一个事务,我想删除一个用户并在“actions”表中更新一些关于他的内容:

  1. 锁定表用户,操作
  2. 启动交易(autocommit false)
  3. 为删除用户制作准备好的声明
  4. 检查语句是否为!= false(因为它可能在3时已经失败。
  5. Bind param
  6. 检查语句中的errorState!=“00000”(绑定参数也可能失败)
  7. 执行声明
  8. 检查语句中的errorState!=“00000”(执行时也可能失败)
  9. 获取陈述结果
  10. 关闭声明
  11. 为更新操作制作新的准备好的声明
  12. 检查语句是否!= false
  13. bind params
  14. check statement's errorState
  15. 执行
  16. check statement's errorState
  17. 获得结果
  18. close statement
  19. 检查整体事务状态,如果有效提交,如果无效,则回滚
  20. 解锁表格
  21. 将autocommit设置为true
  22. 我是这样做的(也许我做错了?)。如果我这样做它的很多工作和烦人。所以我认为自动化这些东西。

    我想要的是这样的:

    $DB->startTransaction();
    $DB->query($query);
    $DB->query($query2);
    $DB->query($query3);
    $DB->endTransaction();
    

    在内部,mysqli上面的数据库抽象层将负责表锁定,预处理语句和事务本身。我们不应该自动化吗?

    这是我的一次尝试:

    public function query($query, $table, $params = null) {
                if($params == null) {
                    $this->connection->query("LOCK TABLES $table WRITE");
                    $query = str_replace("!", $table, $query);
                    $result = $this->connection->query($query);
                    $this->connection->query("UNLOCK TABLES");
                    return $result;
                }
                else {
                    if (!$this->checkParams($query, $params)) {
                        return false;
                    }
                    $this->connection->query("LOCK TABLES $table WRITE");
                    $query = str_replace("!", $table, $query);
    
                    $stmt = $this->connection->prepare($query);
                    if ($stmt != false) {
                        $typesString = "";
                        foreach ($params as $param) {
                            if (is_numeric($param)) {
                                $typesString .= "i";
                            } else if (is_double($param)) {
                                $typesString .= "d";
                            } else {
                                $typesString .= "s";
                            }
                        }
    
                        $finalParamArray = array($typesString);
                        $finalParamArray = array_merge($finalParamArray, $params);
                        call_user_func_array(array($stmt, "bind_param"), $this->ref($finalParamArray));
                        $this->checkStatement($stmt);
                        $stmt->execute();
                        $this->checkStatement($stmt);
                        $result = $stmt->get_result();
                        $stmt->close();
                        $this->connection->query("UNLOCK TABLES");
                        return $result;
                    }
                    $this->query("UNLOCK TABLES");
                    return false;
                }
            }
    

    这可以这样调用:

    $DB->query("DELETE FROM ! WHERE userID =?", "Users", array($userID));
    

    但我对此并不自信。我google了一下,没有找到像我想要的东西。所以现在我的问题是:我想要的实际可能(它应该是)吗?我做错了吗?

    编辑: 我还有另外两次尝试这样做,看起来更复杂(300多行代码)。如果你愿意,我也可以发布它们。然而,我仍然对他们不满意,并且不确定这是否真的正确!

1 个答案:

答案 0 :(得分:1)

您是对的,应该有一个更简单的方法来执行此操作,并且您也正确地说我们需要在mysqli之上的抽象层。它不能单独使用。

您不需要太多步骤。特别是,您无需检查每种方法的返回码。那应该已经消除了6个或更多的步骤。您也不需要关闭语句。

绑定时无需指定类型。一直使用字符串类型。其他类型很少派上用场,几乎从未派上用场。

前一段时间,我发布了一个示例,说明mysqli之上的抽象层是什么样的。

class DBClass extends mysqli {
    public function __construct(
        $host = null,
        $username = null,
        $passwd = null,
        $dbname = null,
        $port = null,
        $socket = null
    ) {
        mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
        parent::__construct($host, $username, $passwd, $dbname, $port, $socket);
        $this->set_charset('utf8mb4');
    }

    public function safeQuery(string $sql, array $params = []): ?array {
        $stmt = $this->prepare($sql);
        if ($params) {
            $stmt->bind_param(str_repeat("s", count($params)), ...$params);
        }
        $stmt->execute();
        if ($result = $stmt->get_result()) {
            return $result->fetch_all(MYSQLI_BOTH);
        }
        return null;
    }
}

这远非完美,但它显示了主要思想。您可以将准备好的语句包装在一个方法中。简单的准备/绑定/执行/获取结果。而已。它可以使用和不使用参数。

在构造函数中,打开连接的3个必需步骤:切换错误报告,创建mysqli实例以及设置正确的字符集。

如果要进行交易,则可以使用mysqli的begin_transaction()commit()。它们足够简单,不需要抽象。

我不知道您为什么觉得需要锁定表,但这又是一个简单的SQL语句,不需要抽象。

$db = new DBClass('localhost', 'user', 'pass', 'test');
$db->safeQuery('LOCK TABLES users WRITE');
$db->begin_transaction();
$db->safeQuery('DELETE FROM users WHERE userID =?', [$userID]);
$db->safeQuery('DELETE FROM otherTable WHERE userID =?', [$userID2]);
$db->commit();
$db->safeQuery('UNLOCK TABLES');