Você pode usar __call. Mas, por favor, não use __call. Alterar dinamicamente o comportamento de um objeto é uma maneira muito fácil de tornar seu código ilegível e impossível de manter.
Muito, muito inteligente, mas desajeitado em um projeto do mundo real IMO! (e +1 para contrariar o downvoter anônimo)
Pekka
2
@pekka - mas como, exatamente, é kludgy? Claro, não estou dizendo que sou um especialista em estender um objeto durante o tempo de execução em PHP, mas honestamente não posso dizer que vejo muito de errado nisso. (talvez eu só tenha mau gosto)
karim79
16
Eu adicionaria uma verificação is_callable nesse if também (para que você não tente acidentalmente chamar uma string). E também lance um BadMethodCallException () se você não conseguir encontrar um método, então você não o fez retornar e acha que ele retornou com sucesso. Além disso, faça do primeiro parâmetro da função o próprio objeto e, em seguida, faça um array_unshift($args, $this);antes da chamada do método, de modo que a função obtenha uma referência ao objeto sem a necessidade de vinculá-lo explicitamente ...
ircmaxell
3
Acho que as pessoas estão perdendo o ponto, o requisito original era usar um objeto classless.
thomas-peter
1
Na verdade, eu sugeriria que você remova completamente o teste isset () porque, se essa função não estiver definida, você provavelmente não deseja retornar silenciosamente.
Usar simplesmente __call para permitir a adição de novos métodos em tempo de execução tem a principal desvantagem de que esses métodos não podem usar a referência $ this instância. Tudo funciona muito bem, até que os métodos adicionados não usem $ this no código.
Atualização: a abordagem mostrada aqui tem uma grande lacuna: a nova função não é um membro totalmente qualificado da classe; $thisnão está presente no método quando chamado dessa maneira. Isso significa que você teria que passar o objeto para a função como um parâmetro se quiser trabalhar com dados ou funções da instância do objeto! Além disso, você não poderá acessar privateou protectedmembros da classe a partir dessas funções.
Boa pergunta e ideia inteligente usando as novas funções anônimas!
Curiosamente, isso funciona: Substitua
$me->doSomething();// Doesn't work
por call_user_func na própria função :
call_user_func($me->doSomething);// Works!
o que não funciona é a maneira "certa":
call_user_func(array($me,"doSomething"));// Doesn't work
se chamado dessa forma, o PHP requer que o método seja declarado na definição da classe.
Isso é um private problema de visibilidade / public/ protected?
Atualização: Não. É impossível chamar a função da maneira normal mesmo de dentro da classe, portanto, esse não é um problema de visibilidade. Passar a função real para call_user_func()é a única maneira de fazer isso funcionar.
Para ver como fazer isso com eval, você pode dar uma olhada no meu micro-framework PHP, Halcyon, que está disponível no github . É pequeno o suficiente para que você consiga descobrir sem problemas - concentre-se na classe HalcyonClassMunger.
Estou assumindo (e meu código também) que você está executando em PHP <5.3.0 e, portanto, precisa de kludges e hacks para fazer isso funcionar.
Ivans
Perguntar se alguém gostaria de comentar sobre o downvote é inútil, suponho ...
ivans
Provavelmente é, há alguns downvoting em massa por aqui. Mas talvez você queira elaborar um pouco sobre o que você fez? Como você pode ver, esta é uma pergunta interessante sem uma resposta definitiva ainda.
Pekka
1
Sem a __callsolução, você pode usar bindTo(PHP> = 5.4), para chamar o método com $thislimite da $meseguinte maneira:
$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"
Como alternativa, você pode atribuir a função vinculada a uma variável e chamá-la sem call_user_func:
A única outra maneira é através do uso de classkit , uma extensão experimental de php. (também na postagem)
Sim, é possível adicionar um método a uma classe PHP após sua definição. Você deseja usar o classkit, que é uma extensão "experimental". Parece que esta extensão não está habilitada por padrão, portanto, depende se você pode compilar um binário PHP personalizado ou carregar DLLs PHP se estiver no Windows (por exemplo, o Dreamhost permite binários PHP personalizados e eles são muito fáceis de configurar )
A resposta karim79 funciona, mas armazena funções anônimas dentro das propriedades do método e suas declarações podem sobrescrever as propriedades existentes com o mesmo nome ou não funcionam se a propriedade existente está privatecausando erro fatal. Objeto inutilizável. Acho que armazená-los em um array separado e usar setter é uma solução mais limpa. O método injetor setter pode ser adicionado a cada objeto automaticamente usando traits .
PS Claro que este é um hack que não deve ser usado, pois viola o princípio aberto fechado de 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);
Respostas:
Você pode aproveitar
__call
para isso:fonte
array_unshift($args, $this);
antes da chamada do método, de modo que a função obtenha uma referência ao objeto sem a necessidade de vinculá-lo explicitamente ...Com o PHP 7 você pode usar classes anônimas
PHP RFC: classes anônimas
fonte
Usar simplesmente __call para permitir a adição de novos métodos em tempo de execução tem a principal desvantagem de que esses métodos não podem usar a referência $ this instância. Tudo funciona muito bem, até que os métodos adicionados não usem $ this no código.
Para resolver este problema, você pode reescrever o padrão desta forma
Desta forma, você pode adicionar novos métodos que podem usar a referência de instância
fonte
Boa pergunta e ideia inteligente usando as novas funções anônimas!
Curiosamente, isso funciona: Substitua
por call_user_func na própria função :
o que não funciona é a maneira "certa":
se chamado dessa forma, o PHP requer que o método seja declarado na definição da classe.
Isso é um
private
problema de visibilidade /public
/protected
?Atualização: Não. É impossível chamar a função da maneira normal mesmo de dentro da classe, portanto, esse não é um problema de visibilidade. Passar a função real para
call_user_func()
é a única maneira de fazer isso funcionar.fonte
você também pode salvar funções em uma matriz:
fonte
Para ver como fazer isso com eval, você pode dar uma olhada no meu micro-framework PHP, Halcyon, que está disponível no github . É pequeno o suficiente para que você consiga descobrir sem problemas - concentre-se na classe HalcyonClassMunger.
fonte
Sem a
__call
solução, você pode usarbindTo
(PHP> = 5.4), para chamar o método com$this
limite da$me
seguinte maneira:O script completo pode ter a seguinte aparência:
Como alternativa, você pode atribuir a função vinculada a uma variável e chamá-la sem
call_user_func
:fonte
Há uma postagem semelhante sobre stackoverflow que esclarece que isso só é possível por meio da implementação de certos padrões de design.
A única outra maneira é através do uso de classkit , uma extensão experimental de php. (também na postagem)
fonte
A resposta karim79 funciona, mas armazena funções anônimas dentro das propriedades do método e suas declarações podem sobrescrever as propriedades existentes com o mesmo nome ou não funcionam se a propriedade existente está
private
causando erro fatal. Objeto inutilizável. Acho que armazená-los em um array separado e usar setter é uma solução mais limpa. O método injetor setter pode ser adicionado a cada objeto automaticamente usando traits .PS Claro que este é um hack que não deve ser usado, pois viola o princípio aberto fechado de SOLID.
fonte