tags:

views:

213

answers:

5

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?

A: 

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 :-(

Pascal MARTIN
+4  A: 

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);
zombat
A: 

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.

Lucas Oman
+1  A: 

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 );
Peter Bailey
Brilliant! This works for my setup, above the other suggestions for adding pointer to the foo instance for the bar instance, because assuming many 'bar' instances, I don't want COPIES of the foo instance inside the bar instance (extra memory use, plus if the original $test instance changes something that modified the behavior of `magic_bullet`, all the copies would be outdated), I want a REFERENCE (`$this->pointer_to_foo = `, not `$this->pointer_to_foo = $foo`).
MidnightLightning
Pascal MARTIN
Haha, I don't know how I missed the PHP 5 style constructors, I just keyed off of the use of `var` and the lack of access/visibility modifiers on the methods. @MidnightLightning if you are actually using PHP 5, Pascal MARTIN is correct - you don't need the reference operator. Sorry for any confusion I created.
Peter Bailey
zombat
MidnightLightning
+1  A: 

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?

Mez
Excellent! Yes, based off what I gave, going with a Static Method or Singlton would solve getting `magic_bullet` visible to everyone. But in actuality, there's more than one function that 'foo' has that I want 'bar' to get access to, and rather than make all of them static, I'll go with a pointer solution from another answer
MidnightLightning
If the things in it don't need access to the bars... then a singleton might be the best idea. You'd then do something like Foo::getInstance()->any_of_your_functions($vars);
Mez