Métodos mágicos
Nesta aula, exploramos os métodos mágicos do PHP, que são métodos especiais que permitem interceptar ações sobre objetos. Abordamos __get, __set, __call, __toString e __invoke, com exemplos práticos e cuidados essenciais para evitar erros comuns.
Os métodos mágicos no PHP são funções especiais que começam com dois underscores (__) e são automaticamente invocadas quando determinadas ações ocorrem em um objeto. Eles permitem que você controle o comportamento de propriedades e métodos inacessíveis, conversão para string, chamada de objeto como função, entre outros. Nesta aula, vamos explorar os mais usados: __get, __set, __call, __toString e __invoke, além de discutir cuidados importantes para evitar problemas de desempenho e segurança.
Entender esses métodos é fundamental para criar classes flexíveis e elegantes, como em implementações de proxies, decorators ou objetos que se comportam como arrays. Vamos ver cada um com exemplos práticos.
__get, __set, __call
Os métodos __get e __set são invocados quando você tenta acessar ou modificar uma propriedade inacessível (protegida ou privada) ou inexistente de um objeto. Eles permitem que você implemente lógica personalizada para leitura e escrita de propriedades dinâmicas.
O método __get recebe o nome da propriedade como argumento e deve retornar o valor correspondente. Já __set recebe o nome e o valor, e permite definir o valor. Exemplo:
class Produto {
private $dados = [];
public function __get($name) {
return $this->dados[$name] ?? null;
}
public function __set($name, $value) {
$this->dados[$name] = $value;
}
}
$p = new Produto();
$p->nome = 'Caneta'; // chama __set
$p->preco = 2.5; // chama __set
echo $p->nome; // chama __get, exibe 'Caneta'O método __call é semelhante, mas para métodos. Ele é invocado quando você chama um método inacessível: recebe o nome do método e um array de argumentos. Exemplo:
class Logger {
public function __call($name, $arguments) {
echo "Chamou método $name com parâmetros: " . implode(', ', $arguments) . "\n";
}
}
$log = new Logger();
$log->log('erro', 'Arquivo não encontrado'); // chama __call__toString
O método __toString permite definir como um objeto deve ser representado como string. Ele é chamado automaticamente quando você usa echo, print ou funções como strval() em um objeto. Deve retornar uma string. Exemplo:
class Usuario {
public $nome;
public $email;
public function __toString() {
return "Usuário: {$this->nome} ({$this->email})";
}
}
$user = new Usuario();
$user->nome = 'João';
$user->email = 'joao@email.com';
echo $user; // exibe "Usuário: João (joao@email.com)"Esse método é útil para depuração, logs ou quando você precisa exibir o objeto de forma legível. Lembre-se de que ele deve retornar uma string, e nunca lançar exceções, pois pode causar erros fatais em contextos que esperam uma string.
__invoke
O método __invoke permite que um objeto seja chamado como se fosse uma função. Quando você coloca parênteses após uma instância de classe, o PHP procura pelo método __invoke. Exemplo:
class Soma {
public function __invoke($a, $b) {
return $a + $b;
}
}
$soma = new Soma();
echo $soma(3, 4); // 7Isso é útil para criar objetos que funcionam como closures, especialmente em frameworks que usam callables. Você pode, por exemplo, implementar um callback que mantém estado interno.
Cuidados
Métodos mágicos podem tornar o código mais difícil de entender e depurar, pois a lógica fica implícita. Use-os com moderação e sempre documente bem. Evite usar __get e __set para simples acesso a propriedades; prefira propriedades públicas ou métodos getters/setters explícitos. __call pode esconder erros de digitação, dificultando a manutenção.
Outro cuidado: métodos mágicos são mais lentos que chamadas diretas, pois envolvem overhead de verificação. Em loops ou operações repetitivas, evite usá-los. Além disso, __toString não deve lançar exceções, pois em muitos contextos (como dentro de um echo) a exceção não será capturada e causará um erro fatal.
Por fim, lembre-se de que métodos mágicos não funcionam com propriedades ou métodos estáticos. Eles só se aplicam a contextos de instância.
Referências
- PHP Manual: Métodos Mágicos
- PHP Manual: Sobrecarga de Propriedades e Métodos
- PHP Manual: __toString
- PHP Manual: __invoke
- PHP Manual: Construtores e Destrutores
Exercícios
Crie uma classe chamada Config que armazene configurações dinâmicas usando __get e __set, permitindo acesso a propriedades como $config->host.
✓ Resposta:class Config { private $dados = []; public function __get($name) { return $this->dados[$name] ?? null; } public function __set($name, $value) { $this->dados[$name] = $value; } } $config = new Config(); $config->host = 'localhost'; $config->port = 3306; echo $config->host; // localhostImplemente uma classe chamada Calculadora que use __call para delegar chamadas de métodos como 'somar', 'subtrair' etc. para funções internas.
✓ Resposta:class Calculadora { public function __call($name, $arguments) { if ($name === 'somar') { return array_sum($arguments); } elseif ($name === 'subtrair') { return $arguments[0] - $arguments[1]; } throw new \BadMethodCallException("Método $name não suportado"); } } $calc = new Calculadora(); echo $calc->somar(2, 3, 5); // 10 echo $calc->subtrair(10, 4); // 6Crie uma classe Pessoa com propriedades nome e idade, e implemente __toString para retornar "Nome: X, Idade: Y".
✓ Resposta:class Pessoa { public $nome; public $idade; public function __toString() { return "Nome: {$this->nome}, Idade: {$this->idade}"; } } $p = new Pessoa(); $p->nome = 'Maria'; $p->idade = 30; echo $p; // Nome: Maria, Idade: 30Implemente uma classe Multiplicador que armazena um fator e, ao ser invocada com um número, retorna o produto. Use __invoke.
✓ Resposta:class Multiplicador { private $fator; public function __construct($fator) { $this->fator = $fator; } public function __invoke($numero) { return $this->fator * $numero; } } $dobro = new Multiplicador(2); echo $dobro(5); // 10Escreva um exemplo que demonstre um cuidado com __toString: tente lançar uma exceção dentro de __toString e veja o que acontece. Explique por que não é recomendado.
✓ Resposta:class Problema { public function __toString() { throw new \Exception("Erro no __toString"); } } try { echo new Problema(); } catch (\Exception $e) { echo "Capturado: " . $e->getMessage(); } // Na prática, o PHP pode não capturar a exceção e gerar um erro fatal. // O manual recomenda que __toString nunca lance exceções, pois pode causar erros fatais em contextos como conversão implícita para string.