Reputation: 3387
I'm writing some PHP code in which one object (a "Container") keeps a pointer to another object (the "Contents"). The problem is that the contents needs to access methods or properties of the container.
Here's a simplified example of what I want to do:
class Container {
function __construct($type, $contents) {
$this->type = $type;
$this->contents = $contents;
}
function display() {
return $this->contents->display();
}
}
class Contents {
function __construct($stuff) {
$this->stuff = $stuff;
}
function display() {
return 'I am ' . $this->stuff . ' in '; // how to access Container here?
}
}
$item = new Container('a can', new Contents('Prince Albert'));
echo $item->display() . "\n";
// Displays: I am Prince Albert in
// Wanted: I am Prince Albert in a can
What's the right way to do this?
I've tried a couple of methods that work, but they feel wrong. For example:
Re-defined Contents::display()
to take a parameter, which doesn't seem elegant:
function display($container) {
return 'I am ' . $this->stuff . ' in ' . $container->type;
}
In Contents::display()
, I called debug_backtrace(true)
to find out what called it, then access the object from the backtrace info. That feels kludgy/dangerous.
Is there a common solution for this kind of problem?
Upvotes: 4
Views: 1132
Reputation:
Try this:
class Container
{
protected $type;
protected $contents;
function __construct($type, Contents $contents)
{
$this->type = $type;
$this->contents = $contents;
$contents->setContainer($this);
}
function display()
{
return $this->contents->display();
}
public function getType()
{
return $this->type;
}
}
class Contents
{
/** @var Container */
protected $container;
function __construct($stuff)
{
$this->stuff = $stuff;
}
public function setContainer(Container $container)
{
$this->container = $container;
}
function display()
{
return 'I am '.$this->stuff.' in '.$this->container->getType(); // how to access Container here?
}
}
$item = new Container('a can', new Contents('Prince Albert'));
echo $item->display()."\n";
// Displays: I am Prince Albert in
// Wanted: I am Prince Albert in a can
And as advice: write public/protected/private for each method and variables, don't use public properties. If you don't know why, read this book: http://www.amazon.com/Objects-Patterns-Practice-Matt-Zandstra/dp/1590599098
Upvotes: 1
Reputation: 131841
At all there are two common solution. The one is the first one you already mention
class A {
public function doSomething ($outer) { /* code */ }
}
where $outer
is your container. Or you strictly bind the content objects to the container
class A {
private $outer;
public function __construct ($outer) {
$this->outer = $outer;
}
}
Upvotes: 4
Reputation: 284786
With dependency injection, you would construct the Container first (not passing in a Contents):
class Container {
function __construct($type) {
Then, you would pass the Container to the Contents constructor:
class Contents {
function __construct($stuff, $container) {
Since the reference is mutual, you would have to call a setter on container:
class Container {
function setContents($contents)
Upvotes: 1