仅在需要时自动连接到 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的主要内容,如果未能解决你的问题,请参考以下文章
通过 php 中的 PDO 将 xampp 连接到在线 MSSQL 数据库
“连接被远程接口拒绝”使用 PDO 连接到 Firebird 3