Конструктор
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
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-строку, предварительно обрабатывает её,
а затем создаёт пустой объект. При этом 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 отправляет HTTP-заголовки перед обработкой деструкторов.
На этапе завершения работы скрипта рабочая директория, как её определяет PHP, иногда
не совпадает с рабочей директорией, которую возвращают отдельные SAPI-интерфейсы
(например, в Apache).
Замечание:
Попытка выбросить исключение из деструктора, который вызвали во время
завершения работы скрипта, вызовет фатальную ошибку.