Подтвердить что ты не робот

Php создать метод класса во время выполнения

Мне интересно, есть ли способ прикрепить новый метод к классу во время выполнения, в php. Я имею в виду не на уровне экземпляра, а непосредственно в классе, так что все вновь созданные экземпляры имеют этот новый метод. Можно ли это сделать с отражением?

Спасибо

4b9b3361

Ответ 1

Да, вы можете.

Ниже приведен способ создания метода во время выполнения в php 5.4.x.

Анонимная функция представлена ​​классом Closure, начиная с 5.3.x. Из 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();

Ответ 2

Разве что-то играло со всей штукой. Кажется, что единственное, что вы можете сделать с помощью 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 это как можно ближе.

Ответ 3

Вы просмотрели create_function() в документах? Вы также можете достичь желаемого результата overloading.

Ответ 4

Это возможно с расширением 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);

Ответ 5

Вы также можете использовать один из следующих двух методов.

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();