+1

Métodos mágicos do PHP5

criado por Daniel Braga em 22/12/2006 8:50am
Esse, na minha opinião, é um dos métodos mágicos mais interessante. Como exemplo, iremos utilizar o __call() para criarmos getters e setters dinâmicos.

Vamos imaginar uma classe que possua 15 propriedades. Se fossemos criar os getters e setters, teríamos que criar 30 métodos praticamente iguais. Mas com o __call() não será necessário nada além de codificar o próprio __call(). Vamos colocar mais algumas propriedades na nossa classe Pessoa:

<?php
class Pessoa {
  /**
  * Propriedades
  */
  protected
    $apelido = '',
    $nome_completo = '',
    $email = '',
    $data_nascimento = '',
    $sexo = '';

  public function __construct($nome) {
    $this->nome_completo = $nome;

    print "Criando Pessoa $nome\n";
  }

  public function __toString() {
    return $this->nome_completo;
  }
}
$Funcionario = new Pessoa("Daniel Braga");
print $Funcionario;
?>

Mudamos a propriedade $nome para $nome_completo e acrescentamos as propriedades $apelido, $email, $data_nascimento e $sexo. Na criação das propriedades usamos o padrão com underscore, que é: todas as letras com minúsculas e em, caso de nome composto, é usado o underscore para separar as palavras.

Agora precisamos "criar" os getters e setters. O padrão usado para nomear os métodos será "camel caps", ou seja, começa com letra minúscula e em as demais palavras começam com maiúsculas. No caso de $nome_completo, o getter seria getNomeCompleto() e o setter setNomeCompleto().

Mas como o __call() funciona? Em uma classe, o PHP tentará executar o método __call() quando ocorrer uma chamada a um método não definido no código da classe.

$Funcionario->setNomeCompleto('Teste');

No exemplo acima será gerado um erro fatal já que ainda não definimos um método chamado setNomeCompleto(). Mas antes de disparar o erro, o PHP tentará chamar o método __call().

Quando formos definir nosso método __call() deveremos fazer assim:

<?php
//...
  public function __call($metodo, $parametros) {
    //...
  }
//...
?>

O primeiro argumento, $metodo, recebe o nome do método que o PHP não encontrou na definição da classe e no segundo, $parametros, um array com os argumentos que foram passados ao método. Então, na chamada $Funcionario->setNomeCompleto('Teste') o __call() recebeu a string 'setNomeCompleto' no argumento $metodo e um array com um membro de valor 'Daniel Braga'.

O próprio programador deverá decidir o que fazer com as variáveis $metodo e $parametros dentro do método __call(). Nós iremos criar falsos getters e setters. Como? Quando o método começar com "get" saberemos que estamos tentando executar um getter e quando começar com "set" será um setter. Vejamos:

<?php
//...
  public function __call($metodo, $parametros) {
    // se for set*, "seta" um valor para a propriedade
    if (substr($metodo, 0, 3) == 'set') {
      //...
    }
    // se for get*, retorna o valor da propriedade
    elseif (substr($metodo, 0, 3) == 'get') {
      //...
    }
  }
//...
?>

Com o código acima já conseguimos identificar quando está sendo chamado um setter e quando é getter. Quando for setter, o texto restante após "set" é o nome da propriedade da classe a qual estamos tentando atribuir um valor. Em setNomeCompleto estamos tentanto atribuir um valor para $nome_completo. Então precisamos transformar a string NomeCompleto (camel caps) em nome_completo (underscore) e depois atribuir o valor para ela. O valor estará no índice zero do array $parametros. No "get" é quase a mesma coisa, mas apenas iremos retornar o valor da propriedade e não iremos utilizar a variável $parametros.

Comentários:

Mostrando 1 - 9 de 9 comentários
Daniel Braga disse:
Sim, existem. Estou preparando uma continução desse artigo para abordar o __get() e o __set().
02/01/2007 12:01pm (~18 anos atrás)

Erick Muller disse:
Existem também os métodos __get() e __set() para desenvolver os getters e setters.

Eu ainda estou aprendendo um pouco sobre orientação a objetos, mas uma coisa que eu vi num livro que eu estou lendo (Code Complete) é que o uso desse tipo de funções é útil para garantir a independência total da classe, com uma encapsulação completa.
02/01/2007 11:58am (~18 anos atrás)

Daniel Braga disse:
Há um tanto de inutilidade no ato de definir todos os getters e setters de uma aplicação.

Será um código bem repetitivo e numa classe com várias propriedades você terá várias linhas de código para te atrapalhar na leitura do arquivo. Se seu editar não possuir code folding isso fica muito irritante.

Mas isso é uma opinião minha. Cada um tem que trabalhar da maneira que lhe for mais conveniente.
02/01/2007 10:01am (~18 anos atrás)

Daniel Braga disse:
Mas dessa forma você não acha que seus editores de código estão delimitando, restringindo, a forma como você trabalha?
02/01/2007 9:49am (~18 anos atrás)

Daniel Braga disse:
Mas dessa forma você não acha que seus editores de código não estão delimitando, restringindo, a forma como você trabalha?
02/01/2007 9:37am (~18 anos atrás)

Daniel Braga disse:
Seria mais conveniente sim. Inclusive é o padrão de codificação do Zend Framework. No entanto, utilizei o formato com underscore apenas para "colorir" o exemplo dado.
02/01/2007 9:34am (~18 anos atrás)

Bozo disse:
Achei ótimo o artigo, porém quando falamos em OO, acho melhor determinar todos os getters e setters, sem processamento de código (automatização __call()).

Parabens pelo artigo.
27/12/2006 9:51am (~18 anos atrás)

Não seria mais conveniente escrever as propriedades como camel caps e manter essa convenção em toda a classe? Isso pouparia a execução de código para transformar os nomes das propriedades no método __call(). Se existe uma razão para isso deveria ser explicada.
27/12/2006 7:04am (~18 anos atrás)

Cara ... eu vinha usando essa técnica já há algum tempo, mas quando se programa com orientação a objetos usando Ferramentas com auto-completar de procedimentos (Zend e Komodo, por exemplo) esse recurso vai embora.
26/12/2006 9:17am (~18 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)