O que são Magic Methods em PHP?
Magic Methods são métodos especiais do PHP que são invocados automaticamente quando certas ações ocorrem em um objeto. Eles começam com dois underscores (__) e permitem interceptar operações como leitura, escrita e chamada de métodos inexistentes. São fundamentais para criar classes flexíveis e dinâmicas, reduzindo código repetitivo e possibilitando comportamentos que seriam impossíveis de outra forma.
Existem mais de 15 magic methods em PHP, mas focaremos nos mais utilizados em produção. Compreendê-los é essencial para trabalhar com frameworks modernos como Laravel e Symfony, que os usam extensivamente em suas arquiteturas.
Magic Methods para Propriedades: __get e __set
O __get() é invocado quando você tenta acessar uma propriedade privada ou inexistente. O __set() faz o mesmo para atribuição de valores. Esses métodos são extraordinariamente úteis para criar classes com comportamento dinâmico.
class Usuario {
private array $dados = [];
public function __set(string $nome, mixed $valor): void {
// Intercepta atribuição: $usuario->email = 'test@example.com'
if ($nome === 'email') {
if (!filter_var($valor, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email inválido');
}
}
$this->dados[$nome] = $valor;
}
public function __get(string $nome): mixed {
// Intercepta leitura: echo $usuario->email
if (!isset($this->dados[$nome])) {
return null;
}
return $this->dados[$nome];
}
}
$user = new Usuario();
$user->email = 'joao@example.com'; // Chama __set
$user->nome = 'João Silva'; // Chama __set
echo $user->email; // Chama __get
Use __get e __set quando precisar de validação centralizada, logging ou comportamento dinâmico. Evite nos casos onde performance é crítica, pois há overhead em relação ao acesso direto de propriedades.
Magic Methods para Métodos: __call e __callStatic
O __call() intercepta chamadas a métodos inexistentes ou inacessíveis em instâncias. O __callStatic() faz o mesmo para chamadas estáticas. Esses são poderosos para criar APIs fluentes ou delegar chamadas dinamicamente.
class Logger {
private string $nivel = 'info';
public function __call(string $metodo, array $argumentos): void {
// Permite: $logger->debug(), $logger->error(), etc.
$niveis = ['debug', 'info', 'warning', 'error', 'critical'];
if (in_array($metodo, $niveis)) {
$this->log($metodo, $argumentos[0] ?? '');
} else {
throw new BadMethodCallException("Método $metodo não existe");
}
}
private function log(string $nivel, string $mensagem): void {
echo "[" . strtoupper($nivel) . "] " . $mensagem . "\n";
}
}
$logger = new Logger();
$logger->debug('Iniciando processamento'); // Chama __call
$logger->error('Erro crítico detectado'); // Chama __call
Para métodos estáticos, use __callStatic():
class BD {
public static function __callStatic(string $metodo, array $argumentos): mixed {
// Permite: BD::usuario() para chamar BD::buscar('usuario')
if (method_exists(self::class, 'buscar')) {
return self::buscar($metodo);
}
throw new BadMethodCallException("Método estático $metodo não existe");
}
private static function buscar(string $tabela): array {
return ["dados da tabela $tabela"];
}
}
$resultado = BD::usuario(); // Chama __callStatic
Outros Magic Methods Importantes
Além de __get, __set, __call e __callStatic, existem outros métodos mágicos essenciais. O __construct() é invocado ao criar um objeto, enquanto __toString() define como o objeto é convertido para string. O __isset() e __unset() controlam verificações e destruição de propriedades dinâmicas.
class Produto {
private array $atributos = [];
public function __construct(string $nome, float $preco) {
$this->atributos['nome'] = $nome;
$this->atributos['preco'] = $preco;
}
public function __toString(): string {
return $this->atributos['nome'] . ' - R$ ' . $this->atributos['preco'];
}
public function __isset(string $chave): bool {
return isset($this->atributos[$chave]);
}
public function __unset(string $chave): void {
unset($this->atributos[$chave]);
}
public function __invoke(string $acao): string {
// Permite usar objeto como função: $produto('detalhes')
return "Executando: $acao no produto";
}
}
$produto = new Produto('Notebook', 3500.00);
echo $produto . "\n"; // Chama __toString
echo isset($produto->nome) . "\n"; // Chama __isset (retorna true)
echo $produto('detalhes'); // Chama __invoke
O __invoke() transforma um objeto em callable, permitindo usá-lo como função. Extremamente útil em callbacks e quando você precisa de comportamentos que combinam estado com execução.
Boas Práticas e Armadilhas
Magic methods oferecem grande flexibilidade, mas podem prejudicar a legibilidade do código. Use-os com propósito claro: validação de dados, logging, ou padrões específicos como Active Record. Nunca os use apenas para "economizar código". Documente bem com comentários PHPDoc para que outros desenvolvedores entendam o comportamento dinâmico.
Evite usar magic methods de forma aninhada ou recursiva — isso causa bugs difíceis de debugar. Se performance é crítica, prefira getters e setters explícitos. Finalmente, lembre-se que IDEs terão dificuldade em autocomplete e refatoração com código dinâmico, então teste minuciosamente.
Conclusão
Magic methods em PHP são ferramentas poderosas quando usadas adequadamente. __get e __set centralizam validação de propriedades dinâmicas, enquanto __call e __callStatic criam APIs flexíveis. Outros como __toString(), __isset() e __invoke() complementam a toolkit, permitindo comportamentos que aproximam objetos de tipos primitivos. O segredo é usá-los com intenção clara, mantendo o código legível e bem documentado.