Intereting Posts
Как создать поддомен для каждого пользователя в облаке AWS Примечание. Неопределенный индекс Дружественные URL-адреса – переписывание мод и переадресация php PHP короткий уникальный генерации идентификатора с использованием auto_increment? Использует ли PHP-деструктор для отображения HTML? Ограничить время соединения с помощью HTTP-клиента Guzzle HTTP Динамически переключать контент на веб-сайте на основе выбора языка пользователя Как разместить изображение в Twitter с помощью PHP? php_osx установлен php 7, phpinfo () и композитор по-прежнему считают его php5.5 Как найти мой php-fpm.sock? Вызов неопределенного метода Illuminate \ Database \ Query \ Builder :: lists () при посеве после обновления до Laravel 5.3 Многобайтовая безопасная функция wordwrap () для UTF-8 Как защитить веб-приложение, проданное на основе лицензии, от пиратства? редактировать XML с помощью simpleXML загрузить файл pdf ajax html

Как добавить новый метод к объекту php на лету?

Как добавить новый метод к объекту «на лету»?

$me= new stdClass; $me->doSomething=function () { echo 'I\'ve done something'; }; $me->doSomething(); //Fatal error: Call to undefined method stdClass::doSomething() 

Вы можете использовать __call для этого:

 class Foo { public function __call($method, $args) { if (isset($this->$method)) { $func = $this->$method; return call_user_func_array($func, $args); } } } $foo = new Foo(); $foo->bar = function () { echo "Hello, this function is added at runtime"; }; $foo->bar(); 

С помощью PHP7 вы можете использовать анонимные классы

 $myObject = new class { public function myFunction(){} }; $myObject->myFunction(); 

PHP RFC: анонимные классы

Использование просто __call для добавления новых методов во время выполнения имеет главный недостаток, заключающийся в том, что эти методы не могут использовать ссылку this this this. Все отлично работает, пока добавленные методы не используют $ this в коде.

 class AnObj extends stdClass { public function __call($closure, $args) { return call_user_func_array($this->{$closure}, $args); } } $a=new AnObj(); $a->color="red"; $a->sayhello=function(){ echo "hello!";} $a->printmycolor=function(){ echo $this->color;} $a->sayhello();//output: "hello!" $a->printmycolor();//ERROR: Undefined variable $this 

Чтобы решить эту проблему, вы можете переписать шаблон таким образом

 class AnObj extends stdClass { public function __call($closure, $args) { return call_user_func_array($this->{$closure}->bindTo($this),$args); } public function __toString() { return call_user_func($this->{"__toString"}->bindTo($this)); } } 

Таким образом, вы можете добавить новые методы, которые могут использовать ссылку на экземпляр

 $a=new AnObj(); $a->color="red"; $a->sayhello=function(){ echo "hello!";} $a->printmycolor=function(){ echo $this->color;} $a->sayhello();//output: "hello!" $a->printmycolor();//output: "red" 

Обновление. Указанный здесь подход имеет большой недостаток: новая функция не является полностью квалифицированным членом класса; $this не существует в методе при вызове таким образом. Это означает, что вам нужно передать объект функции в качестве параметра, если вы хотите работать с данными или функциями из экземпляра объекта! Кроме того, вы не сможете получить доступ к private или protected членам класса из этих функций.

Хороший вопрос и умная идея с использованием новых анонимных функций!

Интересно, что это работает: Замените

 $me->doSomething(); // Doesn't work 

по вызову call_user_func по самой функции :

 call_user_func($me->doSomething); // Works! 

что не работает, это «правильный» способ:

 call_user_func(array($me, "doSomething")); // Doesn't work 

если так называется, PHP требует, чтобы метод был объявлен в определении класса.

Является ли это проблемой private / public / protected видимости?

Обновление: Нет. Невозможно вызвать функцию обычным способом даже изнутри класса, так что это не проблема видимости. Передача фактической функции в call_user_func() является единственным способом, с помощью которого я могу сделать эту работу.

Вот простой класс, который позволяет установить анонимную функцию. Это оптимизированный класс https://gist.github.com/nickunderscoremok/5857846

 <?php class stdObject { public function __construct(array $arguments = array()) { if (!empty($arguments)) { foreach ($arguments as $property => $argument) { if ($argument instanceOf Closure) { $this->{$property} = $argument; } else { $this->{$property} = $argument; } } } } public function __call($method, $arguments) { if (isset($this->{$method}) && is_callable($this->{$method})) { return call_user_func_array($this->{$method}, $arguments); } else { throw new Exception("Fatal error: Call to undefined method stdObject::{$method}()"); } } } $person = new stdObject(array( "name" => "nick", "age" => 23, "friends" => array("frank", "sally", "aaron"), "sayHi" => function() { return "Hello there"; } )); $person->sayHi2 = function() { return "Hello there 2"; }; $person->test = function() { return "test"; }; var_dump($person->name, $person->test(), $person->sayHi2()); ?> 

вы также можете сохранять функции в массиве:

 <?php class Foo { private $arrayFuncs=array();// array of functions // public function addFunc($name,$function){ $this->arrayFuncs[$name] = $function; } // public function callFunc($namefunc,$params=false){ if(!isset($this->arrayFuncs[$namefunc])){ return 'no function exist'; } if(is_callable($this->arrayFuncs[$namefunc])){ return call_user_func($this->arrayFuncs[$namefunc],$params); } } } $foo = new Foo(); //Save function on array variable with params $foo->addFunc('my_function_call',function($params){ return array('data1'=>$params['num1'],'data2'=>'qwerty','action'=>'add'); }); //Save function on array variable $foo->addFunc('my_function_call2',function(){ return 'a simple function'; }); //call func 1 $data = $foo->callFunc('my_function_call',array('num1'=>1224343545)); var_dump($data); //call func 2 $data = $foo->callFunc('my_function_call2'); var_dump($data); ?> 

Есть аналогичная запись в stackoverflow, которая очищает, что это возможно только при реализации определенных шаблонов проектирования.

Единственный другой способ – использование classkit , экспериментального расширения php. (также в сообщении)

Да, после его определения можно добавить метод в класс PHP. Вы хотите использовать classkit, который является «экспериментальным» расширением. По-видимому, это расширение не включено по умолчанию, однако, это зависит от того, можно ли скомпилировать пользовательский PHP-файл или загрузить PHP-библиотеки DLL, если на окнах (например, Dreamhost позволяет создавать собственные двоичные файлы PHP, и их довольно легко настроить ).

Без решения __call вы можете использовать bindTo (PHP> = 5.4), чтобы вызвать метод с $this bound для $me следующим образом:

 call_user_func($me->doSomething->bindTo($me, null)); 

Полный скрипт может выглядеть так:

 $me = new stdClass; // Property for proving that the method has access to the above object: $me->message = "I\'ve done something"; $me->doSomething = function () { echo $this->message; }; call_user_func($me->doSomething->bindTo($me)); // "I've done something" 

В качестве альтернативы вы можете назначить связанную функцию переменной, а затем вызвать ее без call_user_func :

 $f = $me->doSomething->bindTo($me); $f(); 

Я бы никогда не использовал его, синтаксис ужасен, но вы можете добавить объект, который имеет функции для свойства. Статически, когда возможно сохранить ввод новых и () ;

 class SomeFuncs{ static function a(){ /* do something */ }} $o = new stdClass(); $o->f = SomeFuncs; $o->f::a(); 

Ответ karim79 работает, однако он сохраняет анонимные функции внутри свойств метода, и его декларации могут перезаписывать существующие свойства с тем же именем или не работать, если существующее свойство является private что приводит к непригодности объекта фатальной ошибки. Я думаю, что хранение их в отдельном массиве и использование setter – более чистое решение. метод инжектора может быть добавлен к каждому объекту автоматически с использованием признаков .

PS Конечно, это хак, который нельзя использовать, поскольку он нарушает открытый закрытый принцип SOLID.

 class MyClass { //create array to store all injected methods private $anon_methods = array(); //create setter to fill array with injected methods on runtime public function inject_method($name, $method) { $this->anon_methods[$name] = $method; } //runs when calling non existent or private methods from object instance public function __call($name, $args) { if ( key_exists($name, $this->anon_methods) ) { call_user_func_array($this->anon_methods[$name], $args); } } } $MyClass = new MyClass; //method one $print_txt = function ($text) { echo $text . PHP_EOL; }; $MyClass->inject_method("print_txt", $print_txt); //method $add_numbers = function ($n, $e) { echo "$n + $e = " . ($n + $e); }; $MyClass->inject_method("add_numbers", $add_numbers); //Use injected methods $MyClass->print_txt("Hello World"); $MyClass->add_numbers(5, 10); 

Чтобы узнать, как это сделать с помощью eval, вы можете взглянуть на мою микроструктуру PHP, Halcyon, которая доступна на github . Он достаточно мал, чтобы вы могли понять его без каких-либо проблем – сосредоточьтесь на классе HalcyonClassMunger.