PHP 中的属性类型提示 7?

Type hinting for properties in PHP 7?

php 7 是否支持 class 属性的类型提示?

我的意思是,不只是为了 setters/getters,而是为了 属性 本身。

类似于:

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

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

您可以使用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 ...

PHP 7.4 will support typed properties 像这样:

class Person
{
    public string $name;
    public DateTimeImmutable $dateOfBirth;
}

PHP 7.3 及更早版本不支持此功能,但有一些替代方案。

您可以创建一个私有 属性,它只能通过具有类型声明的 getter 和 setter 访问:

class Person
{
    private $name;
    public function getName(): string {
        return $this->name;
    }
    public function setName(string $newName) {
        $this->name = $newName;
    }
}

您还可以制作 public 属性 并使用文档块向阅读代码和使用 IDE 的人提供类型信息,但这不提供运行时类型检查:

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

事实上,您可以将 getter 和 setter 与文档块结合起来。

如果你更喜欢冒险,你可以用 __get, __set, __isset and __unset magic methods 制作一个假的 属性,然后自己检查类型。不过,我不确定我是否会推荐它。

7.4+:

正如@Andrea 指出的那样,好消息是它将在新版本中实现。 我将把这个解决方案留在这里,以防有人想在 7.4

之前使用它

7.3以下

根据我仍然从该线程收到的通知,我相信那里的许多人 had/is 遇到了与我相同的问题。我对这种情况的解决方案是在特征中组合 setters + __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)) 中是否声明了某些 setter。否则它只会像往常一样设置它的值。

声明接收类型提示参数 (setBar(Bar $bar)) 的 setter 方法 (setBar)。

只要 PHP 检测到某些不是 Bar 实例的东西正在传递给 setter,它就会自动触发致命错误:未捕获TypeError:传递给 Foo::setBar() 的参数 1 必须是 Bar 的实例,给定的 NotBar 实例

编辑 PHP 7.4 :

从 PHP 7.4 开始,您可以键入属性 (Documentation / Wiki),这意味着您可以:

    class Foo
{
    protected ?Bar $bar;
    public int $id;
    ...
}

根据 wiki,所有可接受的值是:

  • bool, int, float, string, array, object
  • 可迭代
  • 自己,parent
  • 任何class或接口名称
  • ?type // 其中“type”可以是以上任何一种

PHP < 7.4

这实际上是不可能的,您只有 4 种方法可以实际模拟它:

  • 默认值
  • 评论区中的装饰器
  • 构造函数中的默认值
  • getter 和 setter

我把它们全部合并在这里

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

请注意,自 php 7.1(可为空)以来,您实际上可以将 return 键入为 ?Bar,因为它可能为空(在 php7.0 中不可用。)

您还可以将 return 键入为 void,因为 php7.1