【问题标题】:Auto connecting to PDO only if needed仅在需要时自动连接到 PDO
【发布时间】:2023-03-19 17:41:01
【问题描述】:

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

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

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

【问题讨论】:

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

标签: php mysql database 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

      【讨论】:

        猜你喜欢
        • 2013-03-04
        • 1970-01-01
        • 2017-04-13
        • 2010-11-16
        • 1970-01-01
        • 1970-01-01
        • 2012-11-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多