本节书摘来自华章出版社《Hack与HHVM权威指南》一书中的第1章,第1节,作者 Owen Yamauchi,更多章节内容可以访问云栖社区“华章计算机”公众号查看。
1.7.1 提炼nullable类型到non-nullable类型
null检查语句在从空值(nullable)的类型到非空值(non-nullable)类型的转变中经常用到。下面是个通过了类型检查器检查的示例。
function takes_string(string $str) {
// ...
}
function takes_nullable_string(?string $str) {
if ($str !== null) {
takes_string($str);
}
// ...
}
在if语句段内部,类型检查器知道$str是个非空值,所以它能够传递给函takes_string() 。注意null检查应该使用恒等操作符===和!==,而不是普通的等于操作符 (==和!=),或者转化为一个boolean类型。如果你不用恒等符号,类型检查器会报告一个错误注6。内置的函数is_null()也是可以工作的。下面是三元表达式的例子:
function takes_nullable_string(?string $str) {
takes_string($str === null ? "(null)" : $str);
// ...
}
当然你还可以使用如下的样式,即把一个分支流直接切断:
function processInfo(?string $info) {
if ($info === null) {
return;
}
takes_string($info);
}
类型检查器知道,对函数takes_string()的调用仅仅当$info变量不为空时才会被执行。因为如果它为空,if代码段将会进入,然后函数会直接返回。(如果return语句改成throw语句,效果是一样的。)
如下是个更大的例子,展示了一个更加复杂敏感的流程控制:
function fetch_from_cache(): ?string {
// ...
}
function do_expensive_computation(): string {
// ...
}
function get_data(): string {
$result = fetch_from_cache();
if ($result === null) {
$result = do_expensive_computation();
}
return $result;
}
在return语句这里,类型检查器知道$result是个非空的值,所以返回类型标注就被满足了。如果if代码段进入,那么一个非空的字符串将会分配给$result。如果if代码段没有进入,那么$result必须已经是非空的字符串。
最后,Hack有个特殊内置的函数叫做invariant(),你可以使用这个函数来向类型检查器陈述事实。这个函数使用两个参数,一个是boolean的表达式,另一个是字符串类型,主要用来向读者描述问题所在 :
function processInfo(?string $info) {
invariant($info !== null, "I know it's never null somehow");
takes_string($info);
}
在运行环境中,如果invariant()的第一个参数值被证明是false,那么一个不变异常(InvariantException)将会触发。类型检查器知道这并且推断出:在invariant()函数后面的调用中,$info不会是个空值。否则的话,一个异常早就被抛出了,而代码根本就不会执行到这个地方。