简单的 PDO 包装器

Posted

技术标签:

【中文标题】简单的 PDO 包装器【英文标题】:Simple PDO wrapper 【发布时间】:2011-10-08 01:59:48 【问题描述】:

我的 Web 应用程序目前确实执行简单的查询:简单的 CRUD 操作、计数、...

几个月前,有人推荐我在这里为此编写一个(以避免每次执行查询时都编写 try/catch、prepare()、execute() 等)。展示了这个示例方法(我做了一些更改,以便可以在我自己的项目中使用它):

public function execute() 
    $args  = func_get_args();
    $query = array_shift($args);
    $result = false;

    try 
      $res = $this->pdo->prepare($query);
      $result = $res->execute($args);
     catch (PDOException $e)  echo $e->getMessage(); 

    return $result;
  

由于我需要执行更多操作(执行查询、检索 1 条记录、检索多条记录、计算结果),我为所有这些创建了一个方法:

  public function getMultipleRecords() 
    $args  = func_get_args();
    $query = array_shift($args);
    $records = array();

    try 
      $res = $this->pdo->prepare($query);
      $res->execute($args);
      $records = $res->fetchAll();
     catch (PDOException $e)  echo $e->getMessage(); 

    return $records;
  

  public function getSingleRecord() 
    $args  = func_get_args();
    $query = array_shift($args);
    $record = array();

    try 
      $res = $this->pdo->prepare($query);
      $res->execute($args);
      $record = $res->fetch();
     catch (PDOException $e)  echo $e->getMessage(); 

    return $record;
  

  public function execute() 
    $args  = func_get_args();
    $query = array_shift($args);
    $result = false;

    try 
      $res = $this->pdo->prepare($query);
      $result = $res->execute($args);
     catch (PDOException $e)  echo $e->getMessage(); 

    return $result;
  

  public function count() 
    $args  = func_get_args();
    $query = array_shift($args);
    $result = -1;

    try 
      $res = $this->pdo->prepare($query);
      $res->execute($args);
      $result = $res->fetchColumn();
     catch(PDOException $e)  echo $e->getMessage(); 

    return $result;
  

如您所见,大部分代码是相同的。每个方法只有两行代码不同:$result 的初始化(我总是想返回一个值,即使查询失败)和获取。除了使用 4 种方法,我可以只编写其中一种并传递一个带有动作类型的额外参数。这样,我可以使用一堆 if/else 语句的 switch 语句。但是,我认为代码可能会变得混乱。这是解决这个问题的好方法吗?如果没有,有什么好的解决方法?

我遇到的第二个问题(这就是我现在正在研究这门课的原因)是我想将准备好的语句与 LIMIT SQL 语句一起使用。但是,这是不可能的:

$res = $pdo->prepare("SELECT * FROM table LIMIT ?");
$res->execute(array($int));

变量将因某种原因被引用(因此查询将失败),如下所述: https://bugs.php.net/bug.php?id=40740

解决方案似乎使用了 bindValue() 并使用 int 数据类型作为参数: http://www.php.net/manual/de/pdostatement.bindvalue.php

我可以重写方法来支持这一点,但我还需要使用一个额外的参数。我不能再只使用$db->execute($sql, $variable1, $variable2);,因为我需要知道数据类型。

解决这个问题的最佳方法是什么?

谢谢

【问题讨论】:

【参考方案1】:

如何使用可以链接的方法创建一个类(为清楚起见,我删除了错误检查):

class DB 

    private $dbh;
    private $stmt;

    public function __construct($user, $pass, $dbname) 
        $this->dbh = new PDO(
            "mysql:host=localhost;dbname=$dbname",
            $user,
            $pass,
            array( PDO::ATTR_PERSISTENT => true )
        );
    

    public function query($query) 
        $this->stmt = $this->dbh->prepare($query);
        return $this;
    

    public function bind($pos, $value, $type = null) 

        if( is_null($type) ) 
            switch( true ) 
                case is_int($value):
                    $type = PDO::PARAM_INT;
                    break;
                case is_bool($value):
                    $type = PDO::PARAM_BOOL;
                    break;
                case is_null($value):
                    $type = PDO::PARAM_NULL;
                    break;
                default:
                    $type = PDO::PARAM_STR;
            
        

        $this->stmt->bindValue($pos, $value, $type);
        return $this;
    

    public function execute() 
        return $this->stmt->execute();
    

    public function resultset() 
        $this->execute();
        return $this->stmt->fetchAll();
    

    public function single() 
        $this->execute();
        return $this->stmt->fetch();
    

然后你可以像这样使用它:

// Establish a connection.
$db = new DB('user', 'password', 'database');

// Create query, bind values and return a single row.
$row = $db->query('SELECT col1, col2, col3 FROM mytable WHERE id > ? LIMIT ?')
   ->bind(1, 2)
   ->bind(2, 1)
   ->single();

// Update the LIMIT and get a resultset.
$db->bind(2,2);
$rs = $db->resultset();

// Create a new query, bind values and return a resultset.
$rs = $db->query('SELECT col1, col2, col3 FROM mytable WHERE col2 = ?')
   ->bind(1, 'abc')
   ->resultset();

// Update WHERE clause and return a resultset.
$db->bind(1, 'def');
$rs = $db->resultset();

如果您愿意,您可以更改 bind 方法以接受数组或关联数组,但我发现这种语法非常清楚 - 它避免了构建数组。参数类型检查是可选的,因为PDO::PARAM_STR 适用于大多数值,但请注意传递空值时的潜在问题(请参阅comment in PDOStatement->bindValue 文档)。

【讨论】:

这很漂亮,迈克。我喜欢链式课程的想法! 非常好的主意,但我会使用$row = $db->query('SELECT col1, col2, col3 FROM mytable WHERE id > :id LIMIT :limit')->bind(":id", 2)->bind(":limit", 1)->single(); 很容易阅读 @Ergec 只是一个提示,你不能有相同的 :limit 占位符两次,即使它在两个地方都有相同的值 我喜欢你的课,非常感谢。但只有 1 个问题:你对函数所做的事情的正确名称是什么(绑定或类似的东西?)我的意思是这部分:$db->query(...)->bind(..)->bind(..)->single(...) 因为我喜欢阅读更多内容(它是如何工作的等等) )。 抱歉,我没有读到足够好的 cmets。它被称为链接...more about this

以上是关于简单的 PDO 包装器的主要内容,如果未能解决你的问题,请参考以下文章

php 使用CRUD方法的简单而安全的PHP PDO数据库包装器......

PHP 简单的MySQL包装PDO

PDO 包装器返回 NULL

如何设计一个简单的 GLSL 包装器以供着色器使用

optparse 到 argparse 的简单包装器?

简单的 jdbc 包装器