modify a method/function at runtime

16,178

Solution 1

Look into anonymous functions. If you can run PHP 5.3 that might be more along the lines of what you're trying to do.

Solution 2

Well, one way, would be to make all the method calls "virtual":

class Foo {
    protected $overrides = array();

    public function __call($func, $args) {
        $func = strtolower($func);
        if (isset($this->overrides[$func])) {
            // We have a override for this method, call it instead!
            array_unshift($args, $this); //Add the object to the argument list as the first
            return call_user_func_array($this->overrides[$func], $args);
        } elseif (is_callable(array($this, '_' . $func))) {
            // Call an "internal" version
            return call_user_func_array(array($this, '_' . $func), $args);
        } else {
            throw new BadMethodCallException('Method '.$func.' Does Not Exist');
        }
    }

    public function addOverride($name, $callback) { 
        $this->overrides[strtolower($name)] = $callback;
    }

    public function _doSomething($foo) {
        echo "Foo: ". $foo;
    }
}

$foo = new Foo();

$foo->doSomething('test'); // Foo: test

PHP 5.2:

$f = create_function('$obj, $str', 'echo "Bar: " . $obj->_doSomething($str) . " Baz";');

PHP 5.3:

$f = function($obj, $str) {
    echo "Bar: " . $obj->_doSomething($str) . " Baz";
}

All PHP:

$foo->addOverride('doSomething', $f);

$foo->doSomething('test'); // Bar: Foo: test Baz

It passes the instance of the object as the first method to the "override". Note: This "overriden" method will not have access to any protected members of the class. So use getters (__get, __set). It WILL have access to protected methods, since the actual call is coming from __call()...

Note: You'll need to modify all your default methods to be prefixed with an '_' for this to work... (or you can chose another prefix option, or you can just scope them all protected)...

Solution 3

This isn't possible, at least not in the way you are after. As the comment suggested, reflection is for getting information about classes/functions, not modifying them.

There is a PHP extension called Runkit which I believe provides this type of functionality - http://www.php.net/manual/en/book.runkit.php, however this isn't going to be installed by default on the vast majority of hosts out there.

There may be a different way of doing this though. Perhaps if you could give some more info on what you're trying to do, and why you can't modify the function in question, we might be able to provide some pointers. E.g. is the function you want to modify a core PHP function, or code in a third party library which you don't want to edit?

Solution 4

You could add a special autoloader before the original autoloader, read the file that your original autoload would load, change the content, save that file somewhere else (some tmp dir for example) and include that modified file instead of the original one.

Share:
16,178

Related videos on Youtube

mononym
Author by

mononym

Updated on April 23, 2022

Comments

  • mononym
    mononym about 2 years

    I've been looking at the php reflection methods, what i want to do is inject some code after the method is opened and before any return value, for example i want to change:

    function foo($bar)
    {
        $foo = $bar ;
        return $foo ;
    }
    

    And inject some code into it like:

    function foo($bar)
    {
        //some code here
        $foo = $bar ;
        //some code here
        return $foo ;
    }
    

    possible?

    • mononym
      mononym almost 14 years
      thanks for the replys guys, basically i'm trying to enable profiling of each function without having to use a profiling extension (xdebug, xhprof, apd). As part of a framework i have developed. so the 2 insertions would have been for timing, memory usage and adding to a session variable so i can output data inline. not needing to have to process cachegrind files etc.
  • grossvogel
    grossvogel almost 14 years
    Good idea... though i'd probably make $preprocess and $postprocess into callbacks ('functionname' or array('classname','methodname')) and use call_user_func to call them. it would have scoping implications, but that would also make it safer IMHO.
  • mononym
    mononym almost 14 years
    it needs to be done for all methods in my application, and i want it to remain clean
  • NickSoft
    NickSoft about 11 years
    runkit is beta at the time I posted this comment and it doesn't compile (gives error when trying to compile on linux with php 5.3.19)