Antes do PHP 8.4, uma propriedade só tinha uma visibilidade só: se ela era public, qualquer código de fora podia ler e escrever nela; para impedir a escrita externa, a única saída era torná-la private e criar um getter público. A visibilidade assimétrica resolve isso permitindo declarar visibilidades diferentes para leitura e escrita na mesma propriedade, usando a sintaxe private(set) ou protected(set) junto da visibilidade de leitura.
Como funciona
A visibilidade de escrita vem entre a visibilidade de leitura e o tipo, sempre seguida de (set). A regra é simples: a visibilidade de escrita precisa ser igual ou mais restrita que a de leitura, e a propriedade precisa ter um tipo declarado:
class Produto
{
public function __construct(
public private(set) string $nome,
public private(set) float $preco,
) {}
}
$produto = new Produto('Teclado Mecânico', 350.0);
echo $produto->nome; // Teclado Mecânico — leitura funciona de fora
$produto->nome = 'Outro nome'; // Error: Cannot modify private(set) property
Qualquer código pode ler $produto->nome, mas só a própria classe Produto pode atribuir um novo valor a ela — sem precisar escrever um getter só para expor a leitura.
Forma curta: omitindo o "public"
Como o caso mais comum é "leitura pública, escrita privada", o PHP permite omitir o public e escrever só private(set), que já implica leitura pública:
class Pedido
{
private(set) string $status = 'pendente';
public function confirmar(): void
{
$this->status = 'confirmado';
}
}
$pedido = new Pedido();
echo $pedido->status; // pendente
$pedido->confirmar();
echo $pedido->status; // confirmado
$pedido->status = 'cancelado'; // Error: Cannot modify private(set) property
protected(set) para hierarquias
Quando subclasses também precisam poder escrever na propriedade, mas o mundo externo continua só podendo ler, o certo é protected(set):
class Conta
{
public protected(set) float $saldo = 0.0;
public function depositar(float $valor): void
{
$this->saldo += $valor;
}
}
class ContaPremium extends Conta
{
public function aplicarBonus(float $valor): void
{
$this->saldo += $valor * 1.1; // subclasse pode escrever
}
}
$conta = new ContaPremium();
$conta->depositar(100.0);
echo $conta->saldo; // 100 — leitura pública funciona normalmente
Combinando com readonly
Visibilidade assimétrica e readonly resolvem problemas diferentes: readonly trava a propriedade depois da primeira atribuição (mesmo dentro da própria classe); private(set) permite reatribuições internas ilimitadas, só bloqueando escrita externa. Combinar os dois cobre o caso de um objeto que muda de estado internamente, mas nunca aceita reatribuição direta de fora nem depois de inicializado uma vez fora do construtor:
class Contador
{
public private(set) int $valor = 0;
public function incrementar(): void
{
$this->valor++; // permitido: escrita interna, sem readonly
}
}
Quando usar
- Objetos de valor e DTOs em que os dados devem ser lidos livremente, mas nunca alterados por quem os recebe.
- Substituir getters que só devolviam
$this->propriedadesem nenhuma lógica extra, eliminando boilerplate. - Expor um contador, status ou saldo que só a própria classe (ou subclasses, com
protected(set)) deve poder modificar. - Deixar explícito, na assinatura da propriedade, qual é o contrato de mutabilidade da classe — sem depender de convenção ou documentação.
Cuidados
A visibilidade assimétrica exige tipo declarado na propriedade — não funciona em propriedades sem tipo. Ela também não pode ser aplicada a propriedades static. Interfaces podem declarar visibilidade de leitura em uma propriedade abstrata, mas isso não é o mesmo que promessa de imutabilidade: sem readonly, o valor ainda pode mudar internamente a qualquer momento — a garantia é só sobre quem pode escrever, não sobre quantas vezes o valor pode mudar.
Compatibilidade
Visibilidade assimétrica foi introduzida no PHP 8.4 (lançado em novembro de 2024) e exige essa versão ou superior; não há como fazer polyfill dessa sintaxe em versões anteriores.
Conclusão
Visibilidade assimétrica fecha uma lacuna que existia há anos no sistema de tipos do PHP: até então, expor leitura pública sem abrir mão do controle sobre a escrita exigia getters inteiros só para isso. Combinada com property hooks e constructor promotion, ela deixa classes de dados mais enxutas e comunica o contrato de mutabilidade direto na assinatura da propriedade.
0 Comentário
Deixe seu Comentário!
Dúvida, sugestão ou só um "valeu!" — tudo é bem-vindo por aqui.