Введите 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 ...