PHP 7 中属性的类型提示?

Posted

技术标签:

【中文标题】PHP 7 中属性的类型提示?【英文标题】:Type hinting for properties in PHP 7? 【发布时间】:2016-09-12 06:55:52 【问题描述】:

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

我的意思是,不仅针对 setters/getters,还针对属性本身。

类似:

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


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

【问题讨论】:

我不知道。但是,一般来说,对属性值的 any 约束无论如何都应该通过 setter 来完成。由于 setter 可以轻松地为“value”参数提供类型提示,因此您可以开始了。 许多框架都使用 protected 属性(主要用于控制器)。特别是对于那些情况,它会非常有用。 【参考方案1】:

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;
    

您还可以创建公共属性并使用 docblock 向阅读代码和使用 IDE 的人提供类型信息,但这不提供运行时类型检查:

class Person

    /**
      * @var string
      */
    public $name;

确实,您可以将 getter 和 setter 以及 docblock 结合起来。

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

【讨论】:

听起来不错。迫不及待地想看看下一个版本会发生什么! 另一个重要的问题是引用的处理,它不能真正与类型声明很好地交互,并且可能必须为这些属性禁用。即使没有性能问题,也无法做到,比如 array_push($this->foo, $bar)sort($this->foobar) 将是一件大事。 类型强制是如何工作的?例如:(new Person())->dateOfBirth = '2001-01-01';... 提供declare(strict_types=0); 即是。它会抛出还是使用DateTimeImmutable 构造函数?如果是这种情况,如果字符串是无效日期,会抛出哪种错误? TypeError? @Imerino 没有隐式转换为 DateTime(Immutable) 并且从来没有【参考方案2】:

7.4+:

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


7.3 或更低

根据我仍然从该线程收到的通知,我相信那里的许多人都有/正在遇到与我相同的问题。对于这种情况,我的解决方案是在 trait 中结合 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 将检查当前对象中是否声明了一些 setter (method_exists($this, $setter))。否则它只会像往常一样设置它的值。

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

只要 PHP 检测到不是 Bar 实例被传递给 setter,它就会自动触发致命错误:Uncaught TypeError: Argument 1 passing to Foo::setBar() must be Bar 的实例,NotBar 的实例给定

【讨论】:

【参考方案3】:

为 PHP 7.4 编辑:

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

    class Foo

    protected ?Bar $bar;
    public int $id;
    ...

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

bool、int、float、string、array、object 可迭代 自己,父母 任何类或接口名称 ?type // 其中“type”可以是上述任何一种

PHP

这实际上是不可能的,你只有 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(可为空)起,您实际上可以将返回键入为 ?Bar,因为它可能为空(在 php7.0 中不可用。)

从 php7.1 开始,您也可以将返回键入为 void

【讨论】:

【参考方案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 ...

【讨论】:

以上是关于PHP 7 中属性的类型提示?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 7接口,返回类型提示和自我

为啥在 php 7 中添加回调类型提示会触发弃用警告

PHP 7 Bool 类型提示不起作用

PHP 7 中的类型提示 - 对象数组

Symfony3 表单组件试图将 null 传递给 PHP 7 中的类型提示方法

Netbeans (PHP) 中的变量类型提示