Конструктор
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,
но всё равно вызовет этот метод как конструктор.
PHP вызовет как конструктор метод __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
$some_json_string = '{ "id": 1004, "name": "Elephpant" }';
$some_xml_string = "<animal><id>1005</id><name>Elephpant</name></animal>";
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 = simplexml_load_string($xml);
$new = new static();
$new->id = (int) $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);
var_dump($p1, $p2, $p3);
Конструктор разрешено делать закрытым или защищённым, чтобы исключить
вызов конструктора извне. Тогда создать объект класса получится только
через статический метод. Доступ к закрытым методам класса есть и у конструктора,
и у статического метода, поскольку конструктор, статический и закрытый методы
определили в одном и том же классе, даже если один экземпляр объекта вызывает
закрытый метод другого. Закрытый конструктор необязателен,
и будет ли в закрытом конструкторе смысл, определяет ситуация.
В примере выше три открытых статических метода показывают разные способы,
которыми они создают экземпляр объекта.
-
Метод
fromBasicData() принимает конкретные параметры, создаёт экземпляр
класса через конструктор и возвращает результат.
-
Метод
fromJson() принимает JSON-строку и выполняет над
строкой предварительную обработку, чтобы преобразовать строку в формат,
который требует конструктор. Затем метод возвращает новый объект.
-
Метод
fromXml() принимает XML-строку, предварительно обрабатывает её,
а затем создаёт пустой объект. При этом PHP вызывает конструктор, но поскольку
параметры конструктора необязательны, метод пропускает их. Затем непосредственно
перед возвратом результата метод присваивает значения свойствам объекта.
В каждом из трёх случаев ключевое слово static транслируется в имя класса,
в котором вызвали код. В примере — в имя класса Product.
Деструкторы
Концепция деструктора PHP повторяет концепцию других объектно-ориентированных
языков, например C++. PHP вызовет деструктор, как только не останется ссылок
на конкретный объект, или в другом порядке в течение завершения работы.
Пример #6 Пример использования деструктора
<?php
class MyDestructableClass
{
function __construct()
{
print "Конструктор\n";
}
function __destruct()
{
print "Уничтожается " . __CLASS__ . "\n";
}
}
$obj = new MyDestructableClass();
Как и конструкторы, движок PHP не будет неявно вызывать деструкторы,
которые объявили в родительском классе. Необходимо вызвать parent::__destruct()
в теле деструктора дочернего класса, чтобы запустить деструктор родительского класса.
Аналогично конструкторам, дочерний класс, в котором
не определен деструктор, наследует деструктор из родительского класса.
Движок вызовет деструктор, даже если выполнение скрипта
остановила функция exit(). Вызов функции exit()
в деструкторе предотвратит запуск остальных процедур завершения работы.
Деструктор, который создаёт новые ссылки на свой объект, не вызовется второй раз,
когда счётчик ссылок снова достигнет нуля или при завершении работы программы.
Начиная с PHP 8.4.0 при запуске сборки циклических ссылок
внутри файбера
деструкторы объектов-кандидатов на уничтожение выполняются в отдельном файбере,
который называется gc_destructor_fiber.
Сборщик создаст новый файбер сборки мусора для выполнения оставшихся деструкторов,
если текущий файбер сборщика приостановили внутри деструктора уничтожаемого объекта.
Сборщик мусора перестанет ссылаться на предыдущий файбер gc_destructor_fiber,
который станет доступен для сборки, если на него не останется других ссылок.
Уничтожение объектов с приостановленным деструктором откладывается
до завершения выполнения деструктора или уничтожения самого файбера.
Замечание:
Движок отправляет HTTP-заголовки ещё до начала этапа завершения работы скрипта, на котором вызываются деструкторы.
Относительные пути в деструкторах разрешаются в неожиданные абсолютные пути,
когда SAPI наподобие веб-сервера Apache изменяют текущий рабочий каталог на этапе завершения работы скрипта.
Замечание:
Попытка выбросить исключение из деструктора, который вызвали на этапе
завершения работы скрипта, вызовет фатальную ошибку.