tags:

views:

182

answers:

4

How do I add a new method to an object "on the fly"?

$me= new stdClass;
$me->doSomething=function ()
 {
    echo 'I\'ve done something';
 };
$me->doSomething();

//Fatal error: Call to undefined method stdClass::doSomething()
A: 

To see how to do this with eval, you can take a look at my PHP micro-framework, Halcyon, which is available on github. It's small enough that you should be able to figure it out without any problems - concentrate on the HalcyonClassMunger class.

ivans
I'm assuming (and so does my code) that you are running on PHP < 5.3.0 and thus need kludges and hacks to make this work.
ivans
Asking if anyone would like to comment on the downvote is pointless, I suppose...
ivans
It probably is, there is some mass downvoting going around here. But maybe you want to elaborate a bit on what you did? As you can see, this is an interesting question with no definite answer yet.
Pekka
+7  A: 

You can harness __call for this:

class Foo
{
    public function __call($method, $args)
    {
        if (isset($this->$method)) {
            $func = $this->$method;
            $func();
        }
    }
}

$foo = new Foo();
$foo->bar = function () { echo "Hello, this function is added at runtime"; };
$foo->bar();
karim79
Very, very clever but kludgy in a real world project IMO! (and +1 to counter anonymous downvoter)
Pekka
@pekka - but how, exactly, is it kludgy? Sure, I'm not saying I'm an expert at extending an object during runtime in PHP, but I honestly can't say I see much wrong with it. (maybe I just have poor taste)
karim79
@karim It's just the general feeling that it's not what `__call` was intended for, and it might be in use otherwise already. I would feel uneasy using this from a coding style point of view. On the other hand, it *is* elegant, can be extended with error checks (like when `$method` doesn't exist) and could be defined in some core class somewhere and then passed on to all objects.
Pekka
@Pekka - I actually agree with you now that I think about it. From what I can see, it is kludgy due to its reliance on a method whose sole purpose is to get 'triggered when invoking inaccessible methods in an object context.'. From that perspective, I suppose it is kludgy.
karim79
@karim yup, but I suppose depending on the situation, it may be justifiable to use and better than the alternatives. However as I just discovered (see my update), both our approaches have a lot of side effects because the function added that way is not a fully qualified member of the class. (Try using `$this` in it.) This is making me think it's best not to use this at all at this moment in PHP - maybe PHP 6 adds this natively.
Pekka
I would add a is_callable check in that if as well (So you don't accidentally try to call a string). And also throw a BadMethodCallException() if you can't find a method, so you don't have it return and think it did return successfully. Also, make the first param of the function the object itself, and then do an `array_unshift($args, $this);` before the method call, so that the function gets a reference to the object without explicitly needing to bind it...
ircmaxell
@ircamaxell - +1, all excellent suggestions.
karim79
+5  A: 

Update: The approach shown here has a major shortcoming: The new function is not a fully qualified member of the class; $this is not present in the method when invoked this way. This means that you would have to pass the object to the function as a parameter if you want to work with data or functions from the object instance! Also, you will not be able to access private or protected members of the class from these functions.

Good question and clever idea using the new anonymous functions!

Interestingly, this works: Replace

$me->doSomething();    // Doesn't work

by call_user_func on the function itself:

call_user_func($me->doSomething);    // Works!

what doesn't work is the "right" way:

call_user_func(array($me, "doSomething"));   // Doesn't work

if called that way, PHP requires the method to be declared in the class definition.

Is this a private / public / protected visibility issue?

Update: Nope. It's impossible to call the function the normal way even from within the class, so this is not a visibility issue. Passing the actual function to call_user_func() is the only way I can seem to make this work.

Pekka
A: 

There's a similar post on stackoverflow that clears out that this is only achievable through the implementation of certain design patterns.

The only other way is through the use of classkit, an experimental php extension. (also in the post)

Yes it is possible to add a method to a PHP class after it is defined. You want to use classkit, which is an "experimental" extension. It appears that this extension isn't enabled by default however, so it depends on if you can compile a custom PHP binary or load PHP DLLs if on windows (for instance Dreamhost does allow custom PHP binaries, and they're pretty easy to setup).

zilverdistel