Атрибуты — Тип подсказки для свойств в PHP 7?

Поддерживает ли php 7 подсказки типов для свойств класса?

Я имею в виду, не только для Сеттера / добытчики но для самой собственности.

Что-то вроде:

class Foo {
/**
*
* @var Bar
*/
public $bar : Bar;
}

$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error

51

Решение

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;
}
}

Вы также можете сделать открытое свойство и использовать докблок для предоставления информации о типе людям, читающим код и использующим IDE, но это не обеспечивает проверку типов во время выполнения:

class Person
{
/**
* @var string
*/
public $name;
}

И действительно, вы можете комбинировать геттеры и сеттеры и докблок.

Если вы более предприимчивы, вы можете сделать поддельную собственность с __get, __set, __isset а также __unset магические методы, и проверьте типы самостоятельно. Хотя я не уверен, что рекомендую это.

76

Другие решения

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

2

На самом деле это невозможно, и у вас есть только 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 (с нулевым значением), потому что он может быть нулевым (недоступно в php7.0.)

Вы также можете ввести возврат как void, так как php7.1

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 ...
0
По вопросам рекламы [email protected]