Конструктор
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);
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
транслируется в имя класса,
в котором этот код вызывается. В нашем случае — в класс Product
.
Деструкторы
PHP наследует концепцию деструктора, аналогичную другим объектно-ориентированным языкам, например, C++.
Деструктор будет вызван при освобождении всех ссылок
на объект или при завершении
скрипта (порядок выполнения деструкторов не гарантируется).
Пример #6 Пример использования деструктора
<?php
class MyDestructableClass
{
function __construct() {
print "Конструктор\n";
}
function __destruct() {
print "Уничтожается " . __CLASS__ . "\n";
}
}
$obj = new MyDestructableClass();
Как и конструкторы, деструкторы, объявленные
в родительском классе, не будут вызываться автоматически.
Чтобы запустить деструктор родительского класса,
необходимо вызвать parent::__destruct()
в теле деструктора дочернего класса. Аналогично конструкторам, дочерний класс, в котором
не определен деструктор, наследует его из родительского класса.
Деструктор будет вызван даже если скрипт был остановлен
функцией exit(). Вызов функции exit()
в деструкторе предотвратит запуск всех остальных процедур завершения работы.
Замечание:
Деструкторы, вызываемые при завершении скрипта, вызываются после отправки
HTTP-заголовков. На этапе завершения работы скрипта
рабочая директория SAPI (например, в Apache) может измениться.
Замечание:
Попытка выбросить исключение из деструктора (вызванного во время
завершения работы скрипта) вызовет фатальную ошибку.