为PHP 7中的属性键入提示吗?


80

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

我的意思是,不仅针对设置者/获取者,还针对财产本身。

就像是:

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

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

1
不是我知道的。但是,一般来讲,对财产价值的任何限制都应通过设置员来完成。由于setter可以轻松地为“ value”参数提供类型提示,因此您很高兴。
Niet the Dark Absol's

那里的许多框架都使用受保护的属性(大多数用于控制器)。特别是对于那些情况,这将非常有用。
CarlosCarucce '16

Answers:


134

PHP 7.4将支持如下类型的属性

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__unset魔术方法,并检查自己的类型。不过,我不确定是否会推荐。


听起来真的很好。等不及要看下一个发行版中的新功能了!
CarlosCarucce '16

另一个重要的问题是对引用的处理,这些引用与类型声明的交互作用不佳,可能必须禁用此类属性。即使没有性能问题,也无法做到,array_push($this->foo, $bar)或者说,sort($this->foobar)将是一件大事。
安德里亚(Andrea)

类型强制将如何工作?例如:(new Person())->dateOfBirth = '2001-01-01';...提供的declare(strict_types=0);是。它会抛出还是使用DateTimeImmutable构造函数?如果是这样,如果字符串是无效的日期,将引发哪种错误?TypeError
lmerino

@Imerino没有对DateTime(Immutable)的隐式转换,而且从来没有过
Andrea

12

7.4+:

如@Andrea所指出的,好消息是它将在新版本中实现。如果有人想在7.4之前使用它,我将在此处保留此解决方案。


7.3以下

根据我仍从该线程收到的通知,我相信很多人都遇到过/正在遇到与我相同的问题。对于这种情况,我的解决方案是在特征中结合使用setter__setmagic方法,以模拟这种行为。这里是:

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(Bar $bar))的setter方法(setBar )。

只要PHP检测到非Bar实例传递给setter,它将自动触发致命错误:Uncaught TypeError:传递给Foo :: setBar()的参数1必须是Bar的实例,NotBar的实例已给定


4

编辑PHP 7.4:

从PHP 7.4开始,您可以输入属性(文档/ Wiki),这意味着您可以执行以下操作:

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

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

  • 布尔,整数,浮点数,字符串,数组,对象
  • 可重复的
  • 自我,父母
  • 任何类或接口名称
  • ?type //其中“ type”可以是上述任何一种

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

请注意,由于php 7.1(可为空),您实际上可以将返回值键入?Bar,因为它可以为null(在php7.0中不可用)。

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


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 ...
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.