概述
PHP 7.4为了增强类型新增加了类型化的类属性,并对PHP的类型系统进行了重大改进。当然这些变革都是可选功能,是完备向前对老版本兼容,不会毁坏以前的老代码。新的类型特性:
从PHP 7.4开始支持。
仅在类中可用,并且须要访问润色符:public,protected或private或var。

许可利用除了void和callable以外的所有类型。
其表现如下例:
Uninitialized
在上面的例子中,首先映入我们眼帘的是:
class Foo{public int $bar;}$foo = new Foo;
纵然创建Foo工具后$bar的值不是整数,PHP只会在访问$bar时抛出错误:
var_dump($foo->a);
Fatal error: Uncaught Error: Typed property Foo::$bar
must not be accessed before initialization
从缺点可以得出,有一种新的\"大众可变状态\公众:uninitialized。
如果$bar没有类型,则其值将为null。类型可以是空值,无法确定是否设置了类型化的可空属性,或者忘却设置了。以是新添加了\公众uninitialized\"大众这个新类型加以差异。
关于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 = [$this, 'method'];
假设你代码如下:
在上面的示例中,$callable引用私有的Bar::method方法,但是调用是在Foo的高下文中调用。由于浸染域不同,以是callable也不能在类型属性中利用。
这没什么大不了的,由于Closure是一个有效的类型,它将记住布局它的$ this高下文。
除此之外,所有可用类型的列表如下:
布尔型(bool),整型(int),浮点型(float),字符串(string),数组(array),iterable,工具,?(nullable),self 和parent,类和接口
逼迫和严格的类型
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';
上述语句以缺点类型赋值时候,在严格类型检讨(strict_types=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;}
说到继续,可能会创造很难找到任何好的用例来覆盖继续属性的类型。
值得把稳的是,可以变动继续属性的类型,但条件是访问润色符也从私有变动为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为例先容了PHP 7.4带来的新的变革和加强,更多的功能,可以参考PHP RFC。对付即将到来的7.4版本和后面可期的PHP 8 ,我们只有以活到老,学到老的态度才能跟上新技能的步伐。\"大众求求你别更新了,我快跟不上啦!\公众绝非一个IT人该当有的态度!