Construtores
O PHP permite aos desenvolvedores declararem métodos construtores para as classes.
Classes que tem um método construtor chamam o método a cada
objeto recém criado, sendo apropriado para qualquer inicialização que o
objeto necessite antes de ser utilizado.
Nota:
Construtores pais não são chamados implicitamente se a classe filha define
um construtor. Para executar o construtor da classe pai, uma chamada a
parent::__construct() dentro do construtor da classe filha
é necessária. Se a classe filha não definir um construtor, será herdado
da classe pai como um método normal (se não foi declarado
como privado).
Exemplo #1 Construtoras em herança
<?php
class BaseClass {
function __construct() {
print "In BaseClass constructor\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "In SubClass constructor\n";
}
}
class OtherSubClass extends BaseClass {
// inherits BaseClass's constructor
}
// In BaseClass constructor
$obj = new BaseClass();
// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();
// In BaseClass constructor
$obj = new OtherSubClass();
?>
Diferente de outros métodos, __construct()
não precisa seguir as regras usuais de
compatibilidade de assinatura
em objetos derivados.
Construtores são métodos ordinários que são chamados durante a criação
do objeto correspondente. Eles podem definir um número arbitrários de argumentos, quais
podem ser obrigatórios, podem ter um tipo, e podem ter valores padrão. Argumentos de construtores
são informados dentro de parênteses depois do nome da classe.
Exemplo #2 Utilizando argumentos de construtor
<?php
class Point {
protected int $x;
protected int $y;
public function __construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}
// Passagem de ambos os argumentos.
$p1 = new Point(4, 5);
// Passar somente o argumento obrigatório, $y terá o valor padrão zero.
$p2 = new Point(4);
// Com parâmetros nomeados (a partir do PHP 8.0):
$p3 = new Point(y: 5, x: 4);
?>
Se a classe não tem construtor, ou o construtor não tem argumentos obrigatórios, o parêntesis
pode ser omitido.
Construtores em estilo antigo
Anteriormente ao PHP 8.0.0 as classes no namespace global interpretam um método com o mesmo nome
da classe como sendo um construtor válido. Essa sintaxe foi descontinuada,
e gerará um erro E_DEPRECATED
embora ainda continue funcionando como um construtor.
Se ambos o __construct() e um método homônimo da classe
estiverem definidos, __construct() que será chamado.
Em classes dentro de namespaces, ou quaisquer classes a partir do PHP 8, um método
homônimo ao nome da classe não tem um significado especial.
Sempre utilize __construct() em novos códigos.
New em inicializadores de parâmetros
A partir do PHP 8.1.0, objetos podem ser utilizados como valores padrão de parâmetros,
variáveis estáticas e constantes globais, assim como argumentos de atributos.
Novos objetos também podem ser passados na instrução define().
Nota:
Não é permitido nomes de classe não-string ou classes anônimas.
Não é permitido o espalhamento de argumentos.
Não é permitido o uso de expressões.
Exemplo #4 New em inicializações
<?php
// Permitido:
static $x = new Foo;
const C = new Foo;
function test($param = new Foo) {}
#[AnAttribute(new Foo)]
class Test {
public function __construct(
public $prop = new Foo,
) {}
}
// Não permitido, resultad em erro de compilação:
function test(
$a = new (CLASS_NAME_CONSTANT)(), // Nome dinâmico de classe
$b = new class {}, // Classe anônima
$c = new A(...[]), // Espalhamento de argumento
$d = new B($abc), // Expressão
) {}
?>
Métodos de criação estáticos
O PHP suporta apenas um único construtor por classe. Em alguns casos pode ser
desejável de permitir a um objeto ser construído de maneiras diferentes, a partir de argumentos diferentes.
O método recomendado para realizar isso é através de métodos estáticos, utilizados como empacotadores do construtor.
Exemplo #5 Utilizando métodos estáticos para construção
<?php
class Product {
private ?int $id;
private ?string $name;
private function __construct(?int $id = null, ?string $name = null) {
$this->id = $id;
$this->name = $name;
}
public static function fromBasicData(int $id, string $name): static {
$new = new static($id, $name);
return $new;
}
public static function fromJson(string $json): static {
$data = json_decode($json, true);
return new static($data['id'], $data['name']);
}
public static function fromXml(string $xml): static {
// Custom logic here.
$data = convert_xml_to_array($xml);
$new = new static();
$new->id = $data['id'];
$new->name = $data['name'];
return $new;
}
}
$p1 = Product::fromBasicData(5, 'Widget');
$p2 = Product::fromJson($some_json_string);
$p3 = Product::fromXml($some_xml_string);
O construtor pode ser private ou protected para evitar que ele seja chamado externamente.
Nesses casos apenas um construtor estático será capaz de instanciar a classe. Por eles estarem
nas mesma definição de classe, os métodos estáticos são capazes de instanciar o objeto, mesmo em
uma instância diferente. O construtor privado é opcional e pode não fazer sentido em
todos os casos.
Os três métodos estáticos a seguir demonstram as maneiras diferentes de instanciar um objeto.
fromBasicData()
obtém os exatos parâmetros que são necessários, e então cria
o objeto através da chamada do construtor e retorna o resultado
fromJson()
aceita uma string JSON, realiza algum pré-processamento para
converter os dados no formato necessário ao construtor. Só então retorna o novo objeto.
fromXml()
aceita uma string XML, pré-processa, e então cria um objeto
limpo. O construtor é chamado, mas com todos os parâmetros opcionais o método
os ignora. Por fim, os valores nas propriedades do objeto são associados antes de retornar o resultado.
Nos três casos, a palavra chave static
é convertida no nome da classe onde o código reside.
Nesse caso a classe Product
.
Destrutor
O PHP introduz um conceito de destrutor similar ao de outras
linguagens orientadas a objeto, como C++. O método destrutor será chamado
assim que todas as referências a um objeto particular forem removidas ou quando
o objeto for explicitamente destruído ou qualquer ordem na sequência de encerramento.
Exemplo #6 Exemplo de Destrutor
<?php
class MyDestructableClass
{
function __construct() {
print "In constructor\n";
}
function __destruct() {
print "Destroying " . __CLASS__ . "\n";
}
}
$obj = new MyDestructableClass();
Assim como os construtores, os destrutores da classe pai não serão chamados implicitamente pelo
PHP. Para executar o destrutor pai, deve-se fazer uma chamada
explícita a parent::__destruct() no corpo do
destrutor. Assim como construtores, uma classe filha pode herdar o destrutor
caso não implemente um.
O destrutor será chamado mesmo se o script for terminado utilizando-se
exit(). Chamar exit() em um destrutor
irá impedir que as demais rotinas de encerramento executem.
Se um destrutor criar novas referências ao seu objeto, ele não será chamado
uma segunda vez quando a contagem de referências atingir zero novamente ou durante a
sequência de desligamento.
A partir do PHP 8.4.0, quando
ciclos de coleta
ocorrem durante a execução de uma
Fiber, os destrutores de objetos
programados para coleta são executados em uma Fiber separada, chamada
gc_destructor_fiber
.
Se esta Fiber for suspensa, uma nova será criada para executar quaisquer
destrutores restantes.
O gc_destructor_fiber
anterior não será mais
referenciado pelo coletor de lixo e poderá ser coletado se não for
referenciado em outro lugar.
Objetos cujo destrutor está suspenso não serão coletados até que o
destrutor retorne ou a própria Fiber seja coletada.
Nota:
Destrutores chamados durante o encerramento da execução do script já enviaram os cabeçalhos HTTP.
O diretório atual na fase de encerramento do script pode ser diferente
em alguns SAPIs (e.g. Apache).
Nota:
Tentar disparar uma exceção em um destrutor (chamado no término
do script), lançará um erro fatal.