php在运行时创建类方法

时间:2012-06-11 18:56:31

标签: php reflection

我想知道是否有办法在运行时将新方法附加到类中。 我的意思是,不是在实例级别而是直接到类,所以所有新创建的实例都有这个新方法。 可以用反射完成这样的事情吗?

由于

5 个答案:

答案 0 :(得分:21)

是的,你可以。

下面是在php 5.4.x中运行时创建方法的方法。

匿名函数由从5.3.x开始的Closure类表示。从5.4.x开始,它添加了一个Closure::bind静态方法来将匿名函数绑定到特定对象或类。

示例:

class Foo {
     private $methods = array();

     public function addBar() {
       $barFunc = function () {
         var_dump($this->methods);
       };
       $this->methods['bar'] = \Closure::bind($barFunc, $this, get_class());
     }

     function __call($method, $args) {
          if(is_callable($this->methods[$method]))
          {
            return call_user_func_array($this->methods[$method], $args);
          }
     }
 }

 $foo = new Foo;
 $foo->addBar();
 $foo->bar();

答案 1 :(得分:8)

有些玩弄整件事。似乎只有ReflectionClass可以做的事情才是替换现有方法。但即使这样也是间接的。

我实际上不知道任何基于类的语言,其中存在动态类(然后,我的知识非常有限)。我已经看到它只在基于原型的语言(javascript,ruby,smalltalk)中完成。相反,您可以在 PHP 5.4 中使用Closure并将新方法添加到现有对象

这是一个允许你对任何对象执行此类变换的类:

class Container
{
    protected $target;
    protected $className;
    protected $methods = [];

    public function __construct( $target )
    {
        $this->target = $target;
    }

    public function attach( $name, $method )
    {
        if ( !$this->className )
        {
            $this->className = get_class( $this->target );
        }
        $binded = Closure::bind( $method, $this->target, $this->className );
        $this->methods[$name] = $binded;
    }

    public function __call( $name, $arguments )
    {
        if ( array_key_exists( $name, $this->methods ) )
        {
            return call_user_func_array( $this->methods[$name] , $arguments );
        }

        if ( method_exists( $this->target, $name ) )
        {
            return call_user_func_array( 
                array( $this->target, $name ),
                $arguments
                );
        }
    }   
}

要使用它,您必须为构造函数提供现有对象。以下是一个小用法示例:

class Foo
{
    private $bar = 'payload';
};
$foobar = new Foo;
// you initial object


$instance = new Container( $foobar );
$func = function ( $param )
{
    return 'Get ' . $this->bar . ' and ' . $param;
};
$instance->attach('test', $func);
// setting up the whole thing


echo $instance->test('lorem ipsum');
// 'Get payload and lorem ipsum'

不完全是你想要的,但AFAIK就是你能得到的。

答案 2 :(得分:3)

您是否在文档中查看了create_function()?您也可以通过overloading获得所需的结果。

答案 3 :(得分:3)

这可以通过runkit扩展的runkit_method_add()来实现。但要小心在生产中使用它。

示例:

<?php
class Example {}

$e = new Example();

runkit_method_add(
    'Example',
    'add',
    '$num1, $num2',
    'return $num1 + $num2;',
    RUNKIT_ACC_PUBLIC
);

echo $e->add(12, 4);

答案 4 :(得分:0)

您也可以使用以下两种方法之一。

function method1()
{
    echo "In method one.";
}

function method2()
{
    echo "In method two.";
}

class DynamicClass
{
    function __construct(){
        $function_names = ['method1'];
        foreach ($function_names as $function_name) {
            if (function_exists($function_name)) {
                $this->addMethod($function_name);
            }
        }
    }

    function addMethod($name)
    {
        $this->{$name} = Closure::fromCallable($name);
    }

    public function __call($name, $arguments)
    {
        return call_user_func($this->{$name}, $arguments);
    }
}


$obj = new DynamicClass();
//Call method1 added in constructor
$obj->method1();
//Add method
$obj->addMethod('method2');
$obj->method2();