Modificadores de acesso: public, protected, private

1. Introdução aos modificadores de acesso em PHP

Os modificadores de acesso são um dos pilares da programação orientada a objetos em PHP. Eles controlam a visibilidade e o acesso a propriedades e métodos de uma classe, implementando o princípio de encapsulamento. O encapsulamento permite que você esconda detalhes internos de implementação e exponha apenas o que é necessário para o uso externo da classe.

PHP oferece três níveis de modificadores de acesso:
- public: acesso irrestrito de qualquer lugar
- protected: acesso permitido apenas na própria classe e em classes filhas
- private: acesso restrito exclusivamente à própria classe

Dominar esses modificadores é essencial para criar código orientado a objetos robusto, seguro e de fácil manutenção.

2. O modificador public

O modificador public é o mais permissivo. Propriedades e métodos declarados como public podem ser acessados de qualquer lugar: dentro da classe, em classes filhas e fora da classe.

<?php

class Usuario {
    public string $nome;
    public string $email;

    public function __construct(string $nome, string $email) {
        $this->nome = $nome;
        $this->email = $email;
    }

    public function saudacao(): string {
        return "Olá, {$this->nome}!";
    }
}

$usuario = new Usuario("Maria", "maria@email.com");
echo $usuario->nome;        // Acesso direto à propriedade pública
echo $usuario->saudacao();  // Chamada de método público

O uso típico de public é para a interface pública da classe — métodos que outras partes do sistema precisam chamar. No entanto, expor propriedades diretamente como public pode ser arriscado, pois permite que qualquer código externo modifique o estado interno da classe sem validação. Por isso, é comum usar getters e setters públicos:

<?php

class Produto {
    private float $preco;

    public function getPreco(): float {
        return $this->preco;
    }

    public function setPreco(float $preco): void {
        if ($preco <= 0) {
            throw new InvalidArgumentException("Preço deve ser positivo");
        }
        $this->preco = $preco;
    }
}

3. O modificador protected

O modificador protected permite acesso apenas dentro da própria classe e de classes filhas (herança). Ele é ideal para membros que devem ser compartilhados entre uma classe pai e suas subclasses, mas não devem ser expostos ao mundo externo.

<?php

class Animal {
    protected string $nome;
    protected int $idade;

    public function __construct(string $nome, int $idade) {
        $this->nome = $nome;
        $this->idade = $idade;
    }

    protected function emitirSom(): string {
        return "Som genérico";
    }
}

class Cachorro extends Animal {
    public function latir(): string {
        // Acesso a propriedades protected da classe pai
        return "{$this->nome} diz: Au au!";
    }

    // Sobrescrita de método protected
    protected function emitirSom(): string {
        return "Latido";
    }
}

$dog = new Cachorro("Rex", 3);
echo $dog->latir();        // Funciona
// echo $dog->emitirSom(); // Erro! Método protected não é acessível externamente

A diferença crucial entre protected e private aparece na herança: enquanto protected permite que classes filhas acessem membros da classe pai, private bloqueia completamente esse acesso.

4. O modificador private

O modificador private oferece o mais alto nível de encapsulamento. Membros declarados como private só podem ser acessados dentro da própria classe que os define. Nem mesmo classes filhas podem acessá-los diretamente.

<?php

class ContaBancaria {
    private float $saldo;
    private string $senha;

    public function __construct(float $saldoInicial, string $senha) {
        $this->saldo = $saldoInicial;
        $this->senha = $senha;
    }

    public function depositar(float $valor): void {
        if ($valor <= 0) {
            throw new InvalidArgumentException("Valor inválido");
        }
        $this->saldo += $valor;
    }

    public function sacar(float $valor, string $senha): bool {
        if (!$this->validarSenha($senha)) {
            return false;
        }
        if ($valor > $this->saldo) {
            return false;
        }
        $this->saldo -= $valor;
        return true;
    }

    private function validarSenha(string $senha): bool {
        return $this->senha === $senha;
    }
}

class ContaPoupanca extends ContaBancaria {
    public function aplicarRendimento(): void {
        // ERRO! Não é possível acessar $this->saldo diretamente
        // $this->saldo *= 1.01; // Isso causaria erro
    }
}

O uso de private é fundamental para ocultar detalhes internos de implementação, como validações, cálculos auxiliares e dados sensíveis.

5. Comportamento em herança e sobrescrita de métodos

Cada modificador de acesso se comporta de forma diferente quando uma classe é estendida:

  • public: Mantém-se público na classe filha. Pode ser sobrescrito livremente.
  • protected: Permanece protegido na classe filha. Pode ser sobrescrito.
  • private: Não é herdado pela classe filha. Métodos private não podem ser sobrescritos.
<?php

class Pai {
    public function metodoPublico(): string {
        return "Pai público";
    }

    protected function metodoProtegido(): string {
        return "Pai protegido";
    }

    private function metodoPrivado(): string {
        return "Pai privado";
    }

    public function testarPrivado(): string {
        return $this->metodoPrivado(); // Funciona: acesso dentro da própria classe
    }
}

class Filho extends Pai {
    public function metodoPublico(): string {
        return "Filho público (sobrescrito)";
    }

    protected function metodoProtegido(): string {
        return "Filho protegido (sobrescrito)";
    }

    // Não é possível sobrescrever metodoPrivado()
    // private function metodoPrivado() - Isso criaria um novo método, não sobrescreve

    public function testarAcessos(): string {
        return $this->metodoPublico() . " | " . 
               $this->metodoProtegido() . " | " .
               // $this->metodoPrivado(); // ERRO! Não acessível
               "";
    }
}

Uma regra importante: ao sobrescrever um método, você não pode reduzir sua visibilidade. Um método public não pode se tornar protected ou private na classe filha, e um método protected não pode se tornar private.

6. Modificadores de acesso em propriedades tipadas (PHP 7.4+)

Desde o PHP 7.4, é possível declarar tipos para propriedades, combinando com modificadores de acesso para maior segurança e clareza:

<?php

class Pessoa {
    public string $nome;
    protected int $idade;
    private string $cpf;
    private array $telefones = [];

    public function __construct(string $nome, int $idade, string $cpf) {
        $this->nome = $nome;
        $this->idade = $idade;
        $this->cpf = $cpf;
    }

    public function adicionarTelefone(string $telefone): void {
        $this->telefones[] = $telefone;
    }
}

Com o PHP 8.0, os construtores promovidos simplificam a declaração:

<?php

class Cliente {
    public function __construct(
        public string $nome,
        protected string $email,
        private string $documento
    ) {}

    public function getDocumentoMascarado(): string {
        return substr($this->documento, 0, 3) . '***';
    }
}

$cliente = new Cliente("João", "joao@email.com", "12345678901");
echo $cliente->nome;        // Acessível
// echo $cliente->email;    // Erro! protected
// echo $cliente->documento; // Erro! private
echo $cliente->getDocumentoMascarado(); // 123***

7. Boas práticas e padrões comuns

A principal boa prática ao usar modificadores de acesso é o princípio do menor privilégio: comece sempre com o modificador mais restritivo (private) e evolua conforme necessário.

<?php

// Versão inicial: tudo private
class Pedido {
    private array $itens = [];
    private float $total = 0.0;

    public function adicionarItem(string $produto, float $preco): void {
        $this->itens[] = ['produto' => $produto, 'preco' => $preco];
        $this->recalcularTotal();
    }

    private function recalcularTotal(): void {
        $this->total = array_sum(array_column($this->itens, 'preco'));
    }
}

// Se uma subclasse precisar acessar $itens, mude para protected
class PedidoInternacional extends Pedido {
    protected function calcularFrete(): float {
        // Agora pode acessar $this->itens se for protected
        return count($this->itens) * 10.0;
    }
}

Outras práticas recomendadas:
- Use public apenas para a API essencial da classe — métodos que outras classes precisam chamar
- Use protected para métodos de hook ou template method que subclasses podem personalizar
- Use private para detalhes internos — validações, cálculos auxiliares, dados sensíveis
- Sempre prefira getters/setters públicos a propriedades públicas para manter controle sobre o acesso

<?php

// Exemplo de refatoração: de público para privado com getters/setters
class Configuracao {
    private array $dados = [];

    public function __construct(array $dados) {
        $this->dados = $dados;
    }

    public function get(string $chave, mixed $default = null): mixed {
        return $this->dados[$chave] ?? $default;
    }

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

Dominar os modificadores de acesso é fundamental para escrever código PHP orientado a objetos que seja seguro, sustentável e que respeite os princípios de encapsulamento. Comece restringindo ao máximo e só aumente a visibilidade quando houver uma necessidade real.

Referências