Os métodos mágicos no PHP são métodos "especiais" que são invocadas implicitamente quando certas ações ocorrem. Neste post, exploraremos esses métodos, como eles funcionam e como podem ser úteis.
Métodos mágicos: Uma visão geral
PHP é uma linguagem de programação que suporta, dentre outros, o paradigma de programação orientada a objetos. Sendo assim, podemos definir métodos dentro das nossas classes, ou seja, funções que operam no contexto de um objeto. Métodos mágicos são aqueles que podemos definir em nosso código e são acionados automaticamente pelo PHP em situações específicas.
Alguns exemplos de quando os métodos mágicos podem ser acionados incluem tentativas de acesso a membros inexistentes ou inacessíveis de uma classe, conversões de objetos e verificações especiais.
Exemplo prático
Um exemplo claro dos métodos mágicos em ação é o caso do framework Laravel. Considere uma classe de modelo (Model) que lida com a persistência de um usuário:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{ }
Apesar de não definir nenhuma propriedade explicitamente, podemos atribuir valores a propriedades como se elas existissem:
<?php
use App\Models\User;
$user = new User();
$user->name = 'Vinicius Dias';
$user->email = '[email protected]';
$user->save();
O método save
é entendido como parte da classe base Model
, mas e quanto às propriedades? Para cenários como esse, os métodos mágicos foram concebidos.
Implementação de métodos mágicos
O nome de todo método mágico no PHP começa com dois underscores (__
). Por exemplo, o método __set
é invocado quando tentamos atribuir um valor a uma propriedade inexistente ou inacessível. Ele recebe o nome da propriedade e o valor como parâmetros:
<?php
class NossaModel
{
/**
* @var array<string, mixed> Array associativo contendo todos os valores que definirmos
*/
private array $atributos;
/**
* @param string $name Nome da propriedade inacessível que estamos tentando definir
* @param mixed $value Valor que estamos tentando atribuir a essa propriedade
* @return void
*/
public function __set(string $name, mixed $value): void
{
/*
* Aqui, se fizermos $model->teste = 'Valor', criaremos a chave 'teste'
* na propriedade $atributos com o valor 'Valor'
*/
$this->atributos[$name] = $value;
}
}
Repare que o código que escrevemos no método é código PHP como qualquer outro. Ali nós poderíamos realizar qualquer tipo de lógica.
Como acessar esse valor?
Aprendemos a definir um valor a partir de uma propriedade inexistente, mas se tentarmos ler esse valor, receberemos um erro. O código a seguir, por exemplo, falha:
$teste = new NossaModel();
$teste->nome = 'Vinicius';
/*
* Na linha a seguir, o retorno será nulo e um _Warning_ será emitido
* dizendo que a propriedade NossaModel::$nome não existe.
*/
echo $teste->nome;
Para escrever em propriedades inexistentes, usamos o __set
. Já para ler de propriedades inexistentes ou inacessíveis, utilizamos o método __get
. Então poderíamos ter o seguinte método na classe NossaModel
:
/**
* @param string $name Nome da propriedade inexistente que estamos tentando ler
* @return mixed Aqui devemos retornar o valor que corresponderá a essa propriedade
*/
public function __get(string $name): mixed
{
// O valor retornado pelo método __get é utilizado onde tentamos acessar a propriedade
return $this->atributos[$name];
}
Com isso, o código a seguir funcionaria sem problemas:
$teste = new NossaModel();
// Chamada implícita a $teste->__set('nome', 'Vinicius');
$teste->nome = 'Vinicius';
// Chamada implícita a $teste->__get('nome'), que vai retornar 'Vinicius'
echo $teste->nome;
Mais métodos
Até aqui nós citamos apenas 2 métodos mágicos, mas o PHP fornece diversos outros. Vou deixar a seguir uma lista deles:
Construtor e destrutor
O método construtor, chamado de __construct
, é executado quando criamos um novo objeto (utilizando new
). Normalmente utilizamos o método construtor para receber dependências e inicializar as propriedades.
Já o método destrutor, __destruct
, é executado quando um objeto sai de escopo ou quando o objeto é passado para a função unset
. Normalmente ele é utilizado para liberar recursos que tenham sido alocados no construtor, como fechar arquivos, conexões, etc. Com a natureza stateless do PHP, esse método não é tão utilizado.
Conversões
Em alguns cenários nós podemos querer utilizar nossos objetos como um tipo diferente. Se quisermos usar nosso objeto como uma string, por exemplo, podemos utilizar o método __toString
. O que nós retornarmos nesse método é utilizado se nosso objeto for convertido para string de forma implícita ou explícita. Objetos de resposta HTTP, por exemplo, geralmente implementam esse método retornando o corpo da resposta.
<?php
class Response
{
private string $body;
// Outras propriedades
public function __toString() : string
{
return $this->body;
}
}
$response = new Response();
// Definição dos valores
echo $response; // Isso usa o objeto como string, logo, chama o __toString
Há também casos onde queremos utilizar nosso objeto como se ele fosse uma função. Para isso existe o método __invoke
. Se nós passarmos nosso objeto por parâmetro em algum lugar que espera um callable
, o método __invoke
será executado. Exemplo:
<?php
class ControllerDeExemplo
{
public function __invoke(int $parametro1, string $parametro2): void
{
// Execução de exemplo
}
}
$controller = new ControllerDeExemplo();
$controller(1, 'Teste'); // Isso executará o método __invoke
Chamada de métodos
Assim como nós podemos acessar propriedades inexistentes (ou inacessíveis) com métodos mágicos, o mesmo serve para métodos. Se você tentar chamar um método não existente de um objeto, o método __call
será executado, recebendo o nome do método que tentou ser executado e todos os seus parâmetros. Já o __callStatic
funciona da mesma forma, mas caso um método estático inexistente seja chamado a partir de uma classe.
class Exemplo
{
public function __call(string $name, array $arguments): mixed
{
/*
* $name possui o nome do método inexistente que foi executado,
* enquanto $arguments possui todos os parâmetros que foram passados para esse método
*/
}
public static function __callStatic(string $name, array $arguments): mixed
{
// Equivalente estático ao __call
}
}
// Chama implicitamente Exemplo::__callStatic('qualquerCoisa', [1, 2]):
Exemplo::qualquerCoisa(1, 2);
$objeto = new Exemplo();
// Chama implicitamente $objeto->__call('qualquerCoisa', [1, 2]):
$objeto->qualquerCoisa(1, 2);
Restante dos métodos
Para que esse post não fique ainda maior, vou deixar a seguir uma lista dos demais métodos mágicos com uma breve descrição, mas caso você queira ver mais exemplos e detalhes, pode conferir a documentação do PHP.
__isset
: Método executado caso passemos alguma propriedade inacessível do nosso objeto para a funçãoisset
ouempty
;__unset
: Método executado caso passemos alguma propriedade inacessível do nosso objeto para a funçãounset
;__sleep
: Ao chamar a funçãoserialize
passando nosso objeto, esse método é executado antes da serialização. Seu retorno deve ser um array contendo os nomes das propriedades do objeto em questão que serão serializadas. Normalmente é utilizado para remover propriedades que não devem ser serializadas com conexões com o banco, arquivos abertos, etc;__wakeup
: Esse método não recebe parâmetros e é executado após um dado ser desserializado e o objeto ser criado. Esse método geralmente reconstrói os recursos como conexões com o banco;__serialize
: Similar ao__sleep
. Se ambos estiverem definidos, apenas__serialize
é chamado e__sleep
é ignorado. A diferença é que o__serialize
deve retornar um array associativo que será serializado ao invés de retornar os nomes das propriedades do objeto;__unserialize
: Similar ao__wakeup
. Se ambos estiverem definidos, apenas__unserialize
é chamado e__wakeup
é ignorado.__unserialize
recebe por parâmetro o array que foi serializado e com isso, deve reconstruir o objeto;static __set_state
: Esse método é o mais raro. Ele é utilizado para criar um objeto da classe em questão a partir da funçãovar_export
;__clone
: Método chamado quando um clone de um objeto é criado.$this
nesse método vai se referir ao novo objeto criado. Normalmente esse método é definido para evitar shallow copies e realizar os clones de todas as propriedades também;__debugInfo
: O retorno desse método deve conter os dados que serão exibidos pela funçãovar_dump
. Geralmente é um array associativo;
Conclusão
O PHP possui um grande número de métodos mágicos. Alguns são bastante fáceis de entender como __get
, __set
e __call
, enquanto outros são mais confusos como as diferenças entre __sleep
e __serialize
. Há ainda aqueles bem pouco usados como o __set_state
.
Lembrar de todos os métodos (e suas assinaturas) e o que cada um deles faz não é necessário, mas é importante saber da existência deles para saber o que pesquisar quando for necessário.
Se quiser aprender mais sobre PHP e sobre tecnologia em geral, vou deixar aqui um cupom de 15% de desconto na Alura, plataforma onde eu gravei inúmeros cursos.