Когда вы объявляете, что Bar
расширяет Foo
, конструктор Foo
по умолчанию вызывает конструктор Bar
для инициализации его родительских свойств (в данном случае, public $test
). Если вы определяете пользовательский конструктор для своих дочерних классов, вам также следует вызвать родительский конструктор. В этом разделе документации PHP описано, как это работает :
<?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();
Для конкретной функциональности в вашем примере я бы предпочел поместить логику кода в защищенные конструкторы и вызывать их из фабричного метода:
<?php
class Foo {
public $test;
protected function __construct() {
$this->test = 123;
}
public static function create(): Foo {
return new self();
}
}
class Bar extends Foo {
public $baz;
protected function __construct() {
parent::__construct();
$this->baz = 456;
}
public static function create(): Foo {
return new self();
}
}
Обратите внимание, что я изменил тип возвращаемого значения функции create()
, чтобы он всегда был Foo
. PHP не позволит вам изменять типы возвращаемых функций, перезаписываемых из родительского класса, и использование self
в обоих случаях сделает это.
Однако это не проблема. Bar::create()
дает нам Bar
объект, настроенный так, как вы ожидаете. Тип возвращаемого значения просто должен быть совместим с Foo
, а поскольку Bar
реализует интерфейс Foo
, он считается совместимым с типом.
Вы можете еще больше упростить этот код, используя ключевое слово static
:
<?php
class Foo {
public $test;
protected function __construct() {
$this->test = 123;
}
public static function create(): Foo {
return new static();
}
}
class Bar extends Foo {
public $baz;
protected function __construct() {
parent::__construct();
$this->baz = 456;
}
}
var_dump(Bar::create()); /* Outputs:
object(Bar)#1 (2) {
["baz"]=>
int(456)
["test"]=>
int(123)
}
*/
Может случиться так, что использование защищенных конструкторов не вариант, например потому что у вас уже есть публичный конструктор с необходимыми аргументами или похожей ситуацией. В этом случае вам придется прибегнуть к тому, что объясняется в других ответах, создав временный Foo
и скопировав значения свойств в новый Bar
объект:
<?php
class Foo {
public $test;
public static function create(): Foo {
$result = new self();
$result->test = 123;
return $result;
}
}
class Bar extends Foo {
public $baz;
public static function create(): Foo {
$foo = parent::create();
$result = new self();
foreach($foo as $property => $value) {
$result->$property = $value;
}
$result->baz = 456;
return $result;
}
}
var_dump(Bar::create()); /* Outputs:
object(Bar)#2 (2) {
["baz"]=>
int(456)
["test"]=>
int(123)
}
*/