仅在需要时自动连接到 PDO

Posted

技术标签:

【中文标题】仅在需要时自动连接到 PDO【英文标题】:Auto connecting to PDO only if needed 【发布时间】:2011-07-25 23:33:57 【问题描述】:

我有一段代码,根据请求的 URL,将包含其他 14 个文件之一。这十四个文件中的一些需要连接到三个不同数据库之一,并且可以随时添加其他文件。

我不想默认打开所有三个数据库的 PDO 连接,因为它浪费资源并且会减慢执行时间。所以我的想法是将所有 SQL 查询包装在一个函数中。第一次在未打开的 PDO 连接上执行查询时,try 错误处理程序可以捕获它,找出问题所在(在这种情况下连接不存在),然后打开连接并重新执行询问。这样,数据库只在需要时才被连接——只要连接字符串(主机、数据库、用户名、密码)都事先定义好,我看不出它有什么问题。

但是,我需要继续执行此操作,并且在大约 7 天内无法访问开发框,所以任何人都可以看到这种情况有什么问题吗?另外,谁能给我handler->errorInfo()如果连接没有打开会返回的错误信息?

【问题讨论】:

我建议告诉 PDO 在出错时抛出异常,而不是依赖 errorInfo。在连接选项中,使用array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION) @Chris,取决于。如果应用程序一般不使用异常,在 PDO 中启用它们弊大于利。 【参考方案1】:
function &get_pdo()

    static $_PDO = null;

    if ($_PDO === null)
    
        $_PDO = new PDO('your DSN', 'username', 'password');
    

    return $_PDO;

那你就做吧

$stmt = get_pdo()->prepare('...');

您可以通过扩展 PDO 类并向其添加静态单例函数来执行相同的操作。我发现这种方法更简单。还让您有机会从堆栈上的任何位置调用它,而不必将您的连接放入参数中(根据情况,这可能是好是坏)。

【讨论】:

【参考方案2】:

我采用了另一种方法,使用 __call 魔术方法,因此您无需为每个方法创建单独的包装器。

class PDOLazyConnector

    private $dsn;
    private $username;
    private $password;
    private $driver_options;
    private $dbh;

    public function __construct ($dsn, $username, $password, $driver_options = array ())
    
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->driver_options = $driver_options;
    

    public function __call ($function, $args)
    
        // connect to db (first time only)
        $this->__init_dbh ();

        // invoke the original method
        return call_user_func_array (array($this->dbh, $function), $args);
    

    public function __get ($property)
    
        return $this->dbh->$property;
    

    private function __init_dbh ()
    
        // If db handler is not open yet, do it now
        if (empty ($this->dbh)) 
            $this->dbh = new PDO ($this->dsn, $this->username, $this->password, $this->driver_options);
        
           

你只需要用 PDOLazyConnector 替换你的 PDO 实例,所以:

$dbh = new PDO($dsn, $user, $password, $driver_options);

与:

$dbh = new PDOLazyConnector($dsn, $user, $password, $driver_options);

【讨论】:

【参考方案3】:

完全按照您使用 PDO 类的方式使用这个类。

class DB extends PDO 

    protected $_config = array();

    protected $_connected = false;

    public function __construct($dsn, $user = null, $pass = null, $options = null) 
        //Save connection details for later
        $this->_config = array(
            'dsn' => $dsn,
            'user' => $user,
            'pass' => $pass,
            'options' => $options
        );
    

    public function checkConnection() 
        if (!$this->_connected) 
            extract($this->_config);
            parent::__construct($dsn, $user, $pass, $options)
            $this->_connected = true;
        
    

    public function query($query) 
        $this->checkConnection();
        return parent::query($query);
    

    public function exec($query) 
        $this->checkConnection();
        return parent::exec($query);
    

    //etc.

【讨论】:

最好的方法。 PDO 旨在扩展。 您当然会返回重载方法的结果。除此之外;正确的答案! 大家好,哇——没想到这么快就有答案了!!克里斯/乔恩,非常感谢您的回答。这可能是我,很厚 - 请不要 cmets! - 但是对于 Chris 的回答,我看不出它如何决定要连接到三个数据库中的哪一个?或者我错过了什么。 PDO 对我来说很新,但似乎合乎逻辑! 我发现编写一个不直接扩展 PDO 而是将其用作对象的参数(即$this->db = new PDO())的包装器更舒服。这样,我可以 (1) 编写更易于使用的界面来与 DB 进行交互,并且 (2) 可以将 DB 扩展从 PDO 更改为其他内容,并继续使用我的包装器而不会出现任何问题,如果我需要它的话。 @TIW,您将拥有 3 个对象(例如,$db1$db2$db3),每个对象都使用不同的参数(主机、端口、用户、pswd)创建。 【参考方案4】:

这是正确的想法,但不是最好的实现。

包装 SQL 操作很好。但是你为什么不这样做呢:

class Wrapper 
    private static $db;

    public static function someQuery() 
        $db = self::getDatabase();
        // now go on to execute the query
    

    private static function getDatabase() 
        if (self::$db === null) 
            self::$db = // connect here
        
        return self::$db;
    

这有很多好处:

允许您在逻辑上将 SQL 操作分组到一个(或多个!)类中 不需要时不连接到数据库 不依赖(脆弱的)错误检查才能正常工作

在您的具体情况下,您可能应该使用 3 个单独的 Wrapper 类。将所有内容放在一个类中是可行的(三个不同的$db 变量),但可能比它的价值更令人困惑。

【讨论】:

大家好,哇——没想到这么快就有答案了!!克里斯/乔恩,非常感谢您的回答。我假设您不能将变量变量与 PDO 一起使用?我的想法是通过查询传递数据库名称,然后获取代码以适当地处理连接 - 只要连接字符串是预定义的。那将避免三个单独的包装类 - 但我认为这不会起作用......会吗? 感谢您的示例,我认为这就是我前进的方式。还必须说声谢谢 - 我以前从未理解过私有函数......你的例子向我展示了如何使用它们! @TIW,关于私有成员 - 还有受保护的成员 ;) 虽然私有成员只能在定义它们的类中使用,但受保护的成员也可以在子类中使用,即在类中扩展定义受保护成员的类。【参考方案5】:

PDO 有一个持久连接选项PDO::ATTR_PERSISTENT

查看http://php.net/manual/en/book.pdo.php中的cmets

【讨论】:

以上是关于仅在需要时自动连接到 PDO的主要内容,如果未能解决你的问题,请参考以下文章

无法通过 PDO 连接到 AWS RDS

通过 php 中的 PDO 将 xampp 连接到在线 MSSQL 数据库

尝试使用 PDO 连接到错误的数据库时 PHP 不显示错误

“连接被远程接口拒绝”使用 PDO 连接到 Firebird 3

通过 SSL 上的 PHP PDO 连接到 MSSQL 服务器

PHP PDO 连接到 Advantage 数据库 SQL Server