Конструктор
PHP дозволяє розробникам оголошувати методи-конструктори. Класи, в яких
оголошено метод-конструктор, будуть викликати його під час створення нового
об'єкта. Це корисно, коли перед використанням об'єкта його потрібно
ініціалізувати.
Зауваження:
Батьківський конструктор не викликається автоматично, якщо в дочірньому
класі оголошено власний конструктор. Для цього необхідно здійснити виклик
parent::__construct() всередині дочірнього конструктора.
Якщо ж в дочірньому класі не оголошено власний конструктор, то його можна
наслідувати з батьківського класу як звичайний метод (якщо той не оголошено
як закритий).
Приклад #1 Конструктори в наслідуванні
<?php
class BaseClass {
function __construct() {
print "В конструкторі класу BaseClass\n";
}
}
class SubClass extends BaseClass {
function __construct() {
parent::__construct();
print "В конструкторі класу SubClass\n";
}
}
class OtherSubClass extends BaseClass {
// Успадковує конструктор класу BaseClass
}
// Виводить: В конструкторі класу BaseClass
$obj = new BaseClass();
// Виводить:
// В конструкторі класу BaseClass
// В конструкторі класу SubClass
$obj = new SubClass();
// Виводить: В конструкторі класу BaseClass
$obj = new OtherSubClass();
?>
На відміну від інших методів, на
__construct() під час наслідування
не поширюються звичайні правила сумісності
сигнатур.
Конструктори — це звичайні методи, виклик яких здійснюється під час
створення примірника їхнього класу. Таким чином, для них можна оголошувати
довільну кількість параметрів, які можуть бути обов'язковими, мати тип та
початкові значення. Параметри конструктору передають в дужках після назви
класу.
Приклад #2 Використання параметрів конструктора
<?php
class Point {
protected int $x;
protected int $y;
public function __construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}
// Вказати обидва параметри.
$p1 = new Point(4, 5);
// Вказати тільки обов'язковий параметр. $y отримає початкове значення 0.
$p2 = new Point(4);
// З названими параметрами (починаючи з PHP 8.0):
$p3 = new Point(y: 5, x: 4);
?>
Якщо клас не має конструктора, або конструктор не вимагає обов'язкових
параметрів, дужки можуть бути пропущені.
Конструктори в старому стилі
До PHP 8.0.0, в глобальному просторі імен однойменний з класом метод
інтерпретується як конструктор старого зразка. Такий синтаксис є
застарілим і призводить до помилки E_DEPRECATED
, проте
досі дозволяє викликати цю функцію, як конструктор. Якщо оголошено і метод
__construct(), і однойменний метод,
то для виклику буде взято __construct().
В класах інших просторів імен, чи будь-яких інших класах, починаючи з PHP
8.0.0, однойменний метод не має особливого значення.
В новому коді завжди потрібно використовувати метод
__construct().
Ключове слово new
в ініціалізаторах
Починаючи з PHP 8.1.0, об'єкти можуть бути початковими значеннями
параметрів, статичних змінних, глобальних констант, а також параметрів
атрибутів. Об'єкти також можуть бути передані в функцію
define().
Зауваження:
в таких випадках не дозволено застосовувати: динамічні, нерядкові назви
класів або анонімні класи; розпакування параметрів; непідтримувані
вирази, як параметри.
Приклад #4 Використання new
в ініціалізаторах
<?php
// Дозволено:
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,
) {}
}
// Не дозволено (помилка компіляції):
function test(
$a = new (CLASS_NAME_CONSTANT)(), // динамічна назва класу
$b = new class {}, // анонімний клас
$c = new A(...[]), // розпакування параметрів
$d = new B($abc), // непідтримуваний вираз
) {}
?>
Способи статичного створення об'єктів
PHP підтримує тільки один конструктор на клас. Однак, в деяких випадках
потрібно будувати об'єкт по-різному з різними вхідними даними.
Рекомендованим способом є використання статичних методів, як надбудов над
конструкторами.
Приклад #5 Використання способів статичного створення об'єктів
<?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 {
// Користувацька логіка.
$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);
Конструктор можна зробити закритим або захищеним, щоб заборонити виклик
його ззовні. В такому випадку тільки статичний метод зможе повернути
примірник свого класу. Статичні методи мають доступ до закритих та
захищених методів, визначених в спільному класі, навіть якщо останні
належать до різних примірників цього класу. Закритий конструктор є
необов'язковим і може мати або не мати сенс, залежно від випадку
застосування.
В прикладі вище три загальнодоступні статичні методи показують різні
способи створення об'єктів.
fromBasicData()
приймає основні параметри та передає
їх в конструктор, викликає його та повертає отриманий примірник.
fromJson()
приймає JSON-рядок, робить деякі
перетворення та у форматі, що підходить конструктору, передає йому. Потім
повертає новий об'єкт.
fromXml()
приймає XML-рядок, обробляє його, а потім
створює чистий об'єкт. Метод викликає конструктор без параметрів,
оскільки всі вони необов'язкові. Далі безпосередньо задає значення
властивостям об'єкта та повертає цей об'єкт.
У всіх трьох випадках ключове слово static
транслюється в
назву класу, всередині якого воно застосовано.
In this case, Product
.
Деструктор
PHP має концепцію деструктора, схожу з тими, що є в інших
об'єктно-орієнтованих мовах, як от C++. Метод деструктора об'єкта буде
викликано одразу за відсутності посилань на об'єкт, або в довільному
порядку під час процедури завершення скрипта.
Приклад #6 Використання деструктора
<?php
class MyDestructableClass
{
function __construct() {
print "In constructor\n";
}
function __destruct() {
print "Destroying " . __CLASS__ . "\n";
}
}
$obj = new MyDestructableClass();
Схоже з конструктором, батьківський деструктор не буде викликано
автоматично. Щоб запустити батьківський деструктор, потрібно явно викликати
parent::__destruct() в тілі дочірнього деструктора.
Також, як і у випадку з конструктором, якщо в дочірньому класі не оголошено
деструктор, то він може успадковувати деструктор батьківського класу.
Деструктор буде викликано, навіть коли виконання скрипта припиняється
функцією exit(). Виклик exit()
в деструкторі запобігає виконанню решти процедур завершення скрипта.
Якщо деструктор створює нові посилання на свій об'єкт, його не буде
викликано вдруге, коли кількість посилань знову досягне нуля чи під час
завершення скрипта.
Починаючи з PHP 8.4.0, якщо збирання циклів вмикається
під час виконання файбера, то
заплановані деструктори об'єктів виконуються в окремому файбері з назвою
gc_destructor_fiber
. Якщо цей файбер призупинено, то
створюється новий, щоб довиконати деструктори. Збирач сміття більше не
посилатиметься на попередній файбер gc_destructor_fiber
,
який буде знищений під час наступного збирання сміття. Об'єкт, чий
деструктор призупинено, не знищиться, допоки деструктор не буде виконано
або не буде знищено файбер.
Зауваження:
Під час завершення скрипта деструктори викликаються після відправлення
HTTP-заголовків. Робоча тека під час завершення скриптів може відрізнятись
в деяких SAPI (наприклад в Apache).
Зауваження:
Спроба кинути виключення з деструктора, викликаного під час завершення
скрипта, призведе до фатальної помилки.