• 技术文章 >后端开发 >PHP7

    PHP 7.4中的类型属性(Typed Properties)

    藏色散人藏色散人2019-11-30 18:01:41原创1227
    在PHP 7.4中添加了类型属性,并对PHP的类型系统进行了重大改进。这些更改是完全可选的,并且不破坏以前的版本。

    在这篇文章中,我们将深入了解这个特性,但首先让我们总结最重要的几点:

    ● 它们自PHP 7.4起可用

    ● 它们只在类中可用,并且需要访问修饰符:public、protected或private;或var.

    ● 除了void和callable之外,所有类型都是允许的

    他们的实际情况是这样的:

    class Foo
    {
        public int $a;
        public ?string $b = 'foo';
        private Foo $prop;
        protected static string $static = 'default';
    }

    #未初始化

    在查看有趣的内容之前,首先要讨论类型属性的一个重要方面。

    不管你第一眼看到的是什么,下面的代码是有效的:

    class Foo
    {
        public int $bar;
    }
    $foo = new Foo;

    即使$bar的值不是一个整数后,使一个对象Foo, PHP只会抛出一个错误时,$bar被访问:

    var_dump($foo->bar);
    Fatal error: Uncaught Error: Typed property Foo::$bar 
    must not be accessed before initialization

    从错误消息中可以看到,有一种新的“变量状态”:未初始化。

    如果$bar没有类型,则其值将为null。但是类型可以为空,因此无法确定是否设置了类型为空的属性,或者只是将其忘记了。这就是为什么添加了“uninitialized(未初始化)”的原因。

    关于未初始化,要记住四件事:

    ● 无法读取未初始化的属性,这样做将导致致命错误。

    ● 因为在访问属性时会检查未初始化状态,所以可以使用未初始化的属性创建对象,即使其类型不可为空。

    ● 您可以先写入未初始化的属性,然后再读取它。

    ● 在类型属性上使用unset将使其未初始化,而取消对非类型化属性的设置将使其为null。

    特别要注意,下面的代码是有效的,其中在构造对象之后设置了非初始化的、不可空的属性

    class Foo
    {
        public int $a;
    }
    $foo = new Foo;
    $foo->a = 1;

    虽然仅在读取属性值时才检查未初始化状态,但在写入属性值时进行类型验证。这意味着您可以确保任何无效类型都不会以属性值的形式结束。

    #默认值和构造函数

    让我们仔细看看如何初始化键入的值。对于标量类型,可以提供一个默认值:

    class Foo
    {
        public int $bar = 4;
        
        public ?string $baz = null;
        
        public array $list = [1, 2, 3];
    }

    注意,如果类型实际上是空的,则只能使用null作为默认值。这似乎是显而易见的,但是有些旧的行为带有参数默认值,其中允许以下操作:

    function passNull(int $i = null)
    { /* … */ }
    passNull(null);

    幸运的是,类型属性不允许这种令人困惑的行为。

    另请注意,对象或类类型不可能有默认值。您应该使用构造函数来设置它们的默认值。

    初始化类型化值的明显地方当然是构造函数:

    class Foo{
        private int $a;
        public function __construct(int $a)
        {
            $this->a = $a;
        }
    }

    但也请记住我前面提到的:在构造函数外部写入未初始化的属性是有效的。只要没有从属性中读取任何内容,就不会执行未初始化检查。

    #类型的类型

    那么究竟什么可以输入,如何输入呢?我已经提到类型化属性只在类中有效(目前),它们需要一个访问修饰符或var关键字在它们前面。

    对于可用类型,除了void和callable之外,几乎所有类型都可以使用。

    因为void意味着没有值,所以不能将其用于键入值是有意义的。 callable稍微有点差别。

    可见,PHP中的“ callable” 可以这样写:

    但也请记住我前面提到的:在构造函数外部写入未初始化的属性是有效的。只要没有从属性中读取任何内容,就不会执行未初始化检查。

    看,一个“callable”在PHP可以这样写:

    $callable = [$this, 'method'];

    假设您有以下(无效)代码:

    class Foo
    {
        public callable $callable;
        
        public function __construct(callable $callable)
        { /* … */ }
    }
    class Bar
    {
        public Foo $foo;
        
        public function __construct()
        {
            $this->foo = new Foo([$this, 'method'])
        }
        
        private function method()
        { /* … */ }
    }
    $bar = new Bar;
    ($bar->foo->callable)();

    在本例中,$callable引用私有Bar::方法,但是在Foo的上下文中被调用。由于这个问题,决定不添加callable的支持。

    不过,这没什么大不了的,因为Closure是一个有效类型,它将记住构造它的$this上下文。

    顺便说一句,这是所有可用类型的列表:

    ● bool

    ● int

    ● float

    ● string

    ● array

    ● iterable

    ● object

    ● ? (nullable)

    ● self & parent

    ● Classes & interfaces

    #强制类型和严格类型

    PHP是我们喜欢和讨厌的一种动态语言,它将尽可能地强制转换类型。假设您在期望整数的地方传递了一个字符串,PHP将尝试自动转换该字符串:

    function coerce(int $i)
    { /* … */ }
    coerce('1'); // 1

    同样的原则也适用于类型属性。

    下面的代码是有效的,并将“1”转换为1。

    class Bar
    {
        public int $i;
    }
    $bar = new Bar;
    $bar->i = '1'; // 1

    如果您不喜欢这种行为,可以通过声明严格类型来禁用它:

    declare(strict_types=1);
    $bar = new Bar;
    $bar->i = '1'; // 1
    Fatal error: Uncaught TypeError: 
    Typed property Bar::$i must be int, string used

    #类型差异和继承

    即使PHP 7.4引入了改进的类型差异,但类型属性仍然不变。

    这意味着以下内容无效:

    class A {}
    class B extends A {}
    class Foo
    {
        public A $prop;
    }
    class Bar extends Foo
    {
        public B $prop;
    }
    Fatal error: Type of Bar::$prop must be A (as in class Foo)

    如果上面的示例似乎并不重要,则应查看以下内容:

    class Foo
    {
        public self $prop;
    }
    class Bar extends Foo
    {
        public self $prop;
    }

    在运行代码之前,PHP将在幕后用它引用的具体类替换self。

    这意味着在本例中会抛出相同的错误。处理它的唯一方法,是执行以下操作:

    class Foo
    {
        public Foo $prop;
    }
    class Bar extends Foo
    {
        public Foo $prop;
    }

    说到继承,您可能会发现很难找到任何好的用例来覆盖继承属性的类型。

    虽然我同意这种观点,但值得注意的是,可以更改继承属性的类型,但前提是访问修饰符也从private更改为protected或public。

    以下代码有效:

    class Foo{
        private int $prop;
    }
    class Bar extends Foo
    {
        public string $prop;
    }

    但是,不允许将类型从可为空的类型更改为不可为空或反向的类型。

    class Foo
    {
        public int $a;
        public ?int $b;
    }
    class Bar extends Foo
    {
        public ?int $a;
        public int $b;
    }
    Fatal error: Type of Bar::$a must be int (as in class Foo)

    翻译:https://stitcher.io/blog/typed-properties-in-php-74

    以上就是PHP 7.4中的类型属性(Typed Properties)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文原创发布php中文网,转载请注明出处,感谢您的尊重!如有疑问,请联系admin@php.cn处理
    专题推荐:PHP 7.4
    上一篇:PHP 7.4中的箭头函数(Arrow Functions) 下一篇:PHP 7.4中的预加载(Opcache Preloading)
    大前端线上培训班

    相关文章推荐

    • 在CentOS 8/RHEL 8系统中安装PHP 7.4的方法• 即将发布的PHP 7.4和PHP 8.0:说说你希望增加哪些功能?• PHP 7.4.0刚刚发布!一起看看有哪些新特性• PHP 7.4中的箭头函数(Arrow Functions)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网