2012-06-11 4 views
23

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

Благодаря

ответ

20

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

Ниже приведен способ создания метода во время выполнения в 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

Awesome. Любить эти новые функции PHP. – Thomas

+0

. В чем смысл метода __call здесь, если \ Closure :: bind делает вещь, и наоборот? – jayarjo

+2

@jayarjo '\ Closure :: bind' изменяет контекст анонимной функции (в этом случае он гарантирует, что в пределах замыкания' $ this' ссылается на правильный объект). Однако это не делает эту функцию в методе '$ this'. Нам все еще нужно '__call', чтобы позволить внешнему коду вызывать закрытие, как если бы это был метод. –

3

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

+0

create_function (как фас, как я знаю) создает функции в глобальном контексте и перегрузка не так чиста, решение, как хотелось бы ему быть – Thomas

+0

я понимаю. PHP не является, однако, динамическим языком, который был бы идеальным в этом сценарии. Я верю [Reflection] (http://www.php.net/manual/en/intro.reflection.php) или [Runkit] (http://www.php.net/manual/en/intro.runkit.php) было бы необходимо для достижения более близкого решения, о котором вы говорили в своем вопросе. –

+0

Мне понравилось бы решение с отражением, но я не могу найти какую-либо информацию. Глядя на API, я ничего не могу использовать – Thomas

7

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

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