在PHP 7中输入属性的提示?

Car*_*cce 72 php attributes variable-types type-hinting php-7

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

我的意思是,不仅仅是针对建立者/吸气者,而是针对房产本身.

就像是:

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

$fooInstance = new Foo();
$fooInstance->bar = new NotBar(); //Error
Run Code Online (Sandbox Code Playgroud)

And*_*rea 119

PHP 7.4 将支持类似的类型属性:

class Person
{
    public string $name;
    public DateTimeImmutable $dateOfBirth;
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

class Person
{
    /**
      * @var string
      */
    public $name;
}
Run Code Online (Sandbox Code Playgroud)

实际上,您可以组合getter和setter以及docblock.

如果你更喜欢冒险,你可以使用假的性质__get,__set,__isset__unset魔术方法,并检查自己的类型.不过,我不确定我是否会推荐它.

  • 啊.遗憾的是RFC失败了:( (28认同)
  • Peformance impact on non-type-hinted classes. (10认同)
  • 它被7.4接受了,所以我更新了这个答案. (6认同)
  • 是否有一些解释为什么这个RFC被拒绝了? (5认同)
  • 是否有可能在将来重新考虑此RFC?(特别是如果性能问题得到缓解.)它赢得了60%的大多数34到23,几乎是所需的66%.https://wiki.php.net/rfc/typed-properties (5认同)
  • Typed Properties目标PHP版本已从7.3更改为7.4,投票将在接下来的几天内开始 (3认同)
  • 作品中有一个新的RFC https://wiki.php.net/rfc/typed_properties_v2 (2认同)

Car*_*cce 8

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是演示:

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
Run Code Online (Sandbox Code Playgroud)

说明

首先,将其定义bar为私有属性,以便PHP __set 自动进行铸造。

__set将检查当前对象(method_exists($this, $setter))中是否声明了一些设置器。否则,它将仅照常设置其值。

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

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


Bru*_*ard 6

为 PHP 7.4 编辑:

从 PHP 7.4 开始,您可以输入属性(文档/维基),这意味着您可以:

    class Foo
{
    protected ?Bar $bar;
    public int $id;
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

  • 布尔、整数、浮点数、字符串、数组、对象
  • 可迭代的
  • 自己,父母
  • 任何类或接口名称
  • ?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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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