在运行时修改方法/函数

时间:2010-05-28 18:56:29

标签: php reflection

我一直在看php反射方法,我想要做的是在方法打开之后和任何返回值之前注入一些代码,例如我想要改变:

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

并将一些代码注入其中:

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

可能?

7 个答案:

答案 0 :(得分:5)

嗯,一种方法是将所有方法调用设为“虚拟”:

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";
}

所有PHP:

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

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

它将对象的实例作为第一个方法传递给“覆盖”。注意:此“覆盖”方法将无法访问该类的任何受保护成员。因此,请使用getter(__get__set)。它可以访问受保护的方法,因为实际的调用来自__call() ...

注意:您需要修改所有默认方法,并以“_”为前缀,以便工作...(或者您可以选择其他前缀选项,或者您可以范围他们都受到保护)...

答案 1 :(得分:2)

查看anonymous functions。如果您可以运行PHP 5.3,那可能更符合您的尝试。

答案 2 :(得分:2)

这是不可能的,至少不是你想要的方式。正如评论所建议的那样,反思是获取有关类/函数的信息,而不是修改它们。

有一个名为Runkit的PHP扩展,我认为它提供了这种类型的功能 - http://www.php.net/manual/en/book.runkit.php,但是默认情况下绝大多数主机都不会安装它。

虽然可能有不同的方法。也许如果你可以提供一些关于你想要做什么的更多信息,以及为什么你不能修改有问题的函数,我们也许可以提供一些指示。例如。是你想要修改核心PHP函数的函数,还是你不想编辑的第三方库中的代码?

答案 3 :(得分:0)

只是一个想法:

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;

}

答案 4 :(得分:0)

您可以在原始自动加载器之前添加一个特殊的自动加载器,读取原始自动加载将加载的文件,更改内容,将该文件保存在其他位置(例如某些tmp目录)并包含该修改后的文件而不是原始文件

答案 5 :(得分:0)

我想做同样的事情,所以我创建了一个通用的检测类,它将取代我试图检测的类,并利用@ircmaxell技术,它将调用到要检测的类。

<?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;
    }
}

假设您有一个像$myClass这样的类的实例,其上有函数foo,那么您可以这样做:

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

如果$myClass->foo($bar)$instrumentationOntrue,则呼叫false的工作方式相同(与@ircmaxell回答相同)。如果它是true,它只会做额外的计时。

答案 6 :(得分:-1)

也许我错过了一些东西,但是你真的想像你说的那样“注入”代码吗?你想要实现什么目标?如果你只想在A发生时执行一个代码块,而在B发生时再执行另一个代码块,那么你只需要简单的编程逻辑,就像if()语句一样。

你真的想在运行时改变一个功能吗?或者这只是一个逻辑问题?

更具体地说明您需要做什么。