如何在PHP中跟踪类链中的根对象

时间:2014-04-22 19:25:48

标签: php oop inheritance chain

class Header
{
    public function __construct()
    {
        global $app;
        print($app->config);
    }
}

class Modules
{
    public function __construct()
    {
        $this->header = new Header();
    }
}

class Project
{
    public $config = "config";

    public function __construct()
    {
        $this->modules = new Modules();
    }
}

$app = new Project();

现在,如果我想从Header范围内访问根对象(即Project类的实例),我必须记住我为实例选择的名称(可能会有所不同)和使用global关键字引用它。但我觉得这只是一个快速修复。我需要一种可靠的方法来访问根对象,从中构造当前对象(及其父对象)。

换句话说,我需要访问$app内的$app->modules->header,因为名称app本身是可变的,而且链的长度也是动态的。

我可以访问父级的parent::名称空间,但拥有first_ancestor::之类的内容会很高兴。

1 个答案:

答案 0 :(得分:12)

所以,本着回答这个问题的精神,当然有一种方式(警告:这里是龙):

function YouAreAnIdiotIfYouDoThisForReal($skip = 0) {
    $bt = debug_backtrace();
    if (isset($bt[1]['function']) && $bt[1]['function'] === "__construct") {
        // called from constructor, so skip it!
        $skip++;
    }
    foreach ($bt as $stack) {
        if (isset($stack['function']) && $stack['function'] === "__construct" && $skip-- <= 0) {
            return $stack['object'];
        }
    }
    return null;
}

可以这样使用:

class Header
{
    public function __construct()
    {
        $this->project = YouAreAnIdiotIfYouDoThisForReal(1);
        $this->modules = YouAreAnIdiotIfYouDoThisForReal();
    }
}

现在,这就是你所要求的。但是,请在没有情况下你真的应该这样做。

严重。

我不能强调如果你这样做会有多糟糕。

它很脆弱。

肮脏。

依赖于调试功能。

施工顺序之间的硬编码关系。

和其他垃圾一样。

真实答案:

相反,重构接受显式依赖:

class Header
{
    public function __construct(Project $project, Modules $modules)
    {
        $this->project = $project;
        $this->modules = $modules;
    }
}

class Modules
{
    public function __construct(Project $project)
    {
        $this->project = $project;
        $this->header = new Header($project, $this);
    }
}

class Project
{
    public $config = "config";

    public function __construct()
    {
        $this->modules = new Modules($this);
    }
}

您的依赖关系是明确的,并且一切都清楚地知道发生了什么。

甚至更好

更好的是,完全从等式中删除New:

class Header
{
    public function __construct(Project $project)
    {
        $this->project = $project;
    }
}

class Modules
{
    public function __construct(Header $header)
    {
        $this->header = $header;
    }
}

class Project
{
    public $config = "config";

    public function __construct()
    {
    }

    public function setModules(Modules $modules) {
        $this->modules = $modules;
    }
}

$project = new Project();
$headers = new Header($project);
$modules = new Modules($headers);
$project->setModules($modules);

现在,您的代码本身将完全隔离,而不依赖于任何东西。您可以根据需要覆盖每个依赖项(例如,当您想要将假项目注入Header()时进行测试)...

这称为Dependency Injection

说真的,不要使用第一种方法。它更像是一个笑话而不是任何东西(展示PHP的力量,但是好主,不,那是邪恶......