views:

640

answers:

4

Hi guys, we have a problem [cit.]

I need to assign a callback dynamically within a class, in base of a variable param: my goal is to have just one class (and not a main class and many extender sub-class), and inside this class if a value is X, then the funcitonX must be used, if is Y, the functionY.

I know i cant explain well, i hope my example will do:

class plzComplicateMyLife{
    public $vehicle;
    public $kindVehicle;
    public $dynamicFunction;
    public function __construct($vehicle, $kindVehicle){
    $this->kindVehicle = $kindVehicle;
    $this->vehicle = $vehicle;
    switch($kindVehicle){
        case 'cycle':
            $this->dynamicFunction = "isACycle";
            break;
        case 'car':
            $this->dynamicFunction = "isACar";
            break;
    }
    //here come the problem, i need to call the callback store in dynamicFunction.
    //i tried:
    //call_user_func($this->$this->dinamicFunction, $this->vehicle);
    //error: Catchable fatal error: Object of class plzComplicateMyLife could not be converted to string in [...]
    //call_user_func("plzComplicateMyLife::".$this->dynamicFunction);
    //Warning: call_user_func(plzComplicateMyLife::isACar) [function.call-user-func]: First argument is expected to be a valid callback in [...]
    //$this->dynamicFunction();
    //Fatal error: Call to undefined method plzComplicateMyLife::dynamicFunction() in [...]
    //so, how can i do that?
    }
    public function isACycle($vehicle){
        echo 'im a cycle, model: '.$vehicle.'<br />';
    }
    public function isACar($vehicle){
        echo 'im a car, model: '.$vehicle.'<br />';
    }
    //i know this has no sense, in this example at least.
    public function printKind(){
        //call_user_func($this->$this->dinamicFunction, $this->vehicle);
        //call_user_func("plzComplicateMyLife::".$this->dynamicFunction);
        //then?
    }
}

$maserati = new plzComplicateMyLife('maserati4', 'car');

//then, maybe, outside the class i'll need to recover the callback:

$maserati->printKind();

EDIT:

As Rob said, polymorphism would be really a good solution.

But the problem is that, in this case, i really must have the same declaration for every class instance, changing only the parameters...e.g:

$maserati = new plzComplicateMyLife('maserati4', 'car');
$ducati = new plzComplicateMyLife('maserati4', 'cycle');
//is good
//becose i cant have:
$maserati = new plzComplicateMyLifeWithACar('maserati4');
$ducati = new plzComplicateMyLifeWithACycle('maserati4');
+1  A: 

In PHP you can use specify a method callback using an array as a callback variable (see here), for example:

array( $object, $methodName );

So you could do this

$callback = array($this, $this->dynamicFunction);
call_user_func($callback, $this->vehicle);
Tom Haigh
+1  A: 

Er, why don't you want to use a simple inheritance structure here? If you want different behaviour depending upon the object modelled, then that's pretty much the canonical description of polymorphism.

If you really do want to plough on with callbacks into the same object, then you'll need to do one of two things:

  1. Drop the $vehicle parameter from your callbacks, make them private or protected, and call into them normally, i.e.

    call_user_func( array( $this, 'isACycle' ) );

  2. Mark the callback as static, make them private or protected, and call into them as follows:

    call_user_func( array( __CLASS__, 'isACycle' ), $this );

Within the non-static callback, access the object's properties via $this in the normal fashion. Note also that I suggest marking the callback as private or protected, in order to prevent unnecessary outside callers; presumably, you don't want them executing the wrong method for each type.

Rob
I just edited the post with the why i cant use polymorphism..
DaNieL
+1  A: 

In response to your edit, could you not do something like this instead?

abstract class MethodOfTransport {
    protected $model;

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

    abstract public function printKind();

    public static function create($model, $type) {
       $object = new $type($model);
       return $object;
    }
}

class cycle extends MethodOfTransport {
    public function printKind() {
        echo 'im a cycle, model: '.$this->model.'<br />';
    }
}

class car extends MethodOfTransport {
    public function printKind() {
        echo 'im a car, model: '.$this->model.'<br />';
    }
}

$maserati = MethodOfTransport::create('maserati4', 'car');
$maserati->printKind();

$ducati =  MethodOfTransport::create('maserati4', 'cycle');
$ducati->printKind();
Tom Haigh
Yes'n its exacle what i'll do!Thanks, mate!
DaNieL
I didn't try it, but am I wrong in saying that if each of these classes were defined in separate source files, then then base class would have to include the extended class definition files, while at the same time, the extended classes would have to include the base class? (The only reason I'm unsure is because of the use of `new $type($model)`.
grantwparks
The extended classes would only need to include the base class if not already included. The base class could be responsible for including the extended class files before instantiating them; that would be all you need to do. Alternatively you could look at spl_autoload_register()
Tom Haigh
+1  A: 

Polymorphism is the way to go here but for future reference you can also do this:

public function printKind() {
    $this->{$this->dynamicFunction}($this->vehicle);
}
rojoca