Dominando Magic Methods em PHP: __get, __set, __call e Outros em Projetos Reais Já leu

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 ( get() set() php 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 (!filtervar($valor, FILTERVALIDATEEMAIL)) { 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 get set call() callStatic() php 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 (inarray($metodo, $niveis)) { $this->log($metodo, $argumentos[0] ?? ''); }

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.

Referências


Artigos relacionados