tags:

views:

78

answers:

3

Is it possible to rename a class method in PHP 5.2 during run time? Is it possible to do that using Reflection?

Given:

class Test
{
    public function myMethod()
    {
        echo 'in my method';
    }
}

I want to be able to rename myMethod() to oldMethod() so that later I do this:

$test = new Test();
$test->oldMethod(); // in my method
$test->myMethod(); // run time error: method does not exist
+2  A: 

Using Runkits runkit_method_rename you can do this.

nikic
You mean this URL: http://www.php.net/manual/en/book.classkit.php I'm looking into it right now.
Tom
The Installation page says "This extension is considered unmaintained and dead." I'm afraid of reading anything beyond this.
Tom
Sorry, edited my answer. I meant runkit.
nikic
Oh great, now I see a big red warning saying "EXPERIMENTAL" (yes, in caps). I guess this is better than anything deprecated, as this will probably be easier to maintain. Thanks!
Tom
+1  A: 

For all of you who are asking why you would need this, I have an idea that is very similar. My idea is to attempt to rename an entire PHP Class on the fly. It would be used, in my case, for an IRC Chat bot that I would have load and instantiate plugins on the fly, so that I would no need to reboot the bot and uptime would be very long. This would include renaming of pre-loaded classes of the same name as the class I would be attempting to load, so that there would be no conflict and it would run properly.

For example:

I have $bot running on irc.example.com

I have the plugin test.php installed and working, now when this is loaded into memory I can alter the file test.php without any change to $bot

So I update test.php

Now I want to cause it to load into $bot, but $bot already has a test load in it, and it would conflict if I attempted to include test.php again

so instead, we run a rename function to rename class test to class test[sha1 of a counter]

and then we include 'test.php'

$bot->test = new test();

and there we have it, an updated test plugin installed and loaded into memory on $bot with no reboot.

This is all theory, but it's something to think about before instantly flaming someone for their ideas with the "Why would you even need this" attitude.

I mean, let's be honest here. What are the odds that you're a super genius who knows everything there is to know about programming and would know what everyone would or wouldn't need?

Sincerely,

Anubis

Anubis
I think that they asked about the use case just because there aren't really a lot of cases in wich this is really required, and Tom could probably find a less radical approach than installing an experimentar module.Anyway, I'd like to do that too, just to inspect every call to a function, static class or object method in legacy code, without changing the source code a lot.
Sebastián Grignoli
@Anubis what you are trying to achieve can much better be handled with a [Strategy pattern](http://sourcemaking.com/design_patterns/strategy). See [my answer here at StackOverflow for an example](http://stackoverflow.com/questions/1957732/can-i-include-code-into-a-php-class/1957830#1957830).
Gordon
+2  A: 

From comment below question:

because I would like to install call event handlers on classes, without the classes knowing about it, so I would know that the user is calling a method from a class before that method is actually called.

Solution: use a Decorator.

class EventDecorator
{
    protected $_instance;
    public function __construct($instance)
    {
        $this->_instance = $instance;
    }
    public function __get($prop)
    {
        printf('Getting %s in %s', $prop, get_class($this->_instance));
        return $this->_instance->$prop;
    }
    public function __set($prop, $val)
    {
        printf('Setting %s with %s in %s',
            $prop, $val, get_class($this->_instance));
        return $this->_instance->$prop = $val;
    }
    public function __call($method, $args)
    {
        printf('Calling %s with %s in %s', 
            $method, var_export($args, TRUE), get_class($this->_instance));

        return call_user_func_array(array($this->_instance, $method), $args);
    }
}

Then you can wrap any class into it:

class Foo
{
    public $prop;
    public function doSomething() {}
}

$foo = new EventDecorator(new Foo);
$foo->prop = 'bar'; // Setting prop with bar in Foo    
$foo->prop;         // Getting prop in Foo
$foo->doSomething('something'); 
// Calling doSomething with array (0 => 'something',) in Foo

This can be fleshed out to provide pre and post hooks. You could also make the Decorator use the Subject/Observer Pattern and fire events to whatever other object registered to the decorator. The above approach is more maintainable and understandable than monkeypatching random methods with runkit.

Additional notes:

Gordon