【问题标题】:Using a Persistent Database Object Connection from Other Objects in PHP在 PHP 中使用来自其他对象的持久数据库对象连接
【发布时间】:2017-04-26 21:09:30
【问题描述】:

这是指运行 PHP 5.4 和 MariaDB 5.5 的 CentOS 7 服务器。

我对 PHP 中的 OOP 有点陌生。在将一堆脚本从 MySQL 转换为 MySQLi 并将过程数据库函数转换为 OOP 时,我设置了这个基本的 Database 类:

class Database
{
    private $host = "localhost";
    private $username;
    private $password;
    private $database;
    private $dbconnect;

    function __construct()
    {
        // Load config file for database connection info
        $ini = parse_ini_file("config.ini");

        $this->username = $ini['db.user'];
        $this->password = $ini['db.pword'];
        $this->database = $ini['db'];
    }

    public function connect()
    {
        // Only make a new connection if one not already established. 
        if (empty($this->dbconnect)) {

            $mysql = new mysqli($this->host, $this->username, $this->password, $this->database);

            if ($mysql->connect_errno) {
                throw new appError($mysql->connect_error);
            }

            $this->dbconnect = $mysql;
        }

        return $this->dbconnect;
    }

    public function query($query)
    {
        $db = $this->connect();
        $result = $db->query($query);

        if ($db->errno) return false;
        return $result;
    }

    public function select($query)
    {
        $rows = array();
        $result = $this->query($query);

        if ($result === false) return false;

        // Create array with results
        while ($row = $result->fetch_assoc()) {
            $rows[] = $row;
        }

        return $rows;
    }
}

使用以前使用 mysql_* 的过程数据库函数,在脚本开始附近建立持久数据库连接,并将资源 ID 存储在全局变量中,然后所有查询都通过该数据库连接运行,方法是访问全局资源变量。重要的是,这在其他函数和对象中运行良好。该函数的工作原理如下:

function db_connect() {

    if (!empty($GLOBALS['DBCONNECT']) && is_resource($GLOBALS['DBCONNECT'])) {
        return $GLOBALS['DBCONNECT'];
    } else {    
        $result = mysql_connect("localhost", DB_USER, DB_PASSWORD);
        $GLOBALS['DBCONNECT'] = $result;
        return $result;
    }
}

所以在每个脚本的开头我都会这样做......

db_connect(); 

然后像这样运行我的查询...

$result = mysql_query($query, db_connect());

这确保建立了一个数据库连接,并且所有查询都通过该连接运行。

使用上面的新数据库类,我在脚本开始时实例化它...

$db = new Database;
$db->connect();

但我不明白如何使需要执行数据库查询的其他对象可以访问该数据库对象,以便整个脚本使用相同的数据库连接。我现在做的基本上就是这个……

class MyClass
{
    public function myFunction() 
    {
        $db = new Database; 
        $data = $db->select("SELECT * FROM mydata WHERE id = 888");
        ...
    }
}

这会在上述类中实例化一个新的 Database 对象,该对象正在创建与数据库的新的附加连接,因为它无法访问在父调用脚本中创建的 Database $db 对象(据我所知) .

有没有办法使用对象打开持久的 MySLQi 数据库连接,该脚本加载的所有函数和对象都可以使用该连接?或者这只是用过程函数而不是类和对象更好地完成的事情?解决方案是否在于使数据库属性及其方法静态?如果我这样做,我不确定如何从 connect() 函数需要的 config.ini 文件加载数据库用户名和密码,并且仅在数据库对象实例化时完成。

我想这里的基本问题是如何从另一个对象访问实例化对象中的属性或方法?或者也许这不是问题,我完全错过了其他东西。谢谢!

【问题讨论】:

    标签: php oop mysqli


    【解决方案1】:

    你可以用一个只有一个音调的静态方法来做类似的事情

    <?php
        /*
        * Mysql database class - only one connection alowed
        */
        class Database {
            private $_connection;
            private static $_instance; //The single instance
            private $_host = "HOSTt";
            private $_username = "USERNAME";
            private $_password = "PASSWORd";
            private $_database = "DATABASE";
            /*
            Get an instance of the Database
            @return Instance
            */
            public static function getInstance() {
                if(!self::$_instance) { // If no instance then make one
                    self::$_instance = new self();
                }
                return self::$_instance;
            }
            // Constructor
            private function __construct() {
                $this->_connection = new mysqli($this->_host, $this->_username, 
                    $this->_password, $this->_database);
    
                // Error handling
                if(mysqli_connect_error()) {
                    trigger_error("Failed to conencto to MySQL: " . mysql_connect_error(),
                         E_USER_ERROR);
                }
            }
            // Magic method clone is empty to prevent duplication of connection
            private function __clone() { }
            // Get mysqli connection
            public function getConnection() {
                return $this->_connection;
            }
        }
    

    要连接到数据库并使查询变得简单,请使用以下行:

    $db = Database::getInstance();
    $mysqli = $db->getConnection(); 
    $sql_query = "SELECT foo FROM .....";
    $result = $mysqli->query($sql_query);
    

    【讨论】:

      【解决方案2】:

      有几个选项,一个简单的选项(可能是首选)是在您的数据库类中使用静态工厂方法,该方法返回数据库类的先前初始化实例。

      class Database
      {
          //static instance of the class
          private static $instance;
      
          public static function getDB()
          {
              //if the instance is not set, set it
              if(is_null(self::$instance))
              {
                  self::$instance = new Database();
                  self::$instance->connect();
              }
              return self::$instance;
          }
      
          //rest of your class below
      }
      

      此静态方法可以在任何上下文中的任何位置调用,并允许获取相同的连接数据库实例。只需在您需要的任何地方致电$db = Database::getDB()。例如:

      function TestFunction()
      {
          //get an instance to the database
          $dbInstance = Database::getDB();
      
          //then use the database object like you normally would.
          $dbInstance->query("SELECT * FROM `someTable`");
      }
      
      class TestClass
      {
          public function doSomething()
          {
              //get an instance to the database
              $dbInstance = Database::getDB();
      
              //then use the database object like you normally would.
              $dbInstance->query("SELECT * FROM `someTable`");
          }
      }
      
      $tc = new TestClass();
      $tc->doSomething();
      

      另一个选项,也是我为简单起见而使用的选项,是声明您的连接为静态并在您的数据库类中的任何地方引用它以进行查询等。这允许在任何地方创建$db = new Database(),并且只会使用相同的连接。我对此感到不满,就好像它与全局相同,因为您实际上只能连接到一个数据库(后续连接会覆盖连接变量)但它对我有用,我不需要多个连接。我也喜欢这样,我可以让每个实例记住在该实例上运行的任何查询,并拥有运行的查询的私有计数。

      【讨论】:

      • 不要使用静态
      • @getl0st 很高兴看到该评论背后的原因。如果你使用得当,静态并不全是坏事。工厂方法,一种被广泛接受的做法,非常需要它。另外,我看不到您的替代解决方案。你更喜欢全球化吗?
      • 如何从另一个需要进行数据库查询的对象中访问 getDB() 方法,我该如何处理?
      • 由于getDB 方法是静态的,它在基类上全局可用。您可以在任何需要数据库类实例的地方使用Database::getDB()。这将返回该类的一个实例,并且您剩余的大部分代码都是相同的。我将更新答案是如何使用该功能的示例。
      • @JonathanKuhn 感谢您澄清您的回答。我也对您提到的另一个选项感到好奇,从一开始就将连接设为静态。在我上面的课程中,我是否会简单地在属性中使用private static $dbconnect,然后在connect() 函数中将其引用为self::$dbconnect?显然我对静态属性和方法的理解不够。
      猜你喜欢
      • 2011-05-15
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多