【问题标题】:Passing $db object to other classes so they can access the database将 $db 对象传递给其他类,以便它们可以访问数据库
【发布时间】:2011-07-09 16:31:19
【问题描述】:

我有一个 PHP 数据库类,它连接到 MySQL 并包装了所有 PDO 代码,我用它来查询数据库。基本上在页面控制器中我创建了一个新对象:

$db = new Database($dbConfig);

然后我可以使用准备好的查询从数据库中获取数据:

$params = array('username' => $username);
$result = $db->preparedSelect('select password, salt from users where username = :username', $params);

它将 PDO 语句结果复制到一个新的 assoc 数组中,并仅将数据库结果返回到调用页面。我用一个简单的 foreach 遍历它们,如下所示:

foreach ($result as $key => $val)
{
   $password = $val['password'];
   $salt = $val['salt'];
}

好吧,假设我希望另一个类使用我的 $db 对象,以便它可以通过某些方法访问数据库。目前其他类看起来像这样:

class General
{
    // Database object
    private $db;

    public function __construct($db)
    {
        $this->db = $db;
    }
}

效果很好,但我只是想知道构造函数是否应该如下所示:

public function __construct(&$db)
{
    $this->db = $db;
}

这应该意味着我通过引用传递它而不是将对象复制到另一个类中。我不想要类中的 $db 对象的副本,我希望它使用现有的数据库对象,这样我就不会有它的多个副本漂浮在内存中。

在 PHP5 中将其作为 $db 或 &$db 传递有什么区别?通过阅读,PHP5 默认通过引用传递对象,而其他人说它现在以 Java 方式进行,有些人说使用 & 会产生硬链接。我糊涂了。最好的方法是什么?

非常感谢!

【问题讨论】:

    标签: php database oop pass-by-reference


    【解决方案1】:

    有区别,但并不是你想象的那样。

    在 PHP5 中,保存对象的“$db”基本上等同于 C 或 C++ 中的“Foo *”。换句话说,$db 不存储整个对象,它只存储一个小标记,让代码在必要时找到对象。当您按值传递此标记时,它与传递整数值而不是整个对象的副本一样快。但是,如果您分配 $db,它不会更改调用者中的值,因为您正在更改保存令牌的本地变量以包含不同的令牌。

    如果函数采用“&$db”,那基本上相当于在 C 中传递“Foo **”,或者更准确地说,函数采用 C++ 中的“Foo *&”。调用速度一样快,因为它传递的大小相同,但在函数内部,如果你分配给 $db 它将改变调用者中 $db 的值,因为“通过引用传递”变量将你指向内存在调用者中持有令牌的位置。

    最好的方法是按值传递(不要使用“&”),除非你知道自己在做什么以及为什么这样做。

    【讨论】:

    • 所以基本上如果我执行 _construct($db) 然后更改 $db 对象中的值,例如$db->testvar = 1 我将在内存中有两个 $db 对象,原始的和新的,其中 testvar 包含一个“1”。那正确吗?如果我执行 _construct(&$db) 然后分配 $db->testvar = 1,我应该会影响原始对象并且在内存中只有一个 $db 对象实例。对吗?
    • 不,在这两种情况下,您都会拥有一个具有更改值的对象,因为在这两种情况下都不会复制对象本身。在第一个函数中,如果您执行$db = null,调用者仍将拥有原来的 $db,而在第二个函数中,调用者将不再拥有原来的 $db。
    【解决方案2】:

    这是个好问题。

    您始终可以通过打开 $db 句柄、将其传递给函数并通过 === 运算符检查它们以确保它们是同一个对象来进行测试。

    【讨论】:

    • 我做了一个测试,它们似乎是完全相同的对象。我认为您必须使用 clone $db 来实际制作原始 $db 对象的副本。否则,您从另一个类中对 $db 对象所做的任何更改都会更新原始对象。
    【解决方案3】:

    这对于静态方法来说是件好事。这就是完成相同任务的框架的数量。

    class DB
    {
       private static $db = FALSE:
    
       public static function init($dbConfig)
       {
          if(! self:$db)
          {
             self::$db = new Database($dbConfig);
          }
       }
    
       public static function preparedSelect($sql, $params)
       {
          if(! self::$db)
          {
             die("call the init method first");
          }
    
          // db stuff, where you would call $this->db call self::$db
       }
    }
    

    因此,在您想要调用数据库的其他类中,您所要做的就是:

    class General
    {
       public function __construct()
       {
          DB::init($dbConfig);
       }
    
       public function someMethod()
       {
          $params = array('username' => $username);
          $result = DB::preparedSelect('select password, salt from users where username = :username', $params);
    
       }
    }
    

    【讨论】:

    • 我正打算为此使用静态方法,但经过研究表明,像 General 类(当前是 DB)这样的依赖关系并不立即明显 - 并且可能会导致混乱。不应该以这种方式使用静态方法/单例 - 而应该使用 依赖注入 / 将变量显式传递给构造函数,以便可以准确地看到类依赖于哪些类:)
    猜你喜欢
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 2015-06-21
    相关资源
    最近更新 更多