一尘不染

在类中使用全局变量

php

我正在尝试创建一个分页类,并使用该类外部的变量。

但这给了我致命错误“在非对象上调用成员函数query()”。

这是索引文件:

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new pagi();
$records = $pagination->get_records("SELECT * FROM `table`");

这是pagi.php文件:

class pagi {

    public function get_records($q) {
        $x = $db->query($q);
        return $db->fetch($x);
    }

}

是否可以在类内部使用该变量,而无需在类内部创建新变量?


阅读 1473

收藏
2020-05-26

共1个答案

一尘不染

解决此问题的正确方法是将数据库对象注入另一个类(依赖项注入):

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new Paginator($db);
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`");

class Paginator
{    
    protected $db;

    // Might be better to use some generic db interface as typehint when available
    public function __construct(DB_MySQL $db)
    {
        $this->db = $db;
    }

    public function get_records($q) {
        $x = $this->db->query($q);
        return $this->db->fetch($x);
    }

}

解决该问题的另一种方法是将数据库类的实例注入使用它的方法中:

$db = new DB_MySQL("localhost", "root", "", "test"); // connect to the database
include_once("pagi.php");

$pagination = new Paginator();
$records = $pagination->get_records("SELECT the, fields, you, want, to retrieve FROM `table`", $db);

class Paginator
{
    public function get_records($q, DB_MySQL $db) {
        $x = $db->query($q);
        return $db->fetch($x);
    }

}

您选择哪种方法取决于情况。如果只有一个方法需要数据库的实例,则可以将其注入该方法,否则我会将其注入到类的构造函数中。

另请注意,我已将您的班级从重命名pagiPaginator。Paginator是该类的更好的名字恕我直言,因为其他人(重新)查看您的代码很明显。另请注意,我已将首字母大写。

我所做的另一件事是更改查询以选择要使用的字段,而不是使用“通配符”
*。出于同样的原因,我也更改了类名:(重新)查看代码的人员将确切知道将检索哪些字段,而无需检查数据库和/或结果。

更新资料

因为答案引起了关于为什么我要走依赖注入途径而不是声明对象的讨论,所以我想global阐明为什么我要对global关键字使用依赖注入:当您有如下方法时:

function get_records($q) {
    global $db;

    $x = $db->query($q);
    return $db->fetch($x);
}

当您在某处使用上述方法时,不清楚使用的类或方法取决于$db。因此,这是一个隐藏的依赖关系。上面的代码不好的另一个原因是因为您已经将$db实例(因此DB_MySQL)类与该方法/类紧密耦合。如果您需要在某个时候使用2个数据库怎么办。现在,您将需要遍历所有代码才能更改global $dbglobal $db2。您永远不必为了切换到另一个数据库而更改代码。因此,您不应该这样做:

function get_records($q) {
    $db = new DB_MySQL("localhost", "root", "", "test");

    $x = $db->query($q);
    return $db->fetch($x);
}

同样,这是一个隐藏的依赖关系,并将DB_MySQL类与方法/类紧密耦合。因此,不可能正确地对Paginator类进行单元测试。Paginator您不仅要同时测试单元(课程),还要同时测试DB_MySQL课程。如果您有多个紧密耦合的依赖关系,该怎么办?现在,您突然用所谓的单元测试测试了几个类。因此,在使用依赖注入时,您可以轻松切换到另一个数据库类,甚至可以切换到一个模拟类来进行测试。除了只测试一个单元的好处(您不必担心由于依赖性而导致的错误结果)之外,它还可以确保测试能够快速完成。

某些人可能认为Singleton模式是访问数据库对象的正确方法,但是应该很清楚,在阅读了以上所有内容之后,Singleton基本上只是另一种制作方法global。它的外观可能有所不同,但具有与相同的特征,因此也存在相同的问题global

2020-05-26