PHP对象和共享数据库连接


PHP objects and shared database connection

在PHP中使用OOP方法时,在相关实例中,数据库连接的更好方法是什么?

  • 传递给所有实例的数据库连接
  • 每个实例都有自己的数据库连接

这是我的代码示例。

正在将连接传递给构造函数:

<?php
class Model {
  public $db;
  public function __construct($db) {
        $this->db = $db;
  }
}

正在连接到模型构造函数中的数据库。

class Model {
  protected $db;
  public function __construct() {
        $this->db = new Detabase_Con();
  }
}

用户模型

class Users extends Model {
  public function showUsers() {
    $this->db->select("SELECT * FROM users");
  }
}

Singleton数据库访问

这是非常有争议的,但可以确保数据库连接不会重复,并且更容易在现有代码中使用。

class DB {
    private $instance=null;
    private static function instance() {
        if (self::$instance === null) {
            self::$instance = new PDO(/*connection foo*/);
        }
        return self::$instance;
    }
    public static function __callStatic($method, $args) {
        return call_user_func_array(array(self::instance(), $method), $args);
    }
}

include('DB.php'); // the file containing the class
$sth = DB::prepare("SELECT foo");
$sth->execute();
$row = $sth->fetch();

这在很大程度上借鉴了这个答案中提出的简单PDO包装器。包装是为精确的需求而写的,所以按原样使用

依赖项注入

这可能意味着你必须更好地考虑你的代码,也许还要重写其中的大部分。重点是在类/函数签名中键入提示你的连接,以明确它取决于PDO:

class user{
    function construct(PDO $db,$user_id) {
        // define your user with the infos from $db
    }
}
$db = new PDO(/*connection foo*/);
$wants_db = new user($db,$user_id);

如果重写所有函数签名以传递$db,您可能会发现它并没有更好,而且可能是真的。您可能需要重新设计应用程序的某些部分,以便不是所有的功能或方法都依赖于数据库连接。

如何选择

单例方法现在更容易了,可能适合您的需求,但会带来问题。如果您意识到将其作为参数传递是一种更好的方法,那么您就不想启动此操作并重构代码

依赖注入兼容的方法现在更难了,因为它意味着代码的更改,但这种能量意味着最终您将拥有更好/更干净/更容易维护的代码。

在对singleton进行了实验之后,我认为实现注入的数据库访问更好。我认为它代表了固体的一部分。我想说,这为php oop脚本中的数据库访问提供了一种更好的、更经得起未来考验的方法。

让所有的类从包含数据库连接的类继承是个非常糟糕的主意。这不是MVC本身的问题,而是对面向对象编程方法的滥用
因此,第一种方法看起来要好得多。

就我个人而言,我喜欢将数据库连接作为一个单例。例如:

<?php
class MyDatabase extends PDO
{
    public static function SharedInstance() {
        static $instance = false;
        
        if(!$instance) {
            if(!($instance = new MyDatabase('mysql:.....', '', '', array(PDO::ATTR_PERSISTENT => true)))) {
                throw new Exception('Cannot connect to database');
            }
            $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $instance->exec('SET NAMES utf8'); // MySQL only
        }
        
        return $instance;
    }
    
    // ... More methods and utils for the database class, if you need to extend it
}
?>

然后,在代码的任何地方(在任何范围内),我都可以通过简单地执行以下操作来回忆到与数据库的连接:

$db = MyDatabase::SharedInstance();
// And use it as a normal PDO object. For example:
$db->exec('some query');

您不需要使用PDO;您也可以使用MySQLi和其他数据库驱动程序来实现这一点。但是,当数据库驱动程序具有面向对象的接口时,它的工作效果最好。

附言:只是为了确保,总是使用事先准备好的陈述。上面对$db->exec的调用只是公开PDO接口的一个示例。

为我的想法辩护

我知道单身人士是有争议的,我并不是说他们是解决任何问题的最佳方案。然而,在这种情况下,它们可以而且确实有意义。以下是我在评论中收到的一些批评:

  1. 它们使单元测试更加困难
    由于我没有覆盖__construct()方法以防止从MyDatabase类初始化其他对象,因此您应该能够编写没有任何问题的测试。

  2. 您正在对类名进行硬编码
    假设我们没有使用singleton。许多用户会为数据库连接使用全局变量;硬编码的东西";。无论如何,编程是硬编码的东西,所以我不明白为什么这应该是一个问题。最糟糕的情况:只运行find&替换所有项目文件。任何编辑都可以轻松做到这一点。

  3. 很难连接到多个(SQL)数据库
    首先,我敢打赌,SO上只有0.01%的开发人员真正需要连接到同一PHP应用程序中的多个SQL数据库。对于那些这样做的人来说,单身可能是他们处理的最后一个问题
    因此,使用这种方法,您仍然可以连接到NoSQL数据库和缓存,实际上您可以在MyDatabase类中包含一些缓存逻辑。如果您需要使用PDO连接到另一个SQL数据库,那么只需创建PDO类的另一个实例(没有什么可以阻止您这样做!),或者创建另一个singleton。同样,这不是问题。

  4. 您允许SQL注入
    由于这只是PDO的一个包装,我想说PDO首先允许SQL注入。如果您不知道如何正确使用准备好的语句或引用查询参数,那么这不是我的错。

  5. (您允许SQL注入),因为它配置不好
    我在上面的代码中没有看到任何配置。只有一个使用PDO的数据库连接。

  6. 这很糟糕,因为这篇SO帖子这么说
    引用自维基百科:

Ipse dixit,拉丁语";他自己说;是一个用来识别和描述一种武断的教条式陈述的术语,说话者希望听众接受这种陈述。通过赤裸裸地断言一个命题"是"来为其辩护的谬论;就是这样";通过选择完全退出来扭曲论点:索赔人宣称一个问题是内在的,不能改变。

然而,SO上的其他帖子也为单身人士辩护。然而,最终重要的是保持开放的心态,而不仅仅是把自己困在教条主义的墙后面。