Orientação a Objetos é o paradigma que organiza o código em torno de entidades — objetos — que combinam dados e comportamento em uma única unidade. É o modelo dominante no desenvolvimento PHP profissional: frameworks como Laravel, Symfony e Doctrine são construídos inteiramente sobre OOP.
Neste artigo cobrimos os fundamentos: classes, objetos, propriedades, métodos, construtores, visibilidade e os conceitos de encapsulamento que tornam o código orientado a objetos mais seguro e expressivo do que o código procedural.
O que é uma classe
Uma classe é um molde — ela descreve como um objeto deve ser. Um objeto é uma instância desse molde — um exemplar concreto criado a partir dele:
<?php
declare(strict_types=1);
// Classe — o molde
class Produto
{
// Propriedades — os dados que cada objeto carrega
public string $nome;
public float $preco;
public int $estoque;
}
// Objeto — uma instância criada a partir do molde
$teclado = new Produto();
$teclado->nome = "Teclado Mecânico";
$teclado->preco = 350.0;
$teclado->estoque = 15;
$mouse = new Produto(); // outro objeto, independente do $teclado
$mouse->nome = "Mouse Gamer";
$mouse->preco = 180.0;
$mouse->estoque = 42;
echo $teclado->nome; // Teclado Mecânico
echo $mouse->preco; // 180.0
// Cada objeto tem seus próprios dados — são independentes
$teclado->preco = 320.0; // muda apenas o $teclado
echo $mouse->preco; // 180.0 — não foi afetado
Construtor — inicializando objetos
O construtor é um método especial chamado automaticamente quando um objeto é criado com new. Ele garante que o objeto começa em um estado válido:
<?php
declare(strict_types=1);
class Produto
{
public string $nome;
public float $preco;
public int $estoque;
// __construct é chamado automaticamente pelo new
public function __construct(string $nome, float $preco, int $estoque = 0)
{
// Validação no construtor — objeto nasce válido ou não nasce
if ($preco < 0) {
throw new InvalidArgumentException("Preço não pode ser negativo.");
}
$this->nome = $nome;
$this->preco = $preco;
$this->estoque = $estoque;
}
}
// Agora o new exige os dados obrigatórios
$teclado = new Produto("Teclado Mecânico", 350.0, 15);
echo $teclado->nome; // Teclado Mecânico
// Tentativa de criar produto inválido — lança exceção
// $invalido = new Produto("Teste", -10.0); // InvalidArgumentException
Promoção de propriedades no construtor (PHP 8)
O PHP 8 introduziu uma sintaxe muito mais compacta que declara e atribui propriedades diretamente nos parâmetros do construtor:
<?php
declare(strict_types=1);
class Produto
{
// PHP 8: public/protected/private no parâmetro declara e atribui
// automaticamente a propriedade — elimina muito código repetitivo
public function __construct(
public readonly string $nome, // readonly — não pode ser modificado após o construtor
public float $preco,
public int $estoque = 0,
) {
if ($this->preco < 0) {
throw new InvalidArgumentException("Preço não pode ser negativo.");
}
}
}
$p = new Produto("Monitor 4K", 2500.0, 5);
echo $p->nome; // Monitor 4K
echo $p->preco; // 2500.0
// $p->nome = "Outro"; // Error: Cannot modify readonly property
Métodos — o comportamento do objeto
Métodos são funções definidas dentro de uma classe. Eles operam sobre os dados do próprio objeto através de $this:
<?php
declare(strict_types=1);
class Produto
{
public function __construct(
public readonly string $nome,
public float $preco,
public int $estoque = 0,
) {}
// Método de consulta — retorna informação
public function estaDisponivel(): bool
{
return $this->estoque > 0;
}
// Método de ação — modifica o estado do objeto
public function adicionarEstoque(int $quantidade): void
{
if ($quantidade <= 0) {
throw new InvalidArgumentException("Quantidade deve ser positiva.");
}
$this->estoque += $quantidade;
}
public function vender(int $quantidade): void
{
if ($quantidade > $this->estoque) {
throw new RuntimeException("Estoque insuficiente.");
}
$this->estoque -= $quantidade;
}
// Método de formatação — representação legível
public function resumo(): string
{
$status = $this->estaDisponivel() ? "disponível" : "esgotado";
return "{$this->nome} — R$ {$this->preco} ({$status})";
}
}
$monitor = new Produto("Monitor 4K", 2500.0, 3);
echo $monitor->resumo(); // Monitor 4K — R$ 2500.0 (disponível)
$monitor->vender(3);
echo $monitor->estaDisponivel(); // false (bool)
echo $monitor->resumo(); // Monitor 4K — R$ 2500.0 (esgotado)
Visibilidade: public, protected, private
A visibilidade controla quem pode acessar propriedades e métodos. É o mecanismo central do encapsulamento:
<?php
declare(strict_types=1);
class ContaBancaria
{
// private — acessível apenas dentro desta classe
private float $saldo;
private array $historico = [];
// public — acessível de qualquer lugar
public string $titular;
public function __construct(string $titular, float $depositoInicial = 0.0)
{
$this->titular = $titular;
$this->saldo = 0.0;
if ($depositoInicial > 0) {
$this->depositar($depositoInicial);
}
}
public function depositar(float $valor): void
{
$this->validarValor($valor);
$this->saldo += $valor;
$this->registrar("Depósito", $valor);
}
public function sacar(float $valor): void
{
$this->validarValor($valor);
if ($valor > $this->saldo) {
throw new RuntimeException("Saldo insuficiente.");
}
$this->saldo -= $valor;
$this->registrar("Saque", $valor);
}
// Método getter — acesso controlado ao saldo
public function getSaldo(): float
{
return $this->saldo;
}
public function getHistorico(): array
{
return $this->historico;
}
// private — usado internamente, não exposto ao mundo externo
private function validarValor(float $valor): void
{
if ($valor <= 0) {
throw new InvalidArgumentException("Valor deve ser positivo.");
}
}
private function registrar(string $tipo, float $valor): void
{
$this->historico[] = [
"tipo" => $tipo,
"valor" => $valor,
"data" => date("d/m/Y H:i"),
];
}
}
$conta = new ContaBancaria("Ana Silva", 1000.0);
$conta->depositar(500.0);
$conta->sacar(200.0);
echo $conta->getSaldo(); // 1300.0
// $conta->saldo = 99999; // Error: Cannot access private property
// $conta->validarValor(-10); // Error: Cannot call private method
Propriedades e métodos estáticos
Membros estáticos pertencem à classe, não a uma instância específica. Eles existem independentemente de qualquer objeto criado:
<?php
declare(strict_types=1);
class Configuracao
{
// static — existe na classe, não no objeto
private static array $valores = [];
private static int $chamadas = 0;
// Método estático — chamado na classe, não no objeto
public static function definir(string $chave, mixed $valor): void
{
self::$valores[$chave] = $valor;
}
public static function obter(string $chave, mixed $padrao = null): mixed
{
self::$chamadas++;
return self::$valores[$chave] ?? $padrao;
}
public static function totalChamadas(): int
{
return self::$chamadas;
}
}
// Chamada com :: (double colon / paamayim nekudotayim)
// Não precisa criar objeto
Configuracao::definir("app.nome", "Minha Aplicação");
Configuracao::definir("app.versao", "1.0.0");
echo Configuracao::obter("app.nome"); // Minha Aplicação
echo Configuracao::obter("app.debug", false); // false — padrão
echo Configuracao::totalChamadas(); // 2
// Constante de classe — imutável, compartilhada entre todas as instâncias
class Moeda
{
const BRL = "BRL";
const USD = "USD";
const EUR = "EUR";
// PHP 8.3: constante tipada
// const string SIMBOLO_BRL = "R$";
}
echo Moeda::BRL; // BRL
Encapsulamento — o princípio por trás da visibilidade
O encapsulamento não é apenas esconder dados — é garantir que o objeto sempre esteja em um estado válido e que mudanças de estado passem por regras de negócio:
<?php
declare(strict_types=1);
// ✗ Sem encapsulamento — o objeto pode ser corrompido
class PedidoAberto
{
public string $status = "pendente";
public float $total = 0.0;
public array $itens = [];
}
$pedido = new PedidoAberto();
$pedido->status = "cancelado"; // permitido mesmo sem itens
$pedido->total = -500.0; // total negativo — estado inválido!
// ✓ Com encapsulamento — o objeto protege seu próprio estado
class Pedido
{
private string $status = "pendente";
private float $total = 0.0;
private array $itens = [];
// Transição de estado passa por validação
public function cancelar(): void
{
if ($this->status === "entregue") {
throw new RuntimeException("Pedido entregue não pode ser cancelado.");
}
$this->status = "cancelado";
}
public function adicionarItem(string $nome, float $preco, int $qtd): void
{
if ($this->status !== "pendente") {
throw new RuntimeException("Só é possível adicionar itens a pedidos pendentes.");
}
$this->itens[] = ["nome" => $nome, "preco" => $preco, "qtd" => $qtd];
$this->total += $preco * $qtd;
}
public function getStatus(): string { return $this->status; }
public function getTotal(): float { return $this->total; }
public function getItens(): array { return $this->itens; }
}
O método mágico __toString
O PHP tem vários métodos mágicos — métodos com nomes especiais que são chamados em situações específicas. O __toString é invocado quando o objeto é usado em contexto de string:
<?php
declare(strict_types=1);
class Produto
{
public function __construct(
public readonly string $nome,
public float $preco,
) {}
// Chamado quando o objeto é tratado como string
public function __toString(): string
{
return "{$this->nome} (R$ " . number_format($this->preco, 2, ',', '.') . ")";
}
}
$p = new Produto("Teclado", 350.0);
echo $p; // Teclado (R$ 350,00) — __toString chamado automaticamente
echo "Produto: $p"; // Produto: Teclado (R$ 350,00) — interpolação também chama
$s = (string) $p; // cast explícito também chama __toString
Boas práticas em OOP
Nomeie classes com substantivos, métodos com verbos. Pedido, Usuario, Produto — não GerenciadorDePedido. Métodos: calcularTotal(), validarEmail(), cancelar().
Prefira readonly em propriedades imutáveis. Se uma propriedade não deve mudar após a criação, declare-a readonly. O PHP 8.1 introduziu readonly para propriedades individuais; o PHP 8.2 para classes inteiras.
Nunca deixe objetos em estado inválido. O construtor deve validar os dados e lançar exceção se os dados não permitem criar um objeto válido. É melhor falhar na criação do que ter um objeto corrompido rodando pelo sistema.
Limite o número de dependências públicas. Quanto menos o código externo precisar conhecer sobre o interior de uma classe, mais fácil é mudar essa classe no futuro.
Resumo
| Conceito | O que aprendemos |
|---|---|
class |
Define o molde — estrutura e comportamento |
new |
Cria uma instância (objeto) da classe |
$this |
Referência ao objeto atual dentro dos métodos |
__construct |
Chamado automaticamente pelo new |
| Promoção de propriedades | Declara e atribui no construtor — PHP 8 |
readonly |
Propriedade que não pode ser modificada após o construtor |
public |
Acessível de qualquer lugar |
private |
Acessível apenas dentro da própria classe |
protected |
Acessível na classe e em subclasses (veremos em herança) |
static |
Pertence à classe, não à instância — acesso com :: |
const |
Constante de classe — imutável, compartilhada |
__toString |
Método mágico para representação em string |
| Encapsulamento | Proteger estado interno — objeto sempre válido |
Referências e leituras para aprofundar
-
Classes e Objetos — Manual oficial do PHP https://www.php.net/manual/pt_BR/language.oop5.php Documentação completa do sistema OOP do PHP — todas as funcionalidades com exemplos.
-
Promoção de propriedades no construtor — php.net https://www.php.net/manual/pt_BR/language.oop5.decon.php#language.oop5.decon.constructor.promotion Documentação específica da promoção de propriedades introduzida no PHP 8.0.
-
Propriedades readonly — RFC php.net https://wiki.php.net/rfc/readonly_properties_v2 A proposta que introduziu
readonlyno PHP 8.1, com motivação, design e exemplos. -
MARTIN, Robert C. Clean Code. Capítulo 10: Classes. https://www.oreilly.com/library/view/clean-code-a/9780136083238/ O capítulo sobre classes do Clean Code — tamanho ideal, responsabilidade única e coesão aplicados a OOP.
-
PHP: The Right Way — Object-Oriented Programming https://phptherightway.com/#object-oriented_programming Seção sobre boas práticas OOP na comunidade PHP moderna.
-
GAMMA, E. et al. Design Patterns. Addison-Wesley, 1994. https://www.oreilly.com/library/view/design-patterns-elements/0201633612/ O livro que formalizou os padrões de design OOP. Referência para os artigos futuros do Módulo 3.