PHP Métodos Mágicos

Você já conhece um método especial: o __construct, que o PHP chama sozinho quando você escreve new. Ele não é o único. O PHP tem uma família de métodos com nomes que começam com dois sublinhados (__) e que ninguém chama diretamente: o próprio PHP os chama quando certa coisa acontece com o objeto. Eles se chamam métodos mágicos. Nesta aula você vai conhecer os mais usados, ver cada um funcionando e, tão importante quanto, aprender quando não usá-los.

Você vai aprender
  • O que é um método mágico e quando o PHP o chama.
  • __toString: o objeto vira texto num echo.
  • __get, __set, __isset e __unset: propriedades que não existem (ou não são visíveis).
  • __call e __callStatic: métodos que não existem.
  • __invoke: usar o objeto como se fosse uma função.
  • __clone: consertar a cópia feita pelo clone.
  • Os erros reais de cada um e quando é melhor escolher outro caminho.

Pré-requisitos: classes e objetos, construtores (inclusive a promoção de propriedades no construtor) e visibilidade (public e private). Os exemplos rodam na máquina fictícia da Ana, na pasta ~/poo, com o PHP 8.5.


A ideia antes do código: o porteiro automático

Pense na portaria de um prédio. Você não chama o porteiro para cada coisa: ele reage sozinho a certos acontecimentos. Tocou o interfone? Ele atende. Chegou uma encomenda para alguém que não mora ali? Ele decide o que fazer. Alguém pediu uma cópia da chave? Ele faz a cópia do jeito certo.

Os métodos mágicos são esse porteiro. Você escreve o método uma vez dentro da classe e o PHP o chama na hora certa:

Quando acontece isto……o PHP chama
new Produto(…)__construct (você já conhece)
o objeto é usado como texto (echo $p)__toString
alguém lê uma propriedade que não existe ou é private__get
alguém grava numa propriedade que não existe ou é private__set
alguém chama um método que não existe__call
alguém chama o objeto como função ($obj(10))__invoke
alguém faz clone $obj__clone (na cópia)
o objeto deixa de ser usado__destruct
Os nomes com __ são reservados

Todo método cujo nome começa com dois sublinhados é reservado para o PHP. Não invente os seus (__calcular, __salvar): se uma versão futura do PHP criar um método mágico com esse nome, o seu código passa a se comportar de outro jeito.


1. __toString: o objeto vira texto

O que acontece se a Ana tentar dar echo num objeto? Um número ou um texto o PHP sabe mostrar, mas um objeto pode ter várias propriedades: qual delas mostrar?

<?php
class Produto
{
    public function __construct(
        public string $nome,
        public float $preco,
    ) {}
}

$p = new Produto("Caderno", 12.5);
echo $p;
echo "fim\n";
$ php produto.php

Fatal error: Uncaught Error: Object of class Produto could not be converted to string in /home/ana/poo/produto.php:11
Stack trace:
#0 {main}
  thrown in /home/ana/poo/produto.php on line 11

"O objeto da classe Produto não pôde ser convertido para string". O PHP não adivinha: ele para o programa (o fim nem aparece). O método __toString ("para string") é a resposta: nele você diz como o objeto vira texto.

<?php
class Produto
{
    public function __construct(
        public string $nome,
        public float $preco,
    ) {}

    public function __toString(): string
    {
        return $this->nome . " (R$ " . number_format($this->preco, 2, ",", ".") . ")";
    }
}

$p = new Produto("Caderno", 12.5);
echo $p, "\n";
echo "Você comprou: $p\n";
$texto = "Item: " . $p;
echo $texto, "\n";
echo strlen($p), "\n";
var_dump($p instanceof Stringable);
echo (string) $p, "\n";
public function __toString(): string
O nome é fixo (__toString), o método precisa ser public, não recebe parâmetros e devolve uma string.
return $this->nome . …
Você monta o texto como quiser. Aqui: nome e preço formatado (veja Formatar Números).
echo $p
O PHP percebe que precisa de texto e chama $p->__toString() por você.
"Você comprou: $p"
Dentro de aspas duplas também (interpolação).
"Item: " . $p
No operador de concatenação ., em strlen() e em qualquer função que espera texto.
$p instanceof Stringable
Toda classe com __toString passa a implementar, automaticamente, a interface Stringable ("que pode virar string"). Veja Interfaces.
(string) $p
A conversão explícita (veja Conversão de Tipos) também chama o método.
$ php tostring.php
Caderno (R$ 12,50)
Você comprou: Caderno (R$ 12,50)
Item: Caderno (R$ 12,50)
18                          « strlen contou os bytes do texto (o "R$ " e o resto)
bool(true)
Caderno (R$ 12,50)

Repare que nenhuma linha chama __toString() pelo nome. É isso que faz dele "mágico". Funciona até em funções que recebem vários objetos, como implode:

<?php
class Tag
{
    public function __construct(private string $nome) {}
    public function __toString(): string
    {
        return "#" . $this->nome;
    }
}
$tags = [new Tag("php"), new Tag("poo")];
echo implode(" ", $tags), "\n";
$ php tags.php
#php #poo
Para que serve no dia a dia

Para objetos que têm uma representação "natural" em texto: um preço (R$ 12,50), um nome completo, uma data, um endereço de e-mail, um link. Se você precisaria pensar muito para decidir qual texto mostrar, prefira um método com nome claro, como descricao() ou resumo().

Missão: rode o código e veja o erro. Depois escreva o método __toString na classe Aluno para que o objeto vire o texto Ana tirou 9.5 (nome, a palavra "tirou" e a nota).

<?php
class Aluno
{
    public function __construct(
        public string $nome,
        public float $nota,
    ) {}

    // escreva o __toString aqui
}

$ana = new Aluno("Ana", 9.5);
echo $ana . "\n";
echo "Boletim: $ana\n";
<?php
class Aluno
{
    public function __construct(
        public string $nome,
        public float $nota,
    ) {}

    public function __toString(): string
    {
        return "$this->nome tirou $this->nota";
    }
}

$ana = new Aluno("Ana", 9.5);
echo $ana . "\n";
echo "Boletim: $ana\n";

2. __get e __set: propriedades que não existem

Primeiro, relembre o que o PHP faz sozinho quando você lê ou grava uma propriedade que a classe não declarou:

<?php
class Aluno
{
    public string $nome = "Ana";
}

$a = new Aluno();
echo $a->idade, "\n";
$a->idade = 29;
echo $a->idade, "\n";
$ php dinamica.php

Warning: Undefined property: Aluno::$idade in /home/ana/poo/dinamica.php on line 8


Deprecated: Creation of dynamic property Aluno::$idade is deprecated in /home/ana/poo/dinamica.php on line 9
29

Na leitura, um aviso de "propriedade indefinida". Na gravação, o PHP cria uma propriedade dinâmica na hora, mas avisa que isso está Deprecated (condenado: desde o PHP 8.2, vai deixar de funcionar no futuro). Os métodos __get ("pegar") e __set ("definir") deixam você decidir o que acontece nesses casos.

MétodoO PHP chama quando…RecebeDevolve
__get($nome)alguém lê $obj->nomeo nome da propriedadeo valor a entregar
__set($nome, $valor)alguém grava $obj->nome = valoro nome e o valornada (void)
__isset($nome)alguém usa isset($obj->nome) ou empty()o nometrue ou false
__unset($nome)alguém usa unset($obj->nome)o nomenada

Eles só entram em ação quando a propriedade não existe ou não é visível de onde o código está (uma private acessada de fora). Se a propriedade existe e é public, o PHP usa a propriedade direto e nenhum método mágico é chamado.

Um exemplo: uma "ficha" que aceita qualquer campo e guarda tudo num array. Os echo entre colchetes estão ali só para você ver quando o PHP chama cada método:

<?php
class Ficha
{
    private array $dados = [];

    public function __get(string $nome): mixed
    {
        echo "[__get chamado para '$nome']\n";
        return $this->dados[$nome] ?? null;
    }

    public function __set(string $nome, mixed $valor): void
    {
        echo "[__set chamado: '$nome' = '$valor']\n";
        $this->dados[$nome] = $valor;
    }

    public function __isset(string $nome): bool
    {
        return isset($this->dados[$nome]);
    }

    public function __unset(string $nome): void
    {
        unset($this->dados[$nome]);
    }
}

$f = new Ficha();
$f->nome = "Ana";
$f->cidade = "Recife";
echo $f->nome . " mora em " . $f->cidade . "\n";
var_dump(isset($f->cidade));
unset($f->cidade);
var_dump(isset($f->cidade));
var_dump($f->telefone);
$ php getset.php
[__set chamado: 'nome' = 'Ana']          « $f->nome = "Ana" não existe: foi para o __set
[__set chamado: 'cidade' = 'Recife']
[__get chamado para 'nome']              « o PHP monta a frase inteira antes do echo
[__get chamado para 'cidade']
Ana mora em Recife
bool(true)                               « __isset respondeu
bool(false)                              « depois do unset (via __unset)
[__get chamado para 'telefone']
NULL                                     « o ?? null do __get
private array $dados = [];
O lugar onde os valores ficam de verdade. Ninguém de fora enxerga.
__get(string $nome): mixed
Recebe o nome da propriedade pedida, como texto. mixed quer dizer "pode devolver qualquer tipo".
$this->dados[$nome] ?? null
Devolve o valor guardado ou null se não existir (veja ??).
__set(string $nome, mixed $valor): void
Recebe o nome e o valor. Aqui ele guarda no array, mas poderia validar, recusar, converter…

Um uso de verdade: só leitura de fora

Com __get e sem __set, dá para deixar uma propriedade private legível de fora, mas não gravável. E dá para dar uma mensagem melhor quando alguém erra o nome:

<?php
class Conta
{
    private float $saldo = 100.0;

    public function __get(string $nome): mixed
    {
        if ($nome === "saldo") {
            return $this->saldo;
        }
        throw new Error("A propriedade '$nome' não existe em Conta");
    }
}

$c = new Conta();
echo $c->saldo, "\n";
$c->saldo = 1000000;
$ php conta.php
100                « leitura: o __get entregou o saldo

Fatal error: Uncaught Error: Cannot access private property Conta::$saldo in /home/ana/poo/conta.php:17
Stack trace:
#0 {main}
  thrown in /home/ana/poo/conta.php on line 17

A leitura funcionou pelo __get. A gravação não tem __set, então o PHP fez o normal: recusou mexer numa propriedade private ("Não é possível acessar a propriedade privada"). A Ana não ganha um milhão.

No PHP moderno existe um jeito melhor

Esse "lê de fora, só grava por dentro" hoje se escreve sem mágica nenhuma: com public private(set) ou readonly, e regras de leitura e gravação com property hooks PHP 8.4+. O editor e as ferramentas enxergam essas propriedades; já as do __get são invisíveis para eles. Veja readonly e Property Hooks.


3. __call e __callStatic: métodos que não existem

Chamar um método que a classe não tem é um erro fatal:

<?php
class Loja
{
}
$loja = new Loja();
$loja->precoCaderno();
$ php call-sem.php

Fatal error: Uncaught Error: Call to undefined method Loja::precoCaderno() in /home/ana/poo/call-sem.php:6
Stack trace:
#0 {main}
  thrown in /home/ana/poo/call-sem.php on line 6

"Chamada a um método indefinido". Se a classe tiver __call ("chamar"), o PHP, em vez de parar, chama o __call com o nome do método e um array com os argumentos. O irmão __callStatic faz o mesmo para chamadas estáticas (Loja::algo(), veja static).

<?php
class Loja
{
    private array $precos = ["caderno" => 12.5, "caneta" => 3.0];

    public function __call(string $metodo, array $argumentos): mixed
    {
        echo "[__call: $metodo(" . implode(", ", $argumentos) . ")]\n";
        if (str_starts_with($metodo, "preco")) {
            $item = strtolower(substr($metodo, 5));
            return $this->precos[$item] ?? 0.0;
        }
        throw new BadMethodCallException("Método Loja::$metodo() não existe");
    }

    public static function __callStatic(string $metodo, array $argumentos): string
    {
        return "estático: $metodo";
    }
}

$loja = new Loja();
echo $loja->precoCaderno(), "\n";
echo $loja->precoCaneta(), "\n";
echo $loja->precoBorracha(), "\n";
echo Loja::abrir(), "\n";
echo $loja->vender("caderno", 2), "\n";
$ php call.php
[__call: precoCaderno()]
12.5
[__call: precoCaneta()]
3
[__call: precoBorracha()]
0                              « não está no array: o ?? 0.0
estático: abrir                « Loja::abrir() foi para o __callStatic
[__call: vender(caderno, 2)]   « os argumentos chegam no array

Fatal error: Uncaught BadMethodCallException: Método Loja::vender() não existe in /home/ana/poo/call.php:13
Stack trace:
#0 /home/ana/poo/call.php(27): Loja->__call('vender', Array)
#1 {main}
  thrown in /home/ana/poo/call.php on line 13
__call(string $metodo, array $argumentos)
$metodo é o nome que foi chamado ("precoCaderno"); $argumentos é um array com o que veio nos parênteses (["caderno", 2]).
str_starts_with($metodo, "preco")
"O nome começa com preco?" Se sim, o resto do nome (substr($metodo, 5), a partir da 6ª letra) vira o item procurado.
throw new BadMethodCallException(…)
Para qualquer outro nome, o __call lança uma exceção. Sem isso, um erro de digitação ($loja->precoCadreno()) passaria em silêncio. Veja Exceções.
Sempre trate o "nome desconhecido" no __call e no __get

Um __call que aceita qualquer nome transforma todo erro de digitação num bug silencioso. Termine sempre com um throw para os nomes que você não espera. O mesmo vale para o __get.


4. __invoke: o objeto vira uma função

Quando uma classe tem __invoke ("invocar"), você pode colocar parênteses depois do objeto, como se ele fosse uma função. É útil quando uma "função" precisa guardar uma configuração. Exemplo: a lojinha da Ana tem descontos de 10%, 15%…

<?php
class Desconto
{
    public function __construct(private float $porcentagem) {}

    public function __invoke(float $preco): float
    {
        return $preco - $preco * $this->porcentagem / 100;
    }
}

$dezPorCento = new Desconto(10);
echo $dezPorCento(50), "\n";
echo $dezPorCento(200), "\n";

$precos = [50, 120, 9.9];
print_r(array_map($dezPorCento, $precos));
var_dump(is_callable($dezPorCento));
$ php invoke.php
45
180
Array
(
    [0] => 45
    [1] => 108
    [2] => 8.91
)
bool(true)
new Desconto(10)
Cria o objeto e guarda a configuração (10%).
$dezPorCento(50)
Parênteses depois do objeto: o PHP chama $dezPorCento->__invoke(50).
array_map($dezPorCento, $precos)
Como o objeto é "chamável" (callable), ele pode ir para qualquer função que espera uma função, como array_map (veja Funções de Array).
is_callable($dezPorCento)
"Pode ser chamado?" true.

Sem o __invoke, o PHP recusa: Fatal error: Uncaught Error: Object of type Desconto is not callable ("objeto do tipo Desconto não é chamável").

Para coisas pequenas, uma função anônima resolve o mesmo problema (fn($preco) => $preco * 0.9). Prefira a classe com __invoke quando a "função" tem nome, configuração e merece ser testada sozinha.


5. __clone: consertar a cópia

Lembre: atribuir um objeto a outra variável não copia o objeto; as duas variáveis apontam para o mesmo. Para ter uma cópia, use clone. Só que o clone faz uma cópia rasa: as propriedades que guardam outros objetos continuam apontando para os mesmos objetos de antes.

<?php
class Endereco
{
    public function __construct(public string $cidade) {}
}

class Cliente
{
    public function __construct(
        public string $nome,
        public Endereco $endereco,
    ) {}
}

$ana = new Cliente("Ana", new Endereco("Recife"));
$bia = clone $ana;
$bia->nome = "Bia";
$bia->endereco->cidade = "Natal";

echo $ana->nome, " mora em ", $ana->endereco->cidade, "\n";
echo $bia->nome, " mora em ", $bia->endereco->cidade, "\n";
$ php clone-raso.php
Ana mora em Natal      « a Ana "se mudou" sem querer!
Bia mora em Natal

O nome foi copiado (é uma string), mas o Endereco é o mesmo objeto nas duas fichas. Mudar a cidade da Bia mudou a da Ana. O __clone resolve: o PHP o chama na cópia, logo depois de copiar, e ali você clona também os objetos de dentro (uma cópia profunda).

Missão: rode e veja a Ana "se mudar" para Natal. Depois acrescente à classe Cliente um método public function __clone(): void que faça $this->endereco = clone $this->endereco;. A Ana deve continuar em Recife.

<?php
class Endereco
{
    public function __construct(public string $cidade) {}
}

class Cliente
{
    public function __construct(
        public string $nome,
        public Endereco $endereco,
    ) {}
}

$ana = new Cliente("Ana", new Endereco("Recife"));
$bia = clone $ana;
$bia->nome = "Bia";
$bia->endereco->cidade = "Natal";

echo $ana->nome, " mora em ", $ana->endereco->cidade, "\n";
echo $bia->nome, " mora em ", $bia->endereco->cidade, "\n";
<?php
class Endereco
{
    public function __construct(public string $cidade) {}
}

class Cliente
{
    public function __construct(
        public string $nome,
        public Endereco $endereco,
    ) {}

    public function __clone(): void
    {
        $this->endereco = clone $this->endereco;
    }
}

$ana = new Cliente("Ana", new Endereco("Recife"));
$bia = clone $ana;
$bia->nome = "Bia";
$bia->endereco->cidade = "Natal";

echo $ana->nome, " mora em ", $ana->endereco->cidade, "\n";
echo $bia->nome, " mora em ", $bia->endereco->cidade, "\n";

Com o __clone, a saída fica:

$ php clone-fundo.php
Ana mora em Recife
Bia mora em Natal
public function __clone(): void
Roda na cópia (o $this aqui é a Bia, não a Ana), depois que o PHP copiou as propriedades.
$this->endereco = clone $this->endereco;
Troca o endereço compartilhado por uma cópia só da Bia.

O clone tem novidades no PHP 8.5 (clone com novos valores) e cuidados especiais com propriedades readonly: veja readonly e Property Hooks.


Outros métodos mágicos

Você vai encontrar estes com menos frequência. Dois merecem um exemplo rápido.

__destruct: o objeto vai embora

O contrário do construtor: o PHP chama quando o objeto deixa de ser usado (um unset, a variável sai de escopo ou o programa termina). Serve para "fechar" algo que o objeto abriu.

<?php
class Arquivo
{
    public function __construct(private string $nome)
    {
        echo "Abrindo $this->nome\n";
    }

    public function __destruct()
    {
        echo "Fechando $this->nome\n";
    }
}

$a = new Arquivo("notas.txt");
echo "Trabalhando...\n";
unset($a);
echo "Fim do programa\n";
$ php destruct.php
Abrindo notas.txt
Trabalhando...
Fechando notas.txt     « no unset, antes do "Fim"
Fim do programa

__debugInfo: o que o var_dump mostra

Por padrão, o var_dump mostra tudo, inclusive o que é private. Num objeto com senha, isso é perigoso num log:

$ php debug-sem.php
object(Usuario)#1 (2) {
  ["email"]=>
  string(15) "ana@exemplo.com"
  ["senha":"Usuario":private]=>
  string(10) "segredo123"
}

Com __debugInfo, você devolve um array com o que deve aparecer:

<?php
class Usuario
{
    public function __construct(
        public string $email,
        private string $senha,
    ) {}

    public function __debugInfo(): array
    {
        return ["email" => $this->email, "senha" => "***"];
    }
}
var_dump(new Usuario("ana@exemplo.com", "segredo123"));
$ php debuginfo.php
object(Usuario)#1 (2) {
  ["email"]=>
  string(15) "ana@exemplo.com"
  ["senha"]=>
  string(3) "***"
}

E ainda: __serialize/__unserialize (o que o serialize() guarda do objeto), __set_state (usado por var_export) e os antigos __sleep/__wakeup, substituídos pelo par __serialize/__unserialize em código novo.


Quando usar e quando evitar

MétodoBom usoCuidado
__toStringObjetos com um texto "natural" (preço, nome, data, link)Não coloque lógica pesada nem nada que possa falhar
__invokeUma "função com configuração" que vai para array_map, usort…Para algo pequeno, uma arrow function basta
__cloneCópia profunda de objetos com objetos dentroLembre que roda na cópia
__get/__setObjetos de dados realmente flexíveis (uma ficha, uma configuração)Invisíveis para o editor, sem autocompletar, erros de digitação passam; prefira property hooks e private(set)
__call"Repassar" chamadas para outro objeto, bibliotecasMesmo problema: o editor não sabe que o método existe; trate nomes desconhecidos com throw

A regra geral: use mágica quando ela deixa o código mais claro para quem usa a classe. Se quem lê precisa abrir a classe para descobrir que $loja->precoCaderno() "existe", um método normal $loja->preco("caderno") é melhor. Métodos mágicos também são um pouco mais lentos que métodos e propriedades comuns.


Quando dá errado

__toString devolvendo um número
<?php
declare(strict_types=1);
class Nota
{
    public function __construct(public int $valor) {}

    public function __toString(): string
    {
        return $this->valor;
    }
}
echo new Nota(9), "\n";
$ php nota.php

Fatal error: Uncaught TypeError: Nota::__toString(): Return value must be of type string, int returned in /home/ana/poo/nota.php:9
Stack trace:
#0 /home/ana/poo/nota.php(12): Nota->__toString()
#1 {main}
  thrown in /home/ana/poo/nota.php on line 9

O que significa: "o valor de retorno precisa ser do tipo string, foi devolvido um int". Com declare(strict_types=1) (veja Tipos e Retorno), o PHP não converte o número sozinho. Sem o declare, ele converteria e mostraria 9.

Como consertar: devolva texto de verdade: return (string) $this->valor; ou return "Nota: $this->valor";.

Método mágico que não é public
<?php
class Caixa
{
    private function __get(string $nome): mixed
    {
        return 42;
    }
}
echo (new Caixa())->x, "\n";
$ php caixa.php

Warning: The magic method Caixa::__get() must have public visibility in /home/ana/poo/caixa.php on line 4
42

O que significa: "o método mágico precisa ter visibilidade pública". O PHP avisa e usa o método mesmo assim, mas o aviso aparece em toda execução.

Como consertar: declare os métodos mágicos sempre como public.

Mudar um array devolvido pelo __get não muda nada
<?php
class Carrinho
{
    private array $dados = ["itens" => []];

    public function __get(string $nome): mixed
    {
        return $this->dados[$nome];
    }

    public function __set(string $nome, mixed $valor): void
    {
        $this->dados[$nome] = $valor;
    }
}

$c = new Carrinho();
$c->itens[] = "caderno";
print_r($c->itens);
$ php carrinho.php

Notice: Indirect modification of overloaded property Carrinho::$itens has no effect in /home/ana/poo/carrinho.php on line 18
Array
(
)

O que significa: "a modificação indireta de uma propriedade sobrecarregada não tem efeito". O __get devolveu uma cópia do array; o [] = "caderno" entrou na cópia, que foi jogada fora. O carrinho continua vazio.

Como consertar: crie um método com nome claro, como public function adicionar(string $item): void { $this->dados["itens"][] = $item; }. É mais um motivo para preferir métodos e propriedades declaradas à mágica.

Erro de digitação "engolido"

Se o __get da classe Ficha lá de cima recebe $f->cidde (faltou um "a"), ele devolve null e o programa segue sem nenhum aviso, mostrando um texto vazio. Por isso a Conta lança uma exceção para nomes desconhecidos:

$ php conta-erro.php

Fatal error: Uncaught Error: A propriedade 'sado' não existe em Conta in /home/ana/poo/conta-erro.php:11
Stack trace:
#0 /home/ana/poo/conta-erro.php(16): Conta->__get('sado')
#1 {main}
  thrown in /home/ana/poo/conta-erro.php on line 11

Agora o erro aparece, com a linha (16) e o nome errado ('sado'). Um erro barulhento é muito melhor que um silencioso.


Referência rápida

MétodoAssinaturaChamado quando
__construct__construct(…)new Classe(…)
__destruct__destruct()o objeto deixa de existir
__toString__toString(): stringo objeto é usado como texto
__get__get(string $nome): mixedleitura de propriedade inexistente ou invisível
__set__set(string $nome, mixed $valor): voidgravação de propriedade inexistente ou invisível
__isset__isset(string $nome): boolisset() / empty() nela
__unset__unset(string $nome): voidunset() nela
__call__call(string $metodo, array $args): mixedmétodo inexistente ou invisível no objeto
__callStaticstatic __callStatic(string $metodo, array $args): mixedo mesmo, em Classe::metodo()
__invoke__invoke(…)$obj(…)
__clone__clone(): voidna cópia, depois de clone $obj
__debugInfo__debugInfo(): arrayvar_dump($obj)
__serialize / __unserialize__serialize(): arrayserialize() / unserialize()

Missão final

Missão: a classe Saudacao guarda um cumprimento ("Olá"). Escreva o método __invoke(string $nome): string para que $ola("Ana") devolva Olá, Ana!. O mesmo objeto deve funcionar no array_map, cumprimentando a turma inteira.

<?php
class Saudacao
{
    public function __construct(private string $cumprimento) {}

    // escreva o __invoke aqui
}

$ola = new Saudacao("Olá");
echo $ola("Ana"), "\n";

$turma = ["Bia", "Caio", "Duda"];
foreach (array_map($ola, $turma) as $frase) {
    echo $frase, "\n";
}
<?php
class Saudacao
{
    public function __construct(private string $cumprimento) {}

    public function __invoke(string $nome): string
    {
        return "$this->cumprimento, $nome!";
    }
}

$ola = new Saudacao("Olá");
echo $ola("Ana"), "\n";

$turma = ["Bia", "Caio", "Duda"];
foreach (array_map($ola, $turma) as $frase) {
    echo $frase, "\n";
}

Exercícios

1. O nome do método

Complete com o método mágico que o PHP chama quando o objeto aparece num echo:

public function (): string

2. Objeto chamável

Complete para que $dobro(5) funcione e devolva 10:

public function (int $n): int { return $n * 2; }

3. Cópia profunda

Dentro do __clone, complete a linha que copia também o objeto guardado em $this->endereco:

$this->endereco = ;

4. A classe Ficha tem public string $nome e também um método __get. O que acontece em echo $f->nome;?

5. Quais são os dois parâmetros que o __call recebe?

6. Depois de $bia = clone $ana;, o __clone roda em qual objeto?

7. Por que é bom terminar o __call com um throw para nomes desconhecidos?


Resumo

  • Métodos mágicos começam com __, são public e o PHP os chama quando algo acontece com o objeto.
  • __toString: como o objeto vira texto (echo, interpolação, .). Sem ele, Object of class … could not be converted to string.
  • __get/__set/__isset/__unset: propriedades inexistentes ou invisíveis. Hoje, prefira propriedades declaradas, private(set) e property hooks.
  • __call/__callStatic: métodos inexistentes. Sem eles, Call to undefined method.
  • __invoke: $obj(…), e o objeto passa a valer como função.
  • __clone: roda na cópia; use para clonar os objetos de dentro (cópia profunda).
  • Mágica boa deixa a classe mais fácil de usar. Mágica ruim esconde o que existe e engole erros de digitação.