Classes abstratas
1. Conceitos Fundamentais de Classes Abstratas
Uma classe abstrata em PHP é uma classe que não pode ser instanciada diretamente. Seu propósito principal é servir como modelo base para outras classes, definindo uma estrutura comum que deve ser seguida pelas classes filhas. Diferente de classes concretas — que podem ser instanciadas — e de interfaces — que apenas definem contratos de métodos —, as classes abstratas podem conter tanto métodos abstratos (apenas assinatura) quanto métodos concretos (com implementação completa).
A palavra-chave abstract é utilizada para declarar tanto classes quanto métodos abstratos. Uma regra fundamental é: se uma classe contém ao menos um método abstrato, ela deve ser declarada como abstrata. Além disso, métodos abstratos não podem ser private, pois precisam ser implementados por classes filhas.
2. Sintaxe e Declaração de Classes Abstratas
A declaração de uma classe abstrata segue a sintaxe:
<?php
abstract class FormaGeometrica {
// Método abstrato: apenas assinatura
abstract public function calcularArea(): float;
// Método concreto: implementação completa
public function descricao(): string {
return "Esta é uma forma geométrica.";
}
}
Note que métodos abstratos terminam com ponto e vírgula (;) e não possuem corpo ({}). Já métodos concretos podem conter lógica completa, inclusive acessar propriedades da classe.
3. Herança e Implementação de Classes Abstratas
Para utilizar uma classe abstrata, é necessário estendê-la com extends e implementar todos os seus métodos abstratos. O não cumprimento gera um Fatal error.
<?php
class Circulo extends FormaGeometrica {
private float $raio;
public function __construct(float $raio) {
$this->raio = $raio;
}
// Implementação obrigatória do método abstrato
public function calcularArea(): float {
return pi() * ($this->raio ** 2);
}
}
$circulo = new Circulo(5.0);
echo $circulo->calcularArea(); // 78.5398...
echo $circulo->descricao(); // "Esta é uma forma geométrica."
Erro comum: esquecer de implementar um método abstrato resulta em:
Fatal error: Class Circulo contains 1 abstract method and must therefore be declared abstract or implement the remaining methods
4. Propriedades e Constantes em Classes Abstratas
Desde o PHP 8.0, é possível declarar propriedades abstratas, forçando classes filhas a defini-las:
<?php
abstract class Animal {
// Propriedade abstrata (PHP 8.0+)
public abstract string $nome;
// Constante de classe
public const REINO = "Animalia";
// Método concreto
public function apresentar(): string {
return "Eu sou um {$this->nome}.";
}
}
class Cachorro extends Animal {
public string $nome = "Rex";
}
$dog = new Cachorro();
echo $dog->apresentar(); // "Eu sou um Rex."
Modificadores de acesso funcionam normalmente: public, protected e private. Propriedades abstratas não podem ser private, pois precisam ser visíveis para a classe filha.
5. Classes Abstratas vs Interfaces
A escolha entre classe abstrata e interface depende do cenário:
- Classe abstrata: quando há comportamento compartilhado (métodos concretos) e estado (propriedades). Exemplo: classe
Animalcom método concretorespirar()e método abstratoemitirSom(). - Interface: quando apenas um contrato de métodos é necessário, sem implementação. Exemplo: interface
Movablecom métodomover().
<?php
abstract class Animal {
abstract public function emitirSom(): string;
public function respirar(): string {
return "Respirando...";
}
}
interface Movable {
public function mover(): string;
}
class Passaro extends Animal implements Movable {
public function emitirSom(): string {
return "Piu piu!";
}
public function mover(): string {
return "Voando...";
}
}
Use classe abstrata quando houver código compartilhado; use interface para contratos puros.
6. Padrões de Projeto com Classes Abstratas
Template Method Pattern
Define o esqueleto de um algoritmo, deixando etapas específicas para subclasses:
<?php
abstract class Relatorio {
// Template method
public function gerar(): string {
$cabecalho = $this->criarCabecalho();
$corpo = $this->criarCorpo();
$rodape = $this->criarRodape();
return $cabecalho . "\n" . $corpo . "\n" . $rodape;
}
abstract protected function criarCabecalho(): string;
abstract protected function criarCorpo(): string;
abstract protected function criarRodape(): string;
}
class RelatorioPDF extends Relatorio {
protected function criarCabecalho(): string {
return "=== CABEÇALHO PDF ===";
}
protected function criarCorpo(): string {
return "Conteúdo do relatório PDF";
}
protected function criarRodape(): string {
return "=== RODAPÉ PDF ===";
}
}
Factory Method Pattern
Cria objetos sem especificar a classe exata:
<?php
abstract class DatabaseConnection {
abstract public function connect(): string;
abstract public function query(string $sql): array;
public static function create(string $driver): self {
return match ($driver) {
'mysql' => new MySQLConnection(),
'pgsql' => new PostgreSQLConnection(),
default => throw new InvalidArgumentException("Driver inválido"),
};
}
}
class MySQLConnection extends DatabaseConnection {
public function connect(): string {
return "Conectado ao MySQL";
}
public function query(string $sql): array {
return ["resultado_mysql"];
}
}
7. Boas Práticas e Cuidados
- Evite hierarquias profundas: mais de 3 níveis de herança tornam o código difícil de manter. Prefira composição quando possível.
- Composição sobre herança: se uma classe precisa apenas de comportamento, mas não de identidade, considere usar traits ou injetar dependências.
- Uso de
final: métodosfinalem classes abstratas impedem que subclasses os sobrescrevam, útil para garantir comportamento crítico:
<?php
abstract class BaseController {
final public function executar(): void {
$this->validar();
$this->processar();
}
abstract protected function validar(): void;
abstract protected function processar(): void;
}
8. Exemplo Completo e Cenários Avançados
Sistema de pagamentos com classe abstrata:
<?php
abstract class PaymentGateway {
protected string $apiKey;
protected array $config;
public function __construct(string $apiKey, array $config = []) {
$this->apiKey = $apiKey;
$this->config = $config;
$this->inicializar();
}
abstract protected function inicializar(): void;
abstract public function cobrar(float $valor, array $dados): string;
abstract public function reembolsar(string $transacaoId): bool;
// Método concreto compartilhado
public function formatarMoeda(float $valor): string {
return number_format($valor, 2, ',', '.');
}
}
class PayPalGateway extends PaymentGateway {
protected function inicializar(): void {
// Lógica específica do PayPal
}
public function cobrar(float $valor, array $dados): string {
if ($valor <= 0) {
throw new InvalidArgumentException("Valor inválido");
}
// Simulação de cobrança
return "PAYPAL-" . uniqid();
}
public function reembolsar(string $transacaoId): bool {
return str_starts_with($transacaoId, "PAYPAL-");
}
}
class StripeGateway extends PaymentGateway {
protected function inicializar(): void {
// Configuração específica do Stripe
}
public function cobrar(float $valor, array $dados): string {
if (!isset($dados['token'])) {
throw new InvalidArgumentException("Token de pagamento ausente");
}
return "STRIPE-" . bin2hex(random_bytes(8));
}
public function reembolsar(string $transacaoId): bool {
return str_starts_with($transacaoId, "STRIPE-");
}
}
// Uso
try {
$paypal = new PayPalGateway("api_key_123");
$transacao = $paypal->cobrar(150.00, ['email' => 'cliente@email.com']);
echo "Transação: $transacao\n";
$stripe = new StripeGateway("sk_test_456");
$transacao2 = $stripe->cobrar(200.00, ['token' => 'tok_visa']);
echo "Transação: $transacao2\n";
} catch (Exception $e) {
echo "Erro: " . $e->getMessage();
}
Este exemplo demonstra tratamento de exceções, validação em métodos abstratos e uso de métodos concretos compartilhados.
Referências
- PHP Manual: Classes abstratas — Documentação oficial sobre declaração e uso de classes e métodos abstratos.
- PHP 8.0: Propriedades abstratas — RFC que introduziu propriedades abstratas no PHP 8.0.
- PHP The Right Way: OOP — Guia prático de orientação a objetos em PHP, incluindo classes abstratas.
- Refactoring Guru: Template Method Pattern — Exemplo do padrão Template Method implementado com classes abstratas em PHP.
- Stack Overflow: Abstract class vs Interface — Discussão técnica sobre quando usar classe abstrata versus interface em PHP.