Введите hinting для свойств в PHP 7?
Поддерживает ли тип поддержки типа php 7 свойства класса?
Я имею в виду не только для сеттеров/геттеров, но и для самой собственности.
Что-то вроде:
class Foo {
/**
*
* @var Bar
*/
public $bar : Bar;
}
$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
Ответы
Ответ 1
PHP 7.0, который является текущим незначительным выпуском на момент написания (2016-05-16), не поддерживает это. Были предприняты некоторые усилия в этом направлении (например, Typeed Properties RFC, который был отклонен для PHP 7.1, но может быть в 7.4), но в настоящее время нет прямой поддержки.
В то же время есть несколько альтернатив.
Вы можете сделать частную собственность, доступную только через геттеры и сеттеры, у которых есть объявления типа:
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;
}
И действительно, вы можете объединить геттеры и сеттеры и докблоки.
Если вы более предприимчивы, вы можете сделать фальшивое свойство с помощью __set
__get
, __set
, __isset
и __unset
magic и сами проверить типы. Хотя я не уверен, рекомендую ли я это.
Ответ 2
Основываясь на уведомлениях, которые я все еще получаю из этой темы, я считаю, что у многих людей есть/есть такая же проблема, что и у меня. Мое решение для этого случая состояло в объединении __set
+ __set
magic внутри черты, чтобы имитировать это поведение. Вот:
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)
). В противном случае он будет устанавливать только его значение, как обычно.
Объявите метод setter (setBar), который получает аргумент типа- setBar(Bar $bar)
).
Пока PHP обнаруживает, что экземпляр экземпляра не является экземпляром Bar
, он автоматически запускает Fatal Error: Uncaught TypeError: Аргумент 1, переданный в Foo :: setBar(), должен быть экземпляром Bar, экземпляр NotBar данный
Ответ 3
На самом деле это невозможно, и у вас есть только 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 (nullable), потому что он может быть нулевым (недоступным в php7.0.)
Вы также можете ввести return как void с php7.1
Ответ 4
Вы можете использовать setter
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 ...