PHP7中属性的类型提示?

PHP7中属性的类型提示?,php,attributes,php-7,type-hinting,variable-types,Php,Attributes,Php 7,Type Hinting,Variable Types,PHP7支持类属性的类型暗示吗 我的意思是,不仅对于setter/getter,而且对于属性本身 比如: class Foo { /** * * @var Bar */ public $bar : Bar; } $fooInstance = new Foo(); $fooInstance->bar = new NotBar(); //Error 您可以使用setter class Bar { public $val; } clas

PHP7支持类属性的类型暗示吗

我的意思是,不仅对于setter/getter,而且对于属性本身

比如:

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与此类似:


PHP7.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结合起来

如果你更喜欢冒险,你可以用做一个假的属性,然后自己检查类型。不过,我不确定我是否会推荐它。

7.4+:

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


7.3或更低

根据我仍然从这个帖子收到的通知,我相信很多人都有/正在有与我相同的问题。我对这个案例的解决方案是将setters+
\uu set
魔术方法与trait结合起来,以模拟这种行为。 这是:

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将自动转换
\u set

\u set
将检查当前对象中是否声明了某些setter(
方法存在($this$setter)
)。否则,它将只设置其正常值

声明一个setter方法(setBar),该方法接收类型暗示的参数(
setBar(Bar$Bar)


只要PHP检测到非
Bar
实例的内容正在传递给setter,它就会自动触发一个致命错误:uncaughttypeerror:传递给Foo::setBar()的参数1必须是给定的Bar、NotBar实例为PHP7.4编辑:

由于PHP 7.4,您可以键入属性(/),这意味着您可以执行以下操作:

    class Foo
{
    protected ?Bar $bar;
    public int $id;
    ...
}
根据wiki,所有可接受的值为:

  • bool、int、float、字符串、数组、对象
  • 宜人的
  • 自我,父母
  • 任何类或接口名称
  • ?类型//其中“类型”可以是上述任一类型
PHP<7.4

这实际上是不可能的,您只有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;
    }
}
请注意,从php7.1开始,您实际上可以将返回键入?Bar(可为null),因为它可以为null(在php7.0中不可用)


您还可以键入从php7.1开始的void返回,我不知道这一点。但是,一般来说,对属性值的任何约束都应该通过setter来完成。因为setter可以很容易地为“value”参数提供一个typehint,所以您可以使用它。许多框架都使用受保护的属性(主要用于控制器)。特别是在那些情况下,这将非常有用。听起来真的很好。迫不及待地想看看下一个版本会有什么!另一个重要的问题是引用的处理,它与类型声明的交互不太好,可能必须对此类属性禁用。即使没有性能问题,也无法执行,比如说,
array\u push($this->foo,$bar)
sort($this->foobar)
将是一件大事。强制类型将如何工作?例如:
(newperson())->出生日期='2001-01-01'。。。提供
声明(严格类型=0)就是这样。它会抛出还是使用
datetimeimmunitable
构造函数?如果是这样的话,如果字符串是无效日期,会抛出哪种错误
TypeError
?@Imerino没有到DateTime的隐式转换(不可变),而且从未被转换过
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;
    }
}