Поддерживает ли php 7 указание типов для свойств класса?
Я имею в виду, не только для сеттеров / получателей, но и для самого свойства.
Что-то вроде:
class Foo {
/**
*
* @var Bar
*/
public $bar : Bar;
}
$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
php
attributes
php-7
type-hinting
variable-types
КарлосКаручче
источник
источник
Ответы:
PHP 7.4 будет поддерживать такие типизированные свойства :
class Person { public string $name; public DateTimeImmutable $dateOfBirth; }
PHP 7.3 и более ранние версии не поддерживают это, но есть альтернативы.
Вы можете создать частное свойство, доступное только через геттеры и сеттеры, которые имеют объявления типов:
class Person { private $name; public function getName(): string { return $this->name; } public function setName(string $newName) { $this->name = $newName; } }
Вы также можете сделать общедоступное свойство и использовать docblock для предоставления информации о типе людям, читающим код и использующим IDE, но это не обеспечивает проверку типов во время выполнения:
class Person { /** * @var string */ public $name; }
И действительно, вы можете комбинировать геттеры, сеттеры и докблок.
Если вы боитесь, вы могли бы сделать свойство поддельного с
__get
,__set
,__isset
и__unset
магических методами , и проверьте типы самостоятельно. Однако я не уверен, что рекомендовал бы это.источник
array_push($this->foo, $bar)
илиsort($this->foobar)
будет большим делом.(new Person())->dateOfBirth = '2001-01-01';
... При условии,declare(strict_types=0);
что есть. Будет ли он использоватьDateTimeImmutable
конструктор? И если это так, то какая ошибка будет выдана, если строка является недопустимой датой?TypeError
?7.4+:
Хорошая новость, что это будет реализовано в новых выпусках, как отметил @Andrea. Я просто оставлю это решение здесь на случай, если кто-то захочет использовать его до 7.4.
7.3 или меньше
Основываясь на уведомлениях, которые я до сих пор получаю из этой ветки, я считаю, что у многих людей была та же проблема, что и у меня. Моим решением для этого случая было объединение сеттеров +
__set
магический метод внутри трейта для имитации этого поведения. Вот:trait SettersTrait { /** * @param $name * @param $value */ public function __set($name, $value) { $setter = 'set'.$name; if (method_exists($this, $setter)) { $this->$setter($value); } else { $this->$name = $value; } } }
А вот и демонстрация:
class Bar {} class NotBar {} class Foo { use SettersTrait; //It could be implemented within this class but I used it as a trait for more flexibility /** * * @var Bar */ private $bar; /** * @param Bar $bar */ protected function setBar(Bar $bar) { //(optional) Protected so it wont be called directly by external 'entities' $this->bar = $bar; } } $foo = new Foo(); $foo->bar = new NotBar(); //Error //$foo->bar = new Bar(); //Success
Объяснение
Прежде всего, определите
bar
как частное свойство, чтобы PHP__set
выполнял автоматическое преобразование .__set
проверит, объявлен ли какой-нибудь сеттер в текущем объекте (method_exists($this, $setter)
). В противном случае он установит только свое значение, как обычно.Объявите метод установки (setBar), который получает аргумент с подсказкой типа (
setBar(Bar $bar)
).Пока PHP обнаруживает, что что-то, что не является
Bar
экземпляром, передается сеттеру, он автоматически запускает фатальную ошибку: Uncaught TypeError: аргумент 1, переданный в Foo :: setBar (), должен быть экземпляром Bar, экземпляр NotBar заданисточник
Изменить для PHP 7.4:
Начиная с PHP 7.4 вы можете вводить атрибуты ( документация / Wiki ), что означает, что вы можете:
class Foo { protected ?Bar $bar; public int $id; ... }
Согласно вики, все допустимые значения:
PHP <7,4
На самом деле это невозможно, и у вас есть только 4 способа симулировать это:
Я объединил их все здесь
class Foo { /** * @var Bar */ protected $bar = null; /** * Foo constructor * @param Bar $bar **/ public function __construct(Bar $bar = null){ $this->bar = $bar; } /** * @return Bar */ public function getBar() : ?Bar{ return $this->bar; } /** * @param Bar $bar */ public function setBar(Bar $bar) { $this->bar = $bar; } }
Обратите внимание, что вы действительно можете ввести return как? Bar, начиная с php 7.1 (допускающий значение NULL), потому что он может быть нулевым (недоступно в php7.0.)
Вы также можете ввести возврат как недействительный, поскольку php7.1
источник
Вы можете использовать сеттер
class Bar { public $val; } class Foo { /** * * @var Bar */ private $bar; /** * @return Bar */ public function getBar() { return $this->bar; } /** * @param Bar $bar */ public function setBar(Bar $bar) { $this->bar = $bar; } } $fooInstance = new Foo(); // $fooInstance->bar = new NotBar(); //Error $fooInstance->setBar($fooInstance);
Вывод:
TypeError: Argument 1 passed to Foo::setBar() must be an instance of Bar, instance of Foo given, called in ...
источник