PHP 7.4 类型属性详解
发布时间:2022-09-27 15:40:14 所属栏目:PHP教程 来源:
导读: PHP 7.4 中增加了类型化类属性,对 php 的类型系统进行了重大改进。这些更改完全是自愿加入的,不会破坏以前的版本。
在本文中,我们将深入了解该功能,但首先让我们总结一下最重要的几点:
这些
在本文中,我们将深入了解该功能,但首先让我们总结一下最重要的几点:
这些
|
PHP 7.4 中增加了类型化类属性,对 php 的类型系统进行了重大改进。这些更改完全是自愿加入的,不会破坏以前的版本。 在本文中,我们将深入了解该功能,但首先让我们总结一下最重要的几点: 这些更改自 PHP 7.4 起可用,于去年发布 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值仍不是整数值的情况下,PHP 也只是会在访问$bar时才会报错: var_dump($foo->bar); Fatal error: Uncaught Error: Typed property Foo::$bar must not be accessed before initialization 从错误消息中可以看出,出现了一种新的变量状态:未初始化 (uninitialized) $bar 属性无论是否声明了类型,值都可以为 null。因此,无法确定类型属性是否设置。这就是增加变量「未初始化」状态的原因。 未初始化有四个方面需要注意: 特别要注意在对象实例化之后设置未初始化的类型属性是合法的: class Foo { public int $a; } $foo = new Foo; $foo->a = 1; // 合法 $foo->a = null; // 非法 虽然只会在读取属性值时检查未初始化状态,但在写入属性时会进行类型验证。这意味着任何无效的属性值都不会被设置。 默认值和构造函数 让我们仔细看看如何初始化类型属性值。对于标量类型,可以直接提供一个默认值 class Foo { public int $bar = 4; public ?string $baz = null; // 错误写法 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; } } 但也要记住我之前提到的内容:在构造函数之外写入未初始化 (uninitialized) 的属性是有效的。只要没有读取属性值的操作,编译器就不会执行未初始化的相关检查。 类型 那么到底哪些类型可以指定,又如何指定呢?我已经提到了指定属性类型只能在类中进行 (当前如此),并且它们需要一个访问修饰符或是属性前面的 var 关键字。 对于可用类型,几乎所有类型都可以使用,除了 void 和 callable 类型. 因为 void 意味着没有值,所以它不能用于指定一个值的类型也就说得过去了。然而 callback 就有一点细微不同了。 可见,PHP 中的 "callback" 可以这样写 $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)(); 在此例中,$callback 引用了私有的 Bar::method,但是是在 Foo 的上下文中被调用的。基于这个问题,决定不添加 callback 类型的支持。 不过这并不是什么大问题,因为 Closure(闭包) 是一种有效类型,它会记住构建它的 $this 上下文。 顺带一说,以下是所有可用类型的列表: 强制和严格类型 PHP,是我们既喜欢又反感的动态语言PHP数据类型,它会尽可能地强制或转换类型。假设你在一个期望接受 int 的地方传入字符串,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) 还有更多! 正如开头所说,类型化属性是 PHP 的 主要 补充。关于它们更多的内容,我建议您通读 RFC 以了解所有细节。 以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些PHP高级、架构视频资料和大厂PHP面试PDF免费获取,可以关注公众号:PHP开源社区 (编辑:开发网_运城站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


浙公网安备 33038102330464号