12

is there a way to traverse an object to get the parent object data? With "parent object" I don't mean the parent class, but literally object. Here an example, in a javascripty world :-) :

$parent->test = "hello world!";

$parent->child = new B();

It would be great If I could access all the data from parent in the child object:

class B{

//I know this doesn't exists, but it's what I wanted do do
    function B(){
        $this->parent = $this->parent();
        echo $this->parent->test; //it would ouput "hello world"
    }
}

For now my solution is to pass the parent object to the child (as a reference) or to make the parent global. Do you have any better solution?

Thanks!

Gordon
  • 312,688
  • 75
  • 539
  • 559
AntonioCorrenti
  • 148
  • 1
  • 1
  • 8
  • 1
    it's just two objects with references to each other. you may call them parent and child. – Anurag Mar 11 '10 at 08:12
  • Why do you need the child to have a reference to it's parent? Is there something in the parent that the child needs access too? If so you're better to pass in to the child anything it might need rather than trying to get it from the parent. – rojoca Mar 11 '10 at 08:12
  • I have in the parent a "privacy object", then some variables i need in the child, and passing them all would be tedious... – AntonioCorrenti Mar 11 '10 at 08:17
  • $child = new B($parent->privacy); would work? Does the instance of B() need to be a class attribute of $parent? – Greg K Mar 11 '10 at 08:26
  • Yes, but it's not enough: I need $child = new B($parent->user, $parent->privacy, $parent->url); Then for convenience I switched to $child = new B($parent); – AntonioCorrenti Mar 11 '10 at 08:30
  • You can avoid this situation if you follow the advice here: [http://misko.hevery.com/2008/08/01/circular-dependency-in-constructors-and-dependency-injection/](http://misko.hevery.com/2008/08/01/circular-dependency-in-constructors-and-dependency-injection/) – rojoca Mar 11 '10 at 08:39
  • i have recently stumbled across the same problem. i don't want the child to "extend" the parent, because the child has nothing to do with other already defined functions of its parent. so the only reasonable way for me seemd to inject parent during construction, like Gordon's solution number 1 suggested. i would really like to know though, if there's a language that natively supports this. having this->parent as the actual parent object container rather than parent class structure – user151496 May 25 '16 at 12:41

6 Answers6

24

There is no way to invoke

$parent->test = "hello world!";
$parent->child = new B();

and automatically have a reference to $parent in B.


Generally, there is four ways to structure your classes:

1. Aggregate the parent object via Injection, e.g.

class B
{
     private $parent;

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

     public function setParent($parent) 
     {
         $this->parent = $parent;
     }

     public function accessParent() 
     {
        $this->parent->someMethodInParent();
     }
}

Use constructor injection when the object has to have a parent when it's created. This is a has-a relationship and it creates a very loose coupling. There is no hardcoded dependencies in B, so you can easily swap out the Parent instance, for instance with a Mock when UnitTesting. Using Dependency Injection will make your code more maintainable.

In your UseCase, you'd pass $parent to B when creating B:

$parent->child = new B($parent);

2. Use Composition

class B
{
     private $parent;

     public function __construct() 
     {
         $this->parent = new Parent;
     }

     public function accessParent() 
     {
        $this->parent->someMethodInParent();
     }
}

This is also a has-a relationship, but couples the Parent class to B. It's also not an existing Parent instance, but a new instance. From the wording I find it somewhat odd to have a parent created by the child. Use this, when dependency is a class that is not considered to exist outside of the root class, but is part of the whole thing it represents.

For your UseCase, there is no way of doing $parent->child = new B(); and know what parent is when using this approach, unless $parent is a Singleton. If so, you could get the Singleton instance, e.g. Parent::getInstance() to achieve what you want, but note that Singletons are not everyone's favorite pattern, e.g. hard to test.

3. Use Inheritance

class B extends Parent 
{
    public function accessParent() 
    {
        $this->someMethodInParent();
    }
}

This way you create an is-a relationship. All public and protected methods and properties from the Parent class, (but not of a specific instance) will be available in B and you can access them via the $this keyword of the B instance.

For your UseCase, this approach is not working, as you don't have to have an instance of Parent at all, but B would encapsulate everything of Parent when it's created

$b = new B;

4. Use global keyword

class B extends Parent 
{
    private $parent;

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

    public function accessParent() 
    {
        $this->parent->someMethodInParent();
    }
}

The global keyword imports global variables into the current scope. In general, you should avoid using the global keyword in an OO context, but use one of the other three methods above, preferably the first one. While it's a language feature, it's frowned upon - although it is the next closest thing to the first one, e.g.

$parent->child = new B();

Anyway, hope that helps.

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
3

Passing the parent to the child is the better solution. Though it's a somewhat undesirable symbiotic relationship.

Should B() have any knowledge of the object it's an attribute of? More than likely not. These are only loosely related via composition.

Why does B() need to be an attribute of it's parent? Do you need to have this implementation in B() or should it be part of the parent?

Greg K
  • 10,770
  • 10
  • 45
  • 62
  • After a month of javascript, where traversing object is normal, I find php just too "static" in this case. Think about an mvc framework where some properties are defined on the way and not by default and at the end the main controller class calls a method that fires a child object creation. If I use the "extends" method by Sjoerd, I cannot then access the properties created on the way but just the properties defined on the top of the class. – AntonioCorrenti Mar 11 '10 at 08:26
  • So you need to pass around an object that holds the information you're building up? Maybe you should re-think this design to reduce coupling - would a dependency injection container help here? – Greg K Mar 11 '10 at 10:13
2

Maybe it can be useful in some case: it doesn't bring parent object to ChildClass very early in constructor, but a one step later. It plays with ability to intercept non-existing method:

class ParentClass
{
    const CHILD_PROPERTY_NAME = 'child';
    public $data = 'Some data';

    public function
    __set($property_name, $property_value)
    {
        if ($property_name == self::CHILD_PROPERTY_NAME)
        {
            $property_value->set_parent_object($this);
        }
    }
}


class ChildClass
{
    private $parent_object = null;

    public function
    set_parent_object($object)
    {
        $this->parent_object = $object;
        echo $this->parent_object->data;
    }

}


$p = new ParentClass();
$p->child = new ChildClass();

This will output Some data

Vladimir Fesko
  • 222
  • 2
  • 3
1

I'm pretty sure, that PHP does not have something like that.

Your solution of passing the parent-object to the child is the best solution, I think. You should consider to set your Parent-property only in the constructor of the child to prevent multiple parents from having the same child.

Hinek
  • 9,519
  • 12
  • 52
  • 74
0

No.

You can access variables from the superclass using $this:

<?php
class A {
    public function __construct()
    {
        $this->foo = 'hello';
    }
}

class B extends A {
    public function printFoo()
    {
        echo $this->foo;
    }
}

$b = new B();
$b->printFoo();
?>
Sjoerd
  • 74,049
  • 16
  • 131
  • 175
0

You can create collections of object children when you create the children. you create it trough the parent this way they cannot reference each other but you can access both parents and children trough the parent and trough the children...

class ChildObject
{
    public $name;
}

class ParentObject
{ 
    public $children = array(); 

    public function new_child($text = 'my name is data')
    {
        $child = new ChildObject;   // create a new child
        $child->name = $text;       // assign the name 
        $this->children[] = $child; // store it as part of this object
        return $child;              // return the new instance of Child
    }

    public function print_everything ()
    {
        foreach($this->children as $child) {
            echo $child->name;
        }
    }
}

$parent = new ParentObject;
$child1 = $parent->new_child();     // this is a ChildObject not a ParentObject
$child2 = $parent->new_child('i am not data');

echo $child1->name;                 // 'my name is data' 
echo $child2->name;                 // 'i am not data'

$child1->name = 'something else';
echo $child1->name;                 // 'something else'

$parent->print_everything();        // 'something else' and 'i am not data'

Therefore if you want to access a parent method or property, you should put that in the parent and not in the child. Remember that the parent is also an "instance" of its own class.

pbarney
  • 2,529
  • 4
  • 35
  • 49
Pere Noel
  • 9
  • 1