modify a method/function at runtime

2019-04-23 09:27发布

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?

7条回答
\"骚年 ilove
2楼-- · 2019-04-23 09:31

Just an idea:

function foo($bar, $preprocess, $postprocess){

    //IF CONDITION($preprocess)
    include('/mylogic/'.$preprocess.".php"); //do some preprocessing here?
    $foo = $bar ;
    //IF CONDITION($postprocess)
    include('/mylogic/'.$postprocess.".php"); //process $foo here?
    //FINALLY return
    return $foo;

}
查看更多
狗以群分
3楼-- · 2019-04-23 09:31

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.

查看更多
够拽才男人
4楼-- · 2019-04-23 09:33

I wanted to do the exact same thing, so I created a generic instrumentation class that would replace the class I was trying to instrument and leveraging @ircmaxell technique it would call through to the class to be instrumented.

<?php

class GenericClassInstrumentor
{
    private $instrumentedClass;
    private $className;

    public function __construct($instrumentedClass)
    {
        $this->instrumentedClass = $instrumentedClass;
        $this->className = get_class($instrumentedClass);
    }

    public function __call($name, $arguments)
    {
        $start = microtime(true);
        $result = call_user_func_array(array($this->instrumentedClass, $name), $arguments);
        $end = microtime(true);
        $duration = ($end - $start) * 1000;

        // optionally log something here
        return $result;
    }
}

Say you had an instance of a class like $myClass, that had function foo on it, then you would do something like this:

$myClass = new ClassWithFoo();
if ($instrumentationOn) {
    $myClass = new GenericClassInstrumentor($myClass);
}

Calling $myClass->foo($bar) would work the same (with the same caveats as @ircmaxell answer) if $instrumentationOn is true or false. If it is true it would just do the extra timing stuff.

查看更多
何必那么认真
5楼-- · 2019-04-23 09:35

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)...

查看更多
▲ chillily
6楼-- · 2019-04-23 09:36

Maybe I'm missing something, but do you really want to "inject" code as you say? What are you trying to achieve? If you simply want to execute one block of code when A happens, and another when B happens, then all you need is simple programming logic, like an if() statement.

Are you really trying to alter a function at runtime? Or is this just a logic problem?

Be more specific about what you need to do.

查看更多
Viruses.
7楼-- · 2019-04-23 09:40

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.

查看更多
登录 后发表回答