Herança e Sobrescrita de Métodos em PHP
1. Fundamentos da Herança em PHP
A herança é um dos pilares da programação orientada a objetos (POO) que permite que uma classe filha (child) herde propriedades e métodos de uma classe pai (parent). Em PHP, a herança é implementada com a palavra-chave extends e suporta apenas herança simples — uma classe pode herdar de apenas uma classe pai.
Sintaxe básica:
<?php
class Animal {
public $nome;
public function __construct($nome) {
$this->nome = $nome;
}
public function mover() {
return "{$this->nome} está se movendo.";
}
}
class Cachorro extends Animal {
public function latir() {
return "{$this->nome} está latindo: Au au!";
}
}
$dog = new Cachorro("Rex");
echo $dog->mover(); // Rex está se movendo.
echo $dog->latir(); // Rex está latindo: Au au!
A classe Cachorro herda automaticamente o construtor, a propriedade $nome e o método mover() da classe Animal, demonstrando o reuso de código.
2. Acessando Propriedades e Métodos Herdados
Para acessar explicitamente métodos ou propriedades da classe pai dentro da classe filha, usamos o operador parent::. Isso é especialmente útil quando queremos estender o comportamento herdado.
Modificadores de acesso e herança:
| Modificador | Herdado? | Acessível na filha? | Acessível externamente? |
|---|---|---|---|
public |
Sim | Sim | Sim |
protected |
Sim | Sim | Não |
private |
Não | Não | Não |
Exemplo com construtor herdado:
<?php
class Veiculo {
protected $marca;
protected $modelo;
public function __construct($marca, $modelo) {
$this->marca = $marca;
$this->modelo = $modelo;
}
public function getInfo() {
return "{$this->marca} {$this->modelo}";
}
}
class Carro extends Veiculo {
private $portas;
public function __construct($marca, $modelo, $portas) {
parent::__construct($marca, $modelo);
$this->portas = $portas;
}
public function getInfo() {
return parent::getInfo() . " - {$this->portas} portas";
}
}
$carro = new Carro("Honda", "Civic", 4);
echo $carro->getInfo(); // Honda Civic - 4 portas
3. Sobrescrita de Métodos (Overriding)
A sobrescrita de métodos permite que uma classe filha redefina um método herdado da classe pai. Em PHP, para sobrescrever um método, a classe filha deve declarar um método com o mesmo nome. A assinatura deve ser compatível — o número de parâmetros obrigatórios deve ser o mesmo, embora PHP seja flexível com tipos em versões anteriores ao PHP 8.
Regras importantes:
- O método na classe filha deve ter a mesma visibilidade ou menos restritiva (não pode tornar um método public em private)
- A partir do PHP 8.1, tipos de parâmetros e retorno devem ser compatíveis (covariância/contravariância)
Exemplo prático:
<?php
class Animal {
public function falar() {
return "O animal faz um som.";
}
}
class Gato extends Animal {
public function falar() {
return "Miau!";
}
}
class Vaca extends Animal {
public function falar() {
return "Muuu!";
}
}
$animais = [new Animal(), new Gato(), new Vaca()];
foreach ($animais as $animal) {
echo $animal->falar() . "\n";
}
// O animal faz um som.
// Miau!
// Muuu!
4. A Palavra-chave final e Herança
A palavra-chave final impede que métodos sejam sobrescritos ou que classes sejam estendidas.
Métodos final:
<?php
class Pagamento {
final public function processar() {
return "Processando pagamento...";
}
}
class PagamentoCartao extends Pagamento {
// Erro! Não pode sobrescrever método final
// public function processar() { ... }
}
Classes final:
<?php
final class Configuracao {
public static function getVersao() {
return "1.0.0";
}
}
// Erro! Não pode estender classe final
// class ConfiguracaoAvancada extends Configuracao { ... }
Quando usar final:
- Para garantir que um método crítico não seja alterado (ex: validação de segurança)
- Em classes de configuração ou utilitárias que não devem ser modificadas
- Para design explícito: documentar que a classe não foi projetada para ser estendida
5. Herança e Construtores
A sobrescrita de construtores segue as mesmas regras, mas com uma prática recomendada: sempre chamar o construtor da classe pai quando necessário.
Ordem de execução:
<?php
class Base {
public function __construct() {
echo "Construtor da Base\n";
}
}
class Derivada extends Base {
public function __construct() {
parent::__construct(); // Chama construtor da Base primeiro
echo "Construtor da Derivada\n";
}
}
$obj = new Derivada();
// Construtor da Base
// Construtor da Derivada
Boas práticas:
- Se a classe pai tem um construtor com parâmetros obrigatórios, a filha deve chamá-lo explicitamente
- Use parent::__construct() no início do construtor da filha para garantir inicialização adequada
- Evite lógica complexa em construtores; prefira métodos de inicialização separados
6. Herança vs. Composição
Embora a herança seja poderosa, ela tem limitações. PHP suporta apenas herança simples, e hierarquias profundas podem tornar o código frágil e difícil de manter.
Problemas com herança excessiva:
- Acoplamento forte entre classes
- Dificuldade de testar classes isoladamente
- Herança de comportamentos indesejados
Composição como alternativa:
<?php
interface ComportamentoVoo {
public function voar();
}
class Asa implements ComportamentoVoo {
public function voar() {
return "Voando com asas!";
}
}
class Foguete implements ComportamentoVoo {
public function voar() {
return "Voando com foguete!";
}
}
class Ave {
private $comportamentoVoo;
public function __construct(ComportamentoVoo $comportamento) {
$this->comportamentoVoo = $comportamento;
}
public function voar() {
return $this->comportamentoVoo->voar();
}
}
$ave = new Ave(new Asa());
echo $ave->voar(); // Voando com asas!
A composição oferece maior flexibilidade, permitindo trocar comportamentos em tempo de execução.
7. Exemplo Prático Completo
Vamos construir um sistema de funcionários que demonstra herança, sobrescrita e o uso de final.
<?php
class Funcionario {
protected $nome;
protected $salarioBase;
public function __construct($nome, $salarioBase) {
$this->nome = $nome;
$this->salarioBase = $salarioBase;
}
public function calcularSalario() {
return $this->salarioBase;
}
public function getCargo() {
return "Funcionário";
}
final public function getNome() {
return $this->nome;
}
}
class Gerente extends Funcionario {
private $bonificacao;
public function __construct($nome, $salarioBase, $bonificacao) {
parent::__construct($nome, $salarioBase);
$this->bonificacao = $bonificacao;
}
public function calcularSalario() {
return parent::calcularSalario() + $this->bonificacao;
}
public function getCargo() {
return "Gerente";
}
}
class Diretor extends Gerente {
private $participacaoLucros;
public function __construct($nome, $salarioBase, $bonificacao, $participacaoLucros) {
parent::__construct($nome, $salarioBase, $bonificacao);
$this->participacaoLucros = $participacaoLucros;
}
public function calcularSalario() {
return parent::calcularSalario() + $this->participacaoLucros;
}
public function getCargo() {
return "Diretor";
}
}
// Uso do sistema
$funcionario = new Funcionario("João", 3000);
$gerente = new Gerente("Maria", 5000, 2000);
$diretor = new Diretor("Carlos", 8000, 3000, 5000);
$funcionarios = [$funcionario, $gerente, $diretor];
foreach ($funcionarios as $f) {
echo "{$f->getNome()} - {$f->getCargo()}: R$ {$f->calcularSalario()}\n";
}
// João - Funcionário: R$ 3000
// Maria - Gerente: R$ 7000
// Carlos - Diretor: R$ 13000
Neste exemplo:
- getNome() é final, impedindo que seja sobrescrito
- calcularSalario() é sobrescrito em cada nível, chamando parent:: para reutilizar a lógica da classe pai
- A hierarquia demonstra herança em três níveis com construtores encadeados
Referências
- PHP Manual: Herança de Classes — Documentação oficial sobre herança em PHP, incluindo sintaxe e exemplos
- PHP Manual: Sobrescrita de Métodos — Explicação detalhada sobre sobrescrita de métodos e sobrecarga em PHP
- PHP Manual: Palavra-chave final — Documentação oficial sobre o uso de
finalem métodos e classes - PHP The Right Way: Herança vs Composição — Artigo sobre boas práticas em POO, comparando herança e composição
- PHP.net: Construtores e Destrutores — Documentação oficial sobre construtores, incluindo herança e chamada com
parent::