class Point {
public function __construct(
public int $x = 0,
public int $y = 0,
public int $z = 0,
) {}
}
对比可以看出,新的属性语法更易读且不易出错。
抽象Trait方法验证
Trait是一种在单一继承语言中代码重用的机制。通常,它们用于声明可在多个类中使用的方法。
特征也可以包含抽象方法。这些方法只是声明方法的签名,但是方法的实现必须使用trait在类中完成。
trait T {
abstract public function test(int $x);
}
class C {
use T;
public function test(string $x) {}
}
如果实现方法与抽象特征方法不兼容,将会抛出致命错误:
Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path/to/your/test.php on line 10
不兼容的方法签名
在PHP中,由于方法签名不兼容而导致的继承错误会引发致命错误或警告,具体取决于导致错误的原因。
如果类正在实现接口,则不兼容的方法签名将引发致命错误。根据对象接口文档:
实现接口的类必须使用与LSP(Liskov替换原理)兼容的方法签名。不这样做将导致致命错误。下面是一个带接口的继承错误的示例:
interface I {
public function method(array $a);
}
class C implements I {
public function method(int $a) {}
}
在PHP 7.4中,上面的代码将引发以下错误:
Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a) in /path/to/your/test.php on line 7
子类中具有不兼容签名的函数将引发警告。
class C1 {
public function method(array $a) {}
}
class C2 extends C1 {
public function method(int $a) {}
}
在PHP 7.4中,上面的代码只会发出警告:
Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a) in /path/to/your/test.php on line 7
在PHP 8,都会抛出致命错误。
Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a) in /path/to/your/test.php on line 7
数组索引支持负数
联合类型接受可以是不同类型的值。目前,PHP不支持联合类型,但?Type语法和特殊iterable类型除外。
在PHP 8之前,联合类型只能在phpdoc批注中指定:
class Number {
/**
* @var int|float $number
*/
private $number;
/**
* @param int|float $number
*/
public function setNumber($number) {
$this->number = $number;
}
/**
* @return int|float
*/
public function getNumber() {
return $this->number;
}
}
根据联合类型2.0 RFC提议在函数签名中添加对联合类型的支持,这样我们就不再依赖内联文档,而是使用T1|T2|...语法来定义联合类型:
class Number {
private int|float $number;
public function setNumber(int|float $number): void {
$this->number = $number;
}
public function getNumber(): int|float {
return $this->number;
}
}
联合类型支持所有可用类型,但有一些限制:
void类型不能是并集的一部分,因为void意味着函数不返回任何值。
null类型仅支持union的类型,但它的使用作为一个独立的类型是不允许的。
(?T)也可以使用可为null的类型表示法,表示T|null,但不允许在联合类型中包含该表示法(不允许使用?T1|T2,应该用T1|T2|null改用)。
尽可能多的功能(即strpos(),strstr(),substr()等等)包括false可能的返回类型中,false伪类型也支持。
内部函数的一致类型错误
传递非法类型的参数时,内部函数和用户定义函数的行为会有所不同。
用户定义的函数会引发TypeError,但是内部函数会根据多种情况以多种方式运行。无论如何,典型的行为是发出警告并返回null。比如下面的代码在PHP 7.4会导致告警
var_dump(strlen(new stdClass));
Warning: strlen() expects parameter 1 to be string, object given in /path/to/your/test.php on line 4
NULL
如果启用strict_types,或参数信息指定类型,则行为将有所不同。在这种情况下,将检测到类型错误并导致TypeError。
为了消除这些不一致之处,RFC建议使内部参数解析API在参数类型不匹配的情况下抛出ThrowError。
在PHP 8中,上面的代码将抛出致命错误:
Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, object given in /path/to/your/test.php:4
Stack trace:
#0 {main}
thrown in /path/to/your/test.php on line 4 抛出表表达式
在PHP中,throw是一个语句,因此无法在只允许使用表达式的地方使用它。
建议将thro语句转换为表达式,这样就可以在允许表达式的任何上下文中使用它。例如,箭头符号,空合并运算符,三元判断和合并运算符等:
$callable = fn() => throw new Exception();
// $value is non-nullable.
$value = $nullableValue ?? throw new InvalidArgumentException();
// $value is truthy.
$value = $falsableValue ?: throw new InvalidArgumentException();
WeakMap
if ($pos !== false) {
echo "The string has been found";
} else {
echo "String not found";
}
在上面的示例中,使用了!==比较运算符,该运算符还检查两个值是否属于同一类型。如果针的位置为0,这可以防止我们出错:
"此函数可能返回布尔FALSE,但也可能返回非布尔值,其值为FALSE。[…]使用===运算符测试此函数的返回值。"
此外,一些框架提供了帮助程序功能来搜索给定字符串内的值(比如Laravel)。
RFC建议引入一个新功能,该功能允许在字符串内部进行搜索:str_contains。
str_contains ( string $haystack , string $needle ) : bool
它的用法非常简单。str_contains检查是否$needle在中找到$haystack并返回true或false相应地返回。使用str_contains可以使用如下语法:
$mystring = 'Managed WordPress Hosting';
$findme = 'WordPress';
if (str_contains($mystring, $findme)) {
echo "The string has been found";
} else {
echo "String not found";
}
这更易读,更不容易出错。目前str_contains它区分大小写,但是将来可能会改变。
str_starts_with()和str_ends_with()