Вы можете использовать __call. Но, пожалуйста, не используйте __call. Динамическое изменение поведения объекта - очень простой способ сделать ваш код нечитаемым и не поддерживаемым.
Очень, очень умно, но глупо в реальном проекте IMO! (и +1 против анонимного отрицателя)
Пекка
2
@pekka - а как именно он тупой? Конечно, я не говорю, что я эксперт по расширению объектов во время выполнения в PHP, но я, честно говоря, не могу сказать, что вижу в этом много плохого. (возможно, у меня просто плохой вкус)
karim79
16
Я бы также добавил в это if проверку is_callable (чтобы вы случайно не попытались вызвать строку). А также бросить BadMethodCallException (), если вы не можете найти метод, поэтому вы не получите его возврата и думаете, что он вернулся успешно. Кроме того, сделайте первый параметр функции самим объектом, а затем сделайте его array_unshift($args, $this);перед вызовом метода, чтобы функция получила ссылку на объект без явной необходимости его привязки ...
ircmaxell
3
Я думаю, что люди упускают из виду суть, изначальным требованием было использование бесклассового объекта.
thomas-peter
1
На самом деле я бы посоветовал вам полностью удалить тест isset (), потому что, если эта функция не определена, вы, вероятно, не захотите возвращаться молча.
Alexis Wilke
51
В PHP 7 вы можете использовать анонимные классы, что снимает stdClassограничение.
Использование простого __call для добавления новых методов во время выполнения имеет главный недостаток, заключающийся в том, что эти методы не могут использовать ссылку на экземпляр $ this. Все отлично работает, пока добавленные методы не используют $ this в коде.
Обновление. Показанный здесь подход имеет серьезный недостаток: новая функция не является полностью квалифицированным членом класса; $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()- это единственный способ, которым я могу выполнить эту работу.
Чтобы узнать, как это сделать с помощью eval, вы можете взглянуть на мою микро-структуру PHP Halcyon, доступную на github . Он достаточно мал, чтобы вы могли без проблем разобраться - сконцентрируйтесь на классе HalcyonClassMunger.
Я предполагаю (как и мой код), что вы работаете на PHP <5.3.0 и, следовательно, вам нужны кладжи и хаки для работы.
ivans
Я полагаю, что спрашивать, не хочет ли кто-нибудь прокомментировать голосование против, бессмысленно ...
ivans
Вероятно, здесь происходит массовое голосование против. Но, может быть, вы захотите немного рассказать о том, что вы сделали? Как видите, это интересный вопрос, на который пока нет однозначного ответа.
Пекка
1
Без __callрешения вы можете использовать bindTo(PHP> = 5.4), чтобы вызвать метод со следующей $thisпривязкой $me:
$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:
Есть похожий пост о stackoverflow, в котором разъясняется, что это возможно только путем реализации определенных шаблонов проектирования.
Единственный другой способ - использовать classkit , экспериментальное расширение php. (также в посте)
Да, можно добавить метод к классу PHP после его определения. Вы хотите использовать classkit, это «экспериментальное» расширение. Однако похоже, что это расширение не включено по умолчанию, поэтому это зависит от того, можете ли вы скомпилировать собственный двоичный файл PHP или загрузить библиотеки DLL PHP, если в Windows (например, Dreamhost разрешает настраиваемые двоичные файлы PHP, и их довольно легко настроить ).
Ответ karim79 работает, однако он хранит анонимные функции внутри свойств метода, и его объявления могут перезаписывать существующие свойства с тем же именем или не работают, если существующее свойство privateвызывает непригодность объекта фатальной ошибки, я думаю, что их хранение в отдельном массиве и использование установщика является более чистым решением. Установщик инжектора методов может быть добавлен к каждому объекту автоматически с использованием трейтов .
PS Конечно, это хак, который не стоит использовать, поскольку он нарушает принцип открытости и закрытости SOLID.
classMyClass{//create array to store all injected methodsprivate $anon_methods = array();//create setter to fill array with injected methods on runtimepublicfunction inject_method($name, $method){
$this->anon_methods[$name]= $method;}//runs when calling non existent or private methods from object instancepublicfunction __call($name, $args){if( key_exists($name, $this->anon_methods)){
call_user_func_array($this->anon_methods[$name], $args);}}}
$MyClass =newMyClass;//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);
Ответы:
__call
Для этого можно использовать :источник
array_unshift($args, $this);
перед вызовом метода, чтобы функция получила ссылку на объект без явной необходимости его привязки ...В PHP 7 вы можете использовать анонимные классы, что снимает
stdClass
ограничение.PHP RFC: анонимные классы
источник
Использование простого __call для добавления новых методов во время выполнения имеет главный недостаток, заключающийся в том, что эти методы не могут использовать ссылку на экземпляр $ this. Все отлично работает, пока добавленные методы не используют $ this в коде.
Чтобы решить эту проблему, вы можете переписать узор таким образом
Таким образом вы можете добавлять новые методы, которые могут использовать ссылку на экземпляр
источник
Хороший вопрос и умная идея с использованием новых анонимных функций!
Интересно, что это работает: Заменить
с помощью call_user_func самой функции :
что не работает, так это «правильный» способ:
если вызывается таким образом, PHP требует, чтобы метод был объявлен в определении класса.
Это проблема с
private
/public
/protected
видимостью?Обновление: Нет. Невозможно вызвать функцию обычным способом даже из класса, так что это не проблема видимости. Передача фактической функции
call_user_func()
- это единственный способ, которым я могу выполнить эту работу.источник
вы также можете сохранять функции в массиве:
источник
Чтобы узнать, как это сделать с помощью eval, вы можете взглянуть на мою микро-структуру PHP Halcyon, доступную на github . Он достаточно мал, чтобы вы могли без проблем разобраться - сконцентрируйтесь на классе HalcyonClassMunger.
источник
Без
__call
решения вы можете использоватьbindTo
(PHP> = 5.4), чтобы вызвать метод со следующей$this
привязкой$me
:Полный сценарий может выглядеть так:
В качестве альтернативы вы можете назначить связанную функцию переменной, а затем вызвать ее без
call_user_func
:источник
Есть похожий пост о stackoverflow, в котором разъясняется, что это возможно только путем реализации определенных шаблонов проектирования.
Единственный другой способ - использовать classkit , экспериментальное расширение php. (также в посте)
источник
Ответ karim79 работает, однако он хранит анонимные функции внутри свойств метода, и его объявления могут перезаписывать существующие свойства с тем же именем или не работают, если существующее свойство
private
вызывает непригодность объекта фатальной ошибки, я думаю, что их хранение в отдельном массиве и использование установщика является более чистым решением. Установщик инжектора методов может быть добавлен к каждому объекту автоматически с использованием трейтов .PS Конечно, это хак, который не стоит использовать, поскольку он нарушает принцип открытости и закрытости SOLID.
источник