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.
- O que é um método mágico e quando o PHP o chama.
__toString: o objeto vira texto numecho.__get,__set,__issete__unset: propriedades que não existem (ou não são visíveis).__calle__callStatic: métodos que não existem.__invoke: usar o objeto como se fosse uma função.__clone: consertar a cópia feita peloclone.- 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 |
__ 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 serpublic, 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
., emstrlen()e em qualquer função que espera texto. - $p instanceof Stringable
- Toda classe com
__toStringpassa a implementar, automaticamente, a interfaceStringable("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 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étodo | O PHP chama quando… | Recebe | Devolve |
|---|---|---|---|
__get($nome) | alguém lê $obj->nome | o nome da propriedade | o valor a entregar |
__set($nome, $valor) | alguém grava $obj->nome = valor | o nome e o valor | nada (void) |
__isset($nome) | alguém usa isset($obj->nome) ou empty() | o nome | true ou false |
__unset($nome) | alguém usa unset($obj->nome) | o nome | nada |
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.
mixedquer dizer "pode devolver qualquer tipo". - $this->dados[$nome] ?? null
- Devolve o valor guardado ou
nullse 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.
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
__calllança uma exceção. Sem isso, um erro de digitação ($loja->precoCadreno()) passaria em silêncio. Veja Exceções.
__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
$thisaqui é 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étodo | Bom uso | Cuidado |
|---|---|---|
__toString | Objetos com um texto "natural" (preço, nome, data, link) | Não coloque lógica pesada nem nada que possa falhar |
__invoke | Uma "função com configuração" que vai para array_map, usort… | Para algo pequeno, uma arrow function basta |
__clone | Cópia profunda de objetos com objetos dentro | Lembre que roda na cópia |
__get/__set | Objetos 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, bibliotecas | Mesmo 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";.
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.
__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.
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étodo | Assinatura | Chamado quando |
|---|---|---|
__construct | __construct(…) | new Classe(…) |
__destruct | __destruct() | o objeto deixa de existir |
__toString | __toString(): string | o objeto é usado como texto |
__get | __get(string $nome): mixed | leitura de propriedade inexistente ou invisível |
__set | __set(string $nome, mixed $valor): void | gravação de propriedade inexistente ou invisível |
__isset | __isset(string $nome): bool | isset() / empty() nela |
__unset | __unset(string $nome): void | unset() nela |
__call | __call(string $metodo, array $args): mixed | método inexistente ou invisível no objeto |
__callStatic | static __callStatic(string $metodo, array $args): mixed | o mesmo, em Classe::metodo() |
__invoke | __invoke(…) | $obj(…) |
__clone | __clone(): void | na cópia, depois de clone $obj |
__debugInfo | __debugInfo(): array | var_dump($obj) |
__serialize / __unserialize | __serialize(): array | serialize() / 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:
2. Objeto chamável
Complete para que $dobro(5) funcione e devolva 10:
3. Cópia profunda
Dentro do __clone, complete a linha que copia também o objeto guardado em $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ãopublice 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.