Home » Php » Reference a method of container object in PHP?

Reference a method of container object in PHP?

Posted by: admin July 12, 2020 Leave a comment

Questions:

Given the following in PHP:

<?php
class foo {
  public $bar;
  function __construct() {
    "Foo Exists!";
  }

  function magic_bullet($id) {
    switch($id) {
    case 1:
      echo "There is no spoon! ";
    case 2:
      echo "Or is there... ";
      break;
    }
  }
}

class bar {
  function __construct() {
    echo "Bar exists";
  }
  function target($id) {
    echo "I want a magic bullet for this ID!";
  }
}

$test = new foo();
$test->bar = new bar();
$test->bar->target(42);

I’m wondering if it’s possible for the ‘bar’ class to call the ‘magic bullet’ method of the ‘foo’ class. The ‘bar’ instance is contained by the ‘foo’ instance, but is not in a parent/child relationship with it. In actuality, I’ve got many various “bar” classes that “foo” has in an array, each doing something different to $id before wanting to pass it off to the “magic_bullet” function for an end result, so barring a structure change of the class relations, is it possible to access a method of a ‘container’ instance?

How to&Answers:

You have to modify your code to provide a relationship. in OOP-speak, we call this aggregation.

Assuming PHP 4, and the idea of “an array of bars”

<?php

class foo {
  var $bars = array();
  function __construct() {
    "Foo Exists!";
  }

  function magic_bullet($id) {
    switch($id) {
    case 1:
      echo "There is no spoon! ";
    case 2:
      echo "Or is there... ";
      break;
    }
  }

  function addBar( &$bar )
  {
    $bar->setFoo( $this );
    $this->bars[] = &$bar;
  }
}

class bar {
  var $foo;
  function __construct() {
    echo "Bar exists";
  }

  function target($id){
    if ( isset( $this->foo ) )
    {
      echo $this->foo->magic_bullet( $id );
    } else {
      trigger_error( 'There is no foo!', E_USER_ERROR );
    }
  }
  function setFoo( &$foo )
  {
    $this->foo = &$foo;
  }
}

$test = new foo();
$bar1 = new bar();
$bar2 = new bar();

$test->addBar( $bar1 );
$test->addBar( $bar2 );

$bar1->target( 1 );
$bar1->target( 2 );

Answer:

No, it’s not possible, as there is no relationship defined.

I’d suggest that instead of setting the $test->bar property directly, you use a setter, and you can establish a relationship that way. You’ll also need to add a container property to the bar class. Inside class foo:

function setBar(bar $bar)
{
    $bar->container = $this;
    $this->bar = $bar;
}

That sets up a relationship between the objects. Now change the bar::target($id) function to:

function target($id)
{
    $this->container->magic_bullet($id);
}

You should be able to do this now:

$test = new foo();
$bar = new bar();
$test->setBar($bar);
$test->bar->target(42);

Answer:

This looks to me (though only based on the code you’ve shown!) to be a perfect case for a static Method…

<?php
class foo {
  var $bar;
  function __construct() {
    "Foo Exists!";
  }

  static function magic_bullet($id) {
    switch($id) {
    case 1:
      echo "There is no spoon! ";
    case 2:
      echo "Or is there... ";
      break;
    }
  }
}

class bar {
  function __construct() {
    echo "Bar exists";
  }
  function target($id) {
    echo Foo::magic_bullet($id)
  }
}

$test = new foo();
$test->bar = new bar();
$test->bar->target(42);

Or, if there’s only ever going to be one “Foo” Object – maybe a Singleton Design pattern?

Answer:

You $bar instance, inside of Foo, has no way to know what the class containing it is ; so, no way to call any method of that one.

(Well, maybe (probably not) working with Reflection… I should try that, one day ^^ )

EDIT : thinking a bit about it : you could, inside your bar class, keep a pointer to foo ; a bit like this :

class foo {
  var $bar;
  function __construct() {
    var_dump("Foo Exists!");
  }

  function magic_bullet($id) {
      var_dump('magic_bullet : ' . $id);
    switch($id) {
    case 1:
      var_dump("There is no spoon! ");
    case 2:
      var_dump("Or is there... ");
      break;
    }
  }
}

class bar {
  protected $pointer_to_foo;
  function __construct($pointer_to_foo) {
    var_dump("Bar exists");
    $this->pointer_to_foo = $pointer_to_foo;
  }
  function target($id) {
    var_dump("I want a magic bullet for this ID!");
    if (method_exists($this->pointer_to_foo, 'magic_bullet')) {
        $this->pointer_to_foo->magic_bullet($id);
    }
  }
}

$test = new foo();
$test->bar = new bar($test);
$test->bar->target(42);

Notice I used method_exists as a security measure.

And you’ll get :

string 'Foo Exists!' (length=11)

string 'Bar exists' (length=10)

string 'I want a magic bullet for this ID!' (length=34)

string 'magic_bullet : 42' (length=17)

So, it can be done… If you modify your code a bit 😉

Edit 2 : ho, zombat beat me to it, it seems 🙁

Answer:

You have a couple options here. I would recommend against going the global route.

$test->bar = new bar($test);

This would work if you then store $test in bar’s constructor. You could also:

class test {
...
  public function target($someInt) {
    $this->bar->target($someInt,$this);
  }
}
$test->target(42);

… and use the given test object in bar’s target() method.