在PHP中创建一个MySQL单例数据库类是否有更简单/更好的方法?

3

这是我使用的一个:

<?php
final class Database {
    private static $oDb;
    public static function init() {
        if(self::$oDb == NULL)
        {
            self::$oDb = mysql_connect('localhost', 'mysql_user', 'mysql_password') or die(mysql_error());
            mysql_select_db('mysql_db_name', self::$oDb) or die (mysql_error());;
        }
        return self::$oDb;
    }
    public function query($sql)
    {
        return mysql_query($sql) or die(mysql_error());
    }
}
?>

使用方法:

$oDb = Database::init();
$sql = foo;
$oDb->query($sql);

假设我只想连接并执行这个查询函数,有没有对这个类应该进行的改进?内存或代码效率方面的?

另外,我能否从配置文件中高效地获取数据库凭据?我知道我不能在我的类中使用包含。


为什么不能在类内部使用includes?虽然从技术上讲你是可以的,但从编码实践的角度来看,你可能想尝试避免这样做。 - Darrell Brogdon
我认为他认为你不能在函数外使用它... - Tyler Carter
我的配置文件只是一组 define("name", "value");。不需要解析。 - Justin Johnson
4个回答

4
我通常在这种情况下使用延迟初始化,并只有一个公共方法(在这种情况下),通过私有构造函数来防止外部实例化(按照单例模式):
class Database {
  private static $instance;
  private $conn;

  private function Database() {
    // do init stuff
    require_once('dbconfig.php'); // contains define('DB_USER', 'webuser'); etc...
    $this->conn = mysql_connect(DB_HOST, DB_USER, DB_PASS); // do error checking
  }

  public static function getInstance() {
    if(!self::$instance) {
      self::$instance = new Database();
    }
    return self::$instance;
  }

  public static function query($sql) {
    $instance = self::getInstance();
    return mysql_query($sql, $instance->conn);
  }
}

那么,每当您需要使用它时,只需调用$dbHandle = Database::getInstance()即可。 或者在这种情况下,由于定义了一个静态查询方法,您可以使用Database::query("select * from xx;");,而无需调用任何类型的init。


如果在相对路径上使用 require_once,那么这是否意味着如果我在不同的目录中包含类文件本身,它就会出错?例如在 index.php 中包含 'db.class.php' 和 admin/index.php 中包含 'db.class.php'。 - Citizen
通常,我会使用dirname()样式函数定义一个ROOT_DIR或类似的变量,以获取站点根目录的绝对路径,然后我会使用类似require_once(ROOT_DIR.'/config/db.config.php');的东西。这样,无论在哪个目录下获取配置文件都不会有问题。 - Dan Breen
我可能要求过多,但是当我尝试使用你的类时,我遇到了一堆错误。我在你的“public static”行中添加了“function”,然后我得到了一个关于在对象上下文之外使用$this的错误。有什么想法吗? - Citizen
抱歉,我实际上没有尝试运行它,所以错过了一些语法错误。我已经编辑了帖子并进行了测试;现在它可以工作了。在公共静态方法中$this->instance应该是self::$instance - Dan Breen
这虽然有点古老,但为了后人着想,比起保留一个ROOT_DIR变量,更简单的方法是使用内置的__DIR__常量。假设你的db类在根目录的子目录中,那么你可以调用require_once(DIR.'/../config/db.config.php');。 - Nathan Stretch

2

这很简单,这将非常有效。

您可以将您的凭据传递给init();

include(config.php);
$oDb = Database::init( DB_HOST, DB_NAME, DB_USER, DB_PASSWORD );
$sql = foo;
$oDb->query($sql);

1

你可以在类的函数内部使用 include

<?php
final class Database {
    private static $oDb;
    public static function init() {
        if(self::$oDb == NULL)
        {
            include('config.php')
            self::$oDb = mysql_connect(DB_HOST, DB_USER, DB_PASS) or die(mysql_error());
            mysql_select_db(DB_NAME, self::$oDb) or die (mysql_error());;
        }
        return self::$oDb;
    }
    public function query($sql)
    {
        return mysql_query($sql) or die(mysql_error());
    }
}
?>

或者你可以直接传递变量...

<?php
final class Database {
    private static $oDb;
    public static function init($host, $user, $pass, $name) {
        if(self::$oDb == NULL)
        {
            self::$oDb = mysql_connect($host,$user,$pass) or die(mysql_error());
            mysql_select_db($name, self::$oDb) or die (mysql_error());;
        }
        return self::$oDb;
    }
    public function query($sql)
    {
        return mysql_query($sql) or die(mysql_error());
    }
}
?>

或者您可以将凭证存储在php.ini文件中

<?php
final class Database {
    private static $oDb;
    public static function init($db_name) {
        if(self::$oDb == NULL)
        {
            self::$oDb = mysql_connect() or die(mysql_error());
            mysql_select_db($db_name, self::$oDb) or die (mysql_error());;
        }
        return self::$oDb;
    }
    public function query($sql)
    {
        return mysql_query($sql) or die(mysql_error());
    }
}
?>

php.ini 文件:

mysql.default_host="host"
mysql.default_user="user"
mysql.default_password="password"

1

对于单例类,Dan Breen 所遵循的模式是最干净和非常常见的。然而,在这种情况下,我也会允许 getInstance 方法接受一些参数,以便您可以在实例化时覆盖默认配置,或者只是获取一个引用而不创建连接(这两种用例有时会发生)。

Database.php

require_once("../path/to/config/database.php");

class Database {
  private static $instances = array();

  private function Database($host, $user, $password, $name) {
    // do init stuff
  }

  public static getInstance(
    $host=DB_HOST, $user=DB_USER, $password=DB_PASSWORD, $name=DB_NAME
  ) {
    $key = strtolower($host . $user . $password . $name);

    if ( !$self::instances[$key] ) {
      $self::instances[$key] = new Database($host, $user, $password, $name);
    }
    return $self::instances[$key];
  }
}

..config/database.php:

define("DB_HOST", "localhost");
define("DB_USER", "mrsqlguy");
define("DB_PASS", "!!!");
define("DB_NAME", "just_another_wordpress");

编辑:我已经将它更改为更像是一个轻量级模式,以确保您只获得每个连接位置/数据库的一个实例。这解决了您的问题并保持了一定的灵活性。


灵活性很好,我喜欢这个答案,但潜在的问题是,由于它只初始化一次,可能会导致以后出现非常奇怪的问题。也就是说,如果程序员使用一些新的连接参数调用getInstance,但在其他地方已经使用不同的参数进行了初始调用,那么很难弄清楚第二次没有使用传递的参数。 - Dan Breen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接