PHP создать метод класса во время выполнения
Мне интересно, есть ли способ присоединить новый метод к классу во время выполнения, в php. Я имею в виду не на уровне экземпляра, а непосредственно в классе, чтобы у всех вновь создаваемых экземпляров был этот новый метод. Можно ли сделать такую вещь с помощью отражения?
Спасибо
4 ответа
Да, ты можешь.
Ниже приведен способ создания метода во время выполнения в 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();
Поиграл со всем этим. Кажется, что единственное, что вы можете сделать потенциально 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 это как можно ближе.
Вы смотрели на create_function() в документах? Вы также можете достичь желаемого результата путем перегрузки.
Это возможно с помощью runkit_method_add() расширения runkit. Будьте осторожны, используя это в производстве.
Пример:
<?php
class Example {}
$e = new Example();
runkit_method_add(
'Example',
'add',
'$num1, $num2',
'return $num1 + $num2;',
RUNKIT_ACC_PUBLIC
);
echo $e->add(12, 4);
Вы также можете использовать один из следующих двух методов.
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();