Question

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();

Right now, If I want to access the root object (instance of Project class, that is) from within the Header scope, I have to remember the name I picked for the instance (which could vary) and reference to it by using the global keyword. But I feel like this is just a quick fix. I need a reliable method of accessing the root object from which the current one (along with its parents) was constructed.

In other words, I will need to access $app inside $app->modules->header giving the fact that the name app itself is variable and the length of the chain is also dynamic.

I can access the parent's namespace parent:: but it would've been nice to have something like first_ancestor::.

Was it helpful?

Solution

So, in the spirit of answering the question, of course there's a way (warning: here be dragons):

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;
}

Which can be used as so:

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

Now, this does what you ask. But please, under NO CIRCUMSTANCES should you actually do that.

Seriously.

I cannot stress how bad it would be if you were doing that.

It's fragile.

And dirty.

And relies on debug functionality.

And hard-coded relationships between construction order.

And other garbage.

Real Answer:

Instead, refactor to accept explicit dependencies:

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);
    }
}

Your dependencies are explicit, and everything is clear as to what's happening where.

Even Better Yet

Even better yet, remove New from the equation entirely:

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);

Now, your code itself will be completely isolated, and not depend on anything. You can override each dependency as needed (say for testing, when you want to inject a fake project into Header())...

This is called Dependency Injection.

Seriously, don't use the first method. It was more of a joke than anything (to show what the power of PHP is, but good lord, no, that's evil...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top