Construtores e propriedades tipadas
1. Introdução aos Construtores em PHP
O método __construct() é um método especial em PHP que é automaticamente invocado quando um novo objeto de uma classe é criado. Sua principal finalidade é inicializar o estado do objeto, configurando propriedades, estabelecendo conexões ou executando qualquer lógica necessária antes que o objeto seja utilizado.
<?php
class MinhaClasse
{
public function __construct()
{
echo "Objeto criado!";
}
}
$objeto = new MinhaClasse(); // Exibe: "Objeto criado!"
A sintaxe básica de um construtor é simples: ele é definido como um método público com o nome __construct. Construtores podem ser vazios (sem parâmetros) ou receber argumentos para personalizar a inicialização do objeto.
<?php
class Carro
{
public string $modelo;
public int $ano;
// Construtor sem parâmetros
public function __construct()
{
$this->modelo = "Desconhecido";
$this->ano = 2024;
}
}
// Construtor com parâmetros
class CarroPersonalizado
{
public string $modelo;
public int $ano;
public function __construct(string $modelo, int $ano)
{
$this->modelo = $modelo;
$this->ano = $ano;
}
}
2. Propriedades Tipadas: Conceitos Fundamentais
Desde o PHP 7.4, é possível declarar tipos para propriedades de classe, garantindo que apenas valores do tipo especificado sejam atribuídos a elas. Isso traz segurança e previsibilidade ao código.
<?php
class Produto
{
public string $nome;
public float $preco;
public int $estoque;
public bool $disponivel;
public array $categorias;
}
Os tipos suportados incluem:
- Escalares: int, float, string, bool
- Compostos: array, object, callable, iterable
- Especiais: mixed, never, void (apenas em métodos)
- Classes e interfaces: qualquer nome de classe ou interface
Propriedades tipadas exigem inicialização antes do uso, seja com um valor padrão ou no construtor. Caso contrário, o PHP emitirá um erro.
<?php
class Configuracao
{
public string $nome = "Padrão"; // Valor padrão
public int $versao; // Deve ser inicializada no construtor
public function __construct(int $versao)
{
$this->versao = $versao;
}
}
3. Construtores com Parâmetros Tipados
A tipagem explícita nos parâmetros do construtor oferece validação automática no momento da instanciação, evitando que valores incorretos sejam passados.
<?php
class Usuario
{
public string $nome;
public string $email;
public int $idade;
public bool $ativo;
public function __construct(
string $nome,
string $email,
int $idade,
bool $ativo = true
) {
$this->nome = $nome;
$this->email = $email;
$this->idade = $idade;
$this->ativo = $ativo;
}
}
// Uso correto
$usuario = new Usuario("Maria", "maria@email.com", 28, true);
// Isso gerará um TypeError: Argumento 3 deve ser do tipo int, string fornecido
// $usuarioInvalido = new Usuario("João", "joao@email.com", "trinta", false);
4. Promoção de Propriedades no Construtor (PHP 8+)
O PHP 8 introduziu a promoção de propriedades no construtor, uma sintaxe reduzida que permite declarar e inicializar propriedades diretamente nos parâmetros do construtor, eliminando a duplicação de código.
Antes da promoção (PHP 7.4 e anteriores):
<?php
class Cliente
{
private string $nome;
private string $email;
private int $pontos;
public function __construct(string $nome, string $email, int $pontos)
{
$this->nome = $nome;
$this->email = $email;
$this->pontos = $pontos;
}
}
Depois da promoção (PHP 8+):
<?php
class Cliente
{
public function __construct(
private string $nome,
private string $email,
private int $pontos
) {}
}
A promoção funciona com qualquer modificador de acesso (public, protected, private) e aceita todos os tipos suportados pelo PHP.
5. Propriedades Tipadas com Valores Opcionais e Nullable
O operador ? permite declarar propriedades que podem aceitar null além do tipo especificado. Combinado com valores padrão, oferece flexibilidade na inicialização.
<?php
class Endereco
{
public function __construct(
public string $rua,
public string $cidade,
public ?string $complemento = null, // Pode ser string ou null
public ?int $numero = null // Pode ser int ou null
) {}
}
$endereco1 = new Endereco("Rua A", "São Paulo");
$endereco2 = new Endereco("Rua B", "Rio", "Apto 42", 100);
$endereco3 = new Endereco("Rua C", "Belo Horizonte", null, null);
Em construtores promovidos, o tratamento de null é igualmente simples:
<?php
class Funcionario
{
public function __construct(
private string $nome,
private ?string $telefone = null,
private ?float $salario = null
) {}
public function getTelefone(): ?string
{
return $this->telefone;
}
}
6. Construtores e Herança
Quando trabalhamos com herança, é comum que a classe filha precise chamar o construtor da classe pai usando parent::__construct().
<?php
class Animal
{
public function __construct(
protected string $nome,
protected int $idade
) {}
}
class Cachorro extends Animal
{
public string $raca;
public function __construct(string $nome, int $idade, string $raca)
{
parent::__construct($nome, $idade); // Chama construtor da classe pai
$this->raca = $raca;
}
}
$rex = new Cachorro("Rex", 3, "Labrador");
A sobrescrita de construtores deve manter consistência de tipos com a classe base, especialmente em relação aos parâmetros obrigatórios.
7. Boas Práticas e Armadilhas Comuns
Evite lógica pesada no construtor: O construtor deve ser leve e focado apenas na inicialização. Operações complexas, como consultas a banco de dados ou chamadas de API, devem ser movidas para métodos específicos.
<?php
// Evite isso
class Repositorio
{
public function __construct()
{
$this->conexao = new PDO("mysql:host=localhost;dbname=teste", "user", "pass");
$this->conexao->query("SELECT ..."); // Lógica pesada aqui
}
}
// Prefira isso
class Repositorio
{
public function __construct(
private PDO $conexao
) {}
public function buscarDados(): array
{
return $this->conexao->query("SELECT ...")->fetchAll();
}
}
Uso de readonly com propriedades tipadas (PHP 8.1+): O modificador readonly torna a propriedade imutável após a inicialização, ideal para objetos de valor.
<?php
class ConfiguracaoBanco
{
public function __construct(
readonly public string $host,
readonly public int $porta,
readonly public string $usuario,
readonly private string $senha
) {}
}
$config = new ConfiguracaoBanco("localhost", 3306, "root", "123");
// $config->host = "outro"; // Erro! Propriedade readonly
Erros frequentes:
- Tentar acessar propriedades não inicializadas
- Passar tipos incompatíveis nos parâmetros
- Esquecer de chamar parent::__construct() na herança
8. Exemplo Completo: Sistema de Pedidos
Vamos construir um sistema simples de pedidos utilizando todos os conceitos abordados.
<?php
class ItemPedido
{
public function __construct(
readonly public string $produto,
readonly public float $precoUnitario,
readonly public int $quantidade
) {
if ($precoUnitario <= 0) {
throw new InvalidArgumentException("Preço deve ser positivo");
}
if ($quantidade <= 0) {
throw new InvalidArgumentException("Quantidade deve ser positiva");
}
}
public function calcularTotal(): float
{
return $this->precoUnitario * $this->quantidade;
}
}
class Pedido
{
/** @var ItemPedido[] */
private array $itens = [];
public function __construct(
readonly public int $codigo,
readonly public string $cliente,
readonly public ?string $observacao = null
) {}
public function adicionarItem(ItemPedido $item): void
{
$this->itens[] = $item;
}
public function calcularTotal(): float
{
$total = 0;
foreach ($this->itens as $item) {
$total += $item->calcularTotal();
}
return $total;
}
/** @return ItemPedido[] */
public function getItens(): array
{
return $this->itens;
}
}
class PedidoPremium extends Pedido
{
public function __construct(
int $codigo,
string $cliente,
readonly public float $descontoPercentual,
?string $observacao = null
) {
parent::__construct($codigo, $cliente, $observacao);
if ($descontoPercentual < 0 || $descontoPercentual > 100) {
throw new InvalidArgumentException("Desconto deve estar entre 0 e 100");
}
}
public function calcularTotal(): float
{
$total = parent::calcularTotal();
return $total * (1 - $this->descontoPercentual / 100);
}
}
// Demonstração de uso
try {
$pedido = new PedidoPremium(101, "Ana Silva", 10, "Entrega rápida");
$pedido->adicionarItem(new ItemPedido("Notebook", 3500.00, 1));
$pedido->adicionarItem(new ItemPedido("Mouse", 150.00, 2));
$pedido->adicionarItem(new ItemPedido("Teclado", 200.00, 1));
echo "Pedido #{$pedido->codigo}\n";
echo "Cliente: {$pedido->cliente}\n";
echo "Observação: " . ($pedido->observacao ?? "Nenhuma") . "\n";
echo "Total com desconto: R$ " . number_format($pedido->calcularTotal(), 2, ',', '.') . "\n";
} catch (InvalidArgumentException $e) {
echo "Erro: " . $e->getMessage();
}
Este exemplo demonstra:
- Promoção de propriedades no construtor
- Propriedades tipadas (incluindo readonly)
- Tipos nullable (?string)
- Validação de tipos e valores no construtor
- Herança com chamada a parent::__construct()
- Sobrescrita de métodos em classes filhas
Referências
- PHP Manual: Construtores e Destrutores — Documentação oficial sobre o método
__construct()e boas práticas - PHP Manual: Propriedades Tipadas — Referência completa sobre declaração de tipos em propriedades
- PHP 8.0: Promoção de Propriedades no Construtor — Novidades do PHP 8.0 com exemplos oficiais
- PHP 8.1: Propriedades Readonly — Documentação sobre o modificador
readonlyintroduzido no PHP 8.1 - PHP The Right Way: Classes e Objetos — Guia prático com exemplos de construtores, propriedades tipadas e boas práticas em PHP moderno