Construtores e destrutores
Esta aula aborda os métodos mágicos __construct e __destruct em PHP, explicando como inicializar e finalizar objetos. Também cobre a promoção de propriedades introduzida no PHP 8, que simplifica a declaração de propriedades no construtor.
Construtores e destrutores são métodos especiais em PHP que permitem controlar o ciclo de vida de um objeto. O construtor é executado automaticamente quando um objeto é instanciado, sendo ideal para inicializar propriedades ou executar lógica de configuração. O destrutor é chamado quando um objeto é destruído, útil para liberar recursos como conexões de banco de dados ou arquivos abertos. Nesta aula, exploraremos esses métodos em detalhes, incluindo a promoção de propriedades introduzida no PHP 8, que simplifica a escrita de construtores.
Entender construtores e destrutores é fundamental para escrever código orientado a objetos robusto e eficiente. Eles ajudam a garantir que os objetos comecem em um estado válido e terminem de forma limpa, evitando vazamentos de memória e outros problemas.
__construct
O método __construct é um método mágico que é automaticamente invocado quando um novo objeto é criado usando a palavra-chave new. Ele permite que você passe argumentos para inicializar propriedades do objeto ou executar qualquer lógica necessária antes que o objeto seja usado.
Antes do PHP 8, era comum declarar propriedades separadamente e depois atribuir os parâmetros do construtor a elas. Com a promoção de propriedades, isso se tornou mais conciso. Vejamos um exemplo tradicional:
class Usuario {
public string $nome;
public string $email;
public function __construct(string $nome, string $email) {
$this->nome = $nome;
$this->email = $email;
}
}
$usuario = new Usuario('João', 'joao@exemplo.com');
echo $usuario->nome; // João
No exemplo acima, declaramos as propriedades $nome e $email e depois as inicializamos no construtor. Esse padrão é muito comum e funciona bem, mas requer um pouco de repetição.
Promoção de propriedades (PHP 8)
A partir do PHP 8, você pode combinar a declaração de propriedades com os parâmetros do construtor usando uma sintaxe mais concisa chamada promoção de propriedades. Em vez de declarar a propriedade e depois atribuí-la, você pode definir o modificador de visibilidade diretamente no parâmetro do construtor, e o PHP automaticamente cria e inicializa a propriedade.
O exemplo anterior pode ser reescrito assim:
class Usuario {
public function __construct(
public string $nome,
public string $email
) {}
}
$usuario = new Usuario('Maria', 'maria@exemplo.com');
echo $usuario->email; // maria@exemplo.com
Note que não precisamos mais declarar as propriedades separadamente. O PHP cria automaticamente as propriedades públicas $nome e $email e as inicializa com os valores passados. Você também pode usar outros modificadores como protected ou private, e até mesmo tipos union ou nullable.
A promoção de propriedades reduz a verbosidade e torna o código mais limpo, especialmente quando há muitas propriedades. No entanto, se você precisar de lógica adicional no construtor além da simples atribuição, ainda pode incluir código no corpo do construtor após os parâmetros promovidos.
__destruct
O método __destruct é outro método mágico que é chamado quando um objeto é destruído, seja porque não há mais referências a ele ou porque o script terminou. Ele não aceita parâmetros e não retorna valor. É útil para liberar recursos como arquivos abertos, conexões de rede ou manipuladores de banco de dados.
Exemplo de um destrutor que fecha um arquivo:
class Arquivo {
private $handle;
public function __construct(string $caminho) {
$this->handle = fopen($caminho, 'r');
}
public function ler(): string {
return fread($this->handle, 1024);
}
public function __destruct() {
if ($this->handle) {
fclose($this->handle);
echo "Arquivo fechado.";
}
}
}
$arquivo = new Arquivo('dados.txt');
echo $arquivo->ler();
// Quando o script terminar, o destrutor será chamado automaticamente.
É importante notar que o destrutor não é chamado se uma exceção for lançada durante a execução do construtor, a menos que o objeto já tenha sido completamente construído. Além disso, o destrutor é chamado mesmo se o script for interrompido por exit() ou die().
Inicialização
Além dos construtores e destrutores, o PHP oferece outras formas de inicializar objetos, como métodos estáticos de fábrica ou a inicialização direta de propriedades com valores padrão. Por exemplo, você pode definir valores padrão para propriedades na declaração:
class Config {
public string $host = 'localhost';
public int $porta = 3306;
}
Esses valores são aplicados antes da execução do construtor, e podem ser sobrescritos pelo construtor se necessário. Outra técnica comum é usar o padrão Named Constructor (construtor nomeado) com métodos estáticos:
class Data {
private int $ano;
private int $mes;
private int $dia;
private function __construct(int $ano, int $mes, int $dia) {
$this->ano = $ano;
$this->mes = $mes;
$this->dia = $dia;
}
public static function fromString(string $data): self {
$partes = explode('-', $data);
return new self((int)$partes[0], (int)$partes[1], (int)$partes[2]);
}
public static function now(): self {
return new self(date('Y'), date('m'), date('d'));
}
}
$data = Data::fromString('2024-12-25');
$hoje = Data::now();
Essa abordagem torna a criação de objetos mais expressiva e permite validação antes da instanciação.
Boas práticas
- Use construtores para garantir que o objeto esteja em um estado válido desde o início. Evite criar objetos incompletos.
- Prefira a promoção de propriedades (PHP 8+) para reduzir código repetitivo.
- Implemente destrutores apenas quando necessário para liberar recursos não gerenciados pelo PHP (como arquivos ou conexões).
- Evite lógica complexa em destrutores, pois eles podem ser chamados em momentos imprevisíveis.
- Considere usar construtores privados com métodos estáticos para maior controle sobre a criação de objetos.
Referências
- PHP: Construtores e Destrutores - Manual
- PHP: Promoção de Propriedades no Construtor
- PHP: Métodos Mágicos
- PHP: fclose - Manual
- PHP: Padrões de Projeto - Manual
Exercícios
-
Crie uma classe
Produtoque tenha propriedadesnome,precoequantidade. Use promoção de propriedades no construtor. Instancie um objeto e exiba suas propriedades.✓ Resposta:class Produto { public function __construct( public string $nome, public float $preco, public int $quantidade ) {} } $produto = new Produto('Camiseta', 49.90, 10); echo $produto->nome; // Camiseta echo $produto->preco; // 49.9 echo $produto->quantidade; // 10 -
Implemente um destrutor na classe
Produtoque exiba uma mensagem "Produto {nome} destruído". Crie um objeto e depois force sua destruição comunset().✓ Resposta:class Produto { public function __construct( public string $nome, public float $preco, public int $quantidade ) {} public function __destruct() { echo "Produto {$this->nome} destruído\n"; } } $produto = new Produto('Caneta', 2.50, 100); unset($produto); // Exibe: Produto Caneta destruído -
Crie uma classe
ArquivoLogque abre um arquivo no construtor e escreve uma linha de log. O destrutor deve fechar o arquivo. Use promoção de propriedades para o nome do arquivo.✓ Resposta:class ArquivoLog { private $handle; public function __construct( private string $caminho ) { $this->handle = fopen($this->caminho, 'a'); } public function escrever(string $mensagem): void { fwrite($this->handle, date('Y-m-d H:i:s') . ' - ' . $mensagem . "\n"); } public function __destruct() { if ($this->handle) { fclose($this->handle); } } } $log = new ArquivoLog('app.log'); $log->escrever('Usuário logou'); // Ao final do script, o arquivo é fechado. -
Use um construtor privado e um método estático para criar uma classe
Senhaque represente uma senha criptografada. O método estático deve receber uma string em texto puro e retornar uma instância com o hash da senha.✓ Resposta:class Senha { private string $hash; private function __construct(string $hash) { $this->hash = $hash; } public static function fromPlainText(string $plain): self { return new self(password_hash($plain, PASSWORD_DEFAULT)); } public function verificar(string $plain): bool { return password_verify($plain, $this->hash); } } $senha = Senha::fromPlainText('minha_senha'); var_dump($senha->verificar('minha_senha')); // true -
Crie uma classe
ConfigBancoque receba no construtor um array associativo com chaves 'host', 'porta', 'usuario', 'senha' e 'banco'. Use promoção de propriedades, mas converta os tipos adequadamente (porta para int).✓ Resposta:class ConfigBanco { public function __construct( public string $host, public int $porta, public string $usuario, public string $senha, public string $banco ) {} public static function fromArray(array $config): self { return new self( $config['host'], (int) $config['porta'], $config['usuario'], $config['senha'], $config['banco'] ); } } $config = ConfigBanco::fromArray([ 'host' => 'localhost', 'porta' => '3306', 'usuario' => 'root', 'senha' => 'secret', 'banco' => 'meubanco' ]); echo $config->porta; // 3306 (int)