PHP 8:数组、变量、操作符、异常处理
本篇介绍几个新特性以及与数组、变量、运算符、异常处理和 trait 等有关的改进。
数组与字符串
弃用 false 值的 Autovivification
Autovivification 是指当引用数组中未定义的元素时自动创建新数组,例如:
<?php
$arr['a'][1] = 'a';
var_dump($arr);
新数组 $arr
是自动创建出来的,它在被引用之前并不存在。输出如下:
array(1) { ["a"]=> array(1) { [1]=> string(1) "a" } }
Autovivification 允许开发人员引用结构化变量(如数组)及其子元素,而无需首先显式地创建该结构化变量。
PHP 8.0 支持未定义变量、空值和 false
值的 Autovivification 。下面的脚本演示了null
值的 Autovivification:
<?php
$arr = null;
$arr[] = 1;
var_dump($arr);
输出如下:
array(1) { [0]=> int(1) }
下面的脚本演示了未定义变量的 Autovivification:
<?php
$arr[] = 'undefined value';
$arr['variableNotExist'][] = 1;
var_dump($arr);
上述脚本输出如下:
array(2) { [0]=> string(15) "undefined value" ["variableNotExist"]=> array(1) { [0]=> int(1) } }
PHP 8.0 甚至允许 false
的 Autovivification。不过,PHP 8.0 不支持标量值的 Autovivification,如下面的脚本所示:
<?php
$arr = 1;
$arr[] = 1;
var_dump($arr);
上述脚本输出如下:
Uncaught Error: 不能将标量值作为数组来使用在 PHP 8.1 中,Autovivification 只支持未定义的变量和
null
值,已放弃支持false
值的 Autovivification。想证明这一点,可以运行以下脚本:
<?php
$arr = false;
$arr[] = 1;
上述脚本输出如下:
Deprecated: 从 false 到数组的自动转换已弃用
字符串键数组解包
解包的使用语境是逐项列出数组元素,或使用解包运算符 ...
取出数组元素。PHP 8.0 不允许对数组中的字符串键进行解包,就像不允许对函数的参数进行解包一样。PHP 8.1 通过引入命名参数提供了函数参数解包支持;命名参数可以在参数解包之后使用,条件是命名参数不能覆盖已解包的参数。“PHP 8:函数和方法的新特性”一文中有一个演示函数命名参数解包的示例。
此外,PHP 8.1 还允许使用 …
运算符将字符串键解包到数组中,如下所示:
<?php
$array1 = ["one" => 1];
$array2 = ["two" => 2];
$array1 = ["one" => "one"];
$array2 = ["two" => "two"];
$array = ["one" => 0, "two" => 10, ...$array1, ...$array2];
var_dump($array);
用 …
运算符解包 $array1
和 array2
,前一个数组的键被后一个数组的键所覆盖。输出结果如下:
array(2) { ["one"]=> string(3) "one" ["two"]=> string(3) "two" }
array_merge()
函数用于在后台解包数组,因此,解包前面示例中的两个数组可以调用array_merge($array1,$array2)
。
新特性仅影响字符串键,而整数键将重新编号;原来的整数键不会保留。使用整数键的示例如下:
<?php
$array1 = [1 => 1];
$array2 = [2 => 2];
$array1 = [1 => "one"];
$array2 = [2 => "two"];
$array = [1 => 0, 2 => 10, ...$array1, ...$array2];
var_dump($array);
下面给出了解包字符串键数组的另一个示例,其中引号括起来的整数键实际上会被当作整数键。包含整数的字符串键会被强制转换为整数类型,例如:
<?php
$array1 = ["1" => 1];
$array2 = ["2" => 2];
$array1 = ["1" => "one"];
$array2 = ["2" => "two"];
$array = ["one" => 0, "two" => 10, ...$array1, ...$array2];
var_dump($array);
输出如下:
array(4) { ["one"]=> int(0) ["two"]=> int(10) [0]=> string(3) "one" [1]=> string(3) "two" }
确定数组是否是列表的新函数
数组类型支持字符串键和整数键。有时候,我们需要知道数组键的实际编号是否为 0…count($array)-1
。在这种情况下,我们将数组称为列表。新增函数 array_is_list(array $array): bool
就是为了完成这项工作。如果数组是一个列表,则该函数会返回一个 bool
值 true
,如果不是,则返回 false
。下面的例子演示了这个新函数:
<?php
$x = [1 => 'a', 0 => 'b', 2=>'c'];
$y = [0 => 'a', 1 => 'b', 2=>'c'];
var_export(array_is_list($x));
var_export(array_is_list($y));
输出如下:
false
true
数组排序变得稳定
在 PHP 7 中,数组的排序操作是不稳定的。“不稳定”意味着在连续排序中,不能保证“相等”的元素顺序一致。PHP 8.0 中排序变得稳定。如果输入数组中有多个元素相等,则它们在排序完成后总是邻接。换句话说,相等的元素会保持它们在原数组中的顺序。当按照复杂数据的特定属性进行排序时,这一特性特别有用。在这种情况下,如果排序不稳定,则可能会导致输出不一致。下面的脚本演示了稳定排序:
<?php
$array = [
'd' => 'c',
'c' => 'c',
'b' => 'a',
'a' => 'a',
];
asort($array);
foreach ($array as $key => $val) {
echo "array[" . $key . "] = " . $val . "\n";
}
在稳定排序中,结果总是:
array[b] = a array[a] = a array[d] = c array[c] = c
弃用 ${}
字符串插值
在双引号(")中的字符串里嵌入变量可以有多种形式。PHP 8.2 弃用了两种字符串插值形式:${var}
和 ${expr}
。${var}
形式与其他两种形式("$var")和("{$var}")语法上存在重叠,并且功能不如其他形式强大。${expr}
与(string) ${expr}
等价,很少使用。
下面的例子展示了允许的字符串插值形式,以及不允许的形式 ${var}
和 ${expr}
:
<?php
$str = 'hello';
var_dump("$str");
var_dump("{$str}");
var_dump("${str}");
var_dump("${str}");
var_dump("${(str)}");
上述脚本输出如下:
Deprecated: 字符串插值形式 {var} 已弃用,请使用 {var} 代替 in /home/deepakvohra/php-src/scripts/php-info.php on line 8Deprecated: 字符串插值形式 {var}已弃用,请使用{var} 代替 in /home/deepakvohra/php-src/scripts/php-info.php on line 10Deprecated: 字符串插值形式 {expr} (variable variables)已弃用 ,请使用 {{expr}} 代替 in /home/deepakvohra/php-src/scripts/php-info.php on line 12string(5) "hello" string(5) "hello" string(5) "hello" string(5) "hello"Fatal error: Uncaught Error: 未定义常量 "str"
异常处理
PHP 8.0 引入了非捕获catch
。以前,每个catch
语句都必须为被捕获的异常声明一个变量。例如,下面的脚本在 catch
语句中声明了 Exception
类型的变量 $exc
:
<?php
function sortArray(array $arrayToSort)
{
try {
if (count($arrayToSort) === 0) {
throw new Exception("Array is empty; please provide a non-empty array");
}
}
catch (Exception $exc) {
echo $exc;
}
}
$arrayToSort = array();
执行上述脚本会输出下面这条异常信息:
Exception: 数组为空,请提供一个非空数组虽然前面的示例使用了
$exc
变量,但也不是一定要使用异常变量。对于非捕获catch
,异常变量是可选的;不声明异常变量,因此也就不能使用,如下所示:
<?php
function sortArray(array $arrayToSort)
{
try {
if (count($arrayToSort) === 0) {
throw new Exception("Array is empty; please provide a non-empty array");
}
}
catch (Exception) {
}
}
多捕获 catch
语句也可以是非捕获 catch
,如下所示:
<?php
class Exception1 extends Exception
{
}
class Exception2 extends Exception
{
}
class A
{
public function fn1()
{
try {
throw new Exception1();
}
catch (Exception1 | Exception2) {
}
}
}
抛出异常
throw
语句以前不能在表达式中使用。PHP 8.0 增加了在表达式中使用 throw
的支持。例如,在以下脚本中,match
表达式在 default
中使用了 throw
表达式。
<?php
$vector = new \Ds\Vector();
$vector->push('a');
try {
match ('set') {
'push' => $vector->push('b'),
'pop' => $vector->pop(),
default => throw new Exception()
};
}
catch (Exception $exc) {
echo $exc;
}
print_r($vector);
输出如下:
Exception in C:\php-8.1.9-nts-Win32-vs16-x64\scripts\sample.php:11 Stack trace: #0 {main}Ds\Vector Object ( [0] => a )
下面的例子将 throw
与空值合并运算符(??
)一起使用:
<?php
$name = $_GET['name'] ?? throw new Exception("请提供请求参数'name'");
echo "Hello " . htmlspecialchars($name)."<br>";
输出如下:
Uncaught Exception: 请提供请求参数 'name' 在下面的例子中,throw 和三元运算符(
?
)一起使用:
<?php
try {
function fn1(bool $param1)
{
$value = $param1 ? true: throw new InvalidArgumentException();
}
fn1(true);
fn1(false);
}
catch (Exception $exc) {
echo $exc;
}
输出如下:
InvalidArgumentException in C:\php-8.1.9-nts-Win32-vs16-x64\scripts\sample.php:5 Stack trace: #0 C:\php-8.1.9-nts-Win32-vs16-x64\scripts\sample.php(9): fn1(false) #1 {main}
变量
整型字面量的显式八进制表示法
字面量八进制表示法可能产生误导性结果,例如 12 === 012
的计算结果为 false
。PHP 8.1 增加了对整型字面量显式八进制表示法 0o/0O
的支持,类似于十六进制的 0x/0x
表示法和二进制的 0b/0b
表示法。下面的脚本演示了显式八进制表示法:
<?php
var_export(0o12 === 10);
var_export(0o26 === 22);
var_export(0O12 === 10);
var_export(0O26 === 22);
var_export(012 === 0o12);
var_export(012 === 0O12);
输出如下:
true
true
true
true
true
true
限制 $GLOBALS 的使用
通过 $GLOBALS
变量可以直接访问内部符号表,新版本对它的使用增加了一些限制。从 PHP 8.1 开始,$GLOBALS
只能使用 $GLOBALS[$name] = $value
语法来修改。为了证明这一点,可以运行下面这个直接访问 $GLOBALS
的脚本:
<?php
$x=1;
$GLOBALS = 1;
$GLOBALS += 1;
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);
上述脚本会导致以下错误信息:
GLOBALS 只能使用 GLOBALS[name]=value 语法来修改下面这种使用
GLOBALS
的方法就没问题:
<?php
$GLOBALS['a'] = 1;
$GLOBALS['a']--;
var_dump($GLOBALS['a']);
输出如下:
int(0)
将命名空间名称视为单个标记
为了使保留关键字可以出现在命名空间名称中,PHP 8.0 将把命名空间名称视为单个标记。这降低了引入新的保留字时,因为现有命名空间名称已使用该保留字而导致向后不兼容的可能性。
为了说明这一点,下面的脚本在命名空间名称中使用了保留字 fn
。该脚本还使用了 ReflectionClass
来输出类的属性,比如它是否是一个命名空间、类名、命名空间和类方法:
<?php
namespace do\while\fn\iter;
function fn1()
{
}
class C
{
static function fn2()
{
}
}
$class = new \ReflectionClass('do\while\fn\iter\C');
var_dump($class->inNamespace());
var_dump($class->getName());
var_dump($class->getNamespaceName());
$methods = $class->getMethods();
var_dump($methods);
输出如下:
bool(true) string(18)
"do\while\fn\iter\C" string(16)
"do\while\fn\iter"
array(1) { [0]=> object(ReflectionMethod)#2 (2) { ["name"]=> string(3) "fn2" ["class"]=> string(18) "do\while\fn\iter\C" } }
运算符
PHP 8 增加了许多与新运算符相关的特性。
非严格字符串数字比较变得更加有用
在 PHP 8.0 之前,非严格字符串数字比较都是假设字符串实际上是一个数字,并将字符串转换为数字后进行数字比较。PHP 8.0 会在转换类型并进行数字比较之前确保字符串是一个数字。否则,它会将数字转换为字符串,并进行字符串比较。新特性不适用于严格比较运算符 ===
和 !==
。这些运算符要求两个操作数具有相同的类型,并且不执行隐式类型转换。受影响的只有非严格比较运算符 ==
、!=
、>
、>=
、<
、<=
和 ⇔
。下面的脚本演示了新的字符串数字比较:
<?php
var_dump(1 == "001");
var_dump(1 == "1.0");
var_dump(1.0 == "+1.0E0");
var_dump(1 == "1 ");
var_dump(1 == " 1");
var_dump(1 == " 1 ");
var_dump("one" == "1");
var_dump("one" != "1");
输出如下:
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(false)
bool(true)
空值安全运算符
你是不是经常调用方法或获取表达式结果的属性并假设结果非空?由于结果可能为空,所以最好首先确保它不是空的。你可以显式地使用 if(result!=null)
进行比较,但它可能涉及层次化的多重比较。下面的脚本使用传统的 if
比较,对整数值的加法进行空值安全的比较:
<?php
class Sum
{
public int $i;
function __construct()
{
$this->i = 0;
}
function addA(Sum $sum,int $a):?Sum
{
$sum->i= $a+$sum->i;
return $sum;
}
function addB(Sum $sum,int $b):?Sum
{
$sum->i= $b+$sum->i;
return $sum;
}
function addC(Sum $sum,int $c):?Sum
{
$sum->i= $c+$sum->i;
return $sum;
}
function getSum(Sum $sum):int
{
return $sum->i;
}
}
$a = new Sum();
if ($a->addA($a,1) !== null) {
if ($a->addB($a,2) !== null) {
if ($a->addC($a,3) !== null) {
echo $a->getSum($a);
}
}
}
上述脚本的结果是 6
。
新运算符 ?->
可用于链接调用以进行空值安全比较,当运算符的左操作数计算结果为 null
时,它将停止所有后续的比较。对于上述加法操作,以下脚本演示了链接操作数,使用新运算符 ?->
进行空值安全比较。
echo $a->addA($a,1)?->addB($a,2)?->addC($a,3)?->getSum($a);
与语言环境无关的浮点数字符串转换
在 PHP 8.0 之前,浮点数到字符串类型的转换依赖于语言环境,也就是说,小数分隔符会因语言环境而异。这可能会导致一些不一致,例如将字符串解释为格式错误,或将字符串解释为数值。开发人员非常需要一致的浮点数字符串表示,而 PHP 8.0 正好提供了这一点。下面的脚本演示了与本地语言环境无关的浮点数字符串转换。
<?php
setlocale(LC_ALL, "de_DE");
$f = 1.23;
echo (string) $f;
echo strval($f);
输出结果如下:
1.23
1.23
对算术/位运算符进行更严格的类型检查
算术/位运算符 +
、-
、*
、/
、**
、%
、<<
、>>
、&
、|
、^
、~
、++
、-
只能应用于支持这些运算符的操作数。这些运算符不能用于数组、资源或非重载对象操作数。PHP 8.0 会进行严格的类型检查,如果操作数与算术/位运算符不兼容,则抛出 TypeError
。为了演示这一点,下面的脚本对数组使用了减法运算符(-
):
<?php
$arrayToSort = array(3, 1, 0, 2);
var_dump($arrayToSort - [1]);
上述脚本会导致以下错误:
Uncaught TypeError: 不支持的操作数类型: array - array
在常量表达式中使用 ->
/?->
运算符获取枚举属性
枚举对象不允许出现在常量表达式中,如数组键,这样你就无法在常量表达式中获取枚举属性的名称和值。为了演示这一点,请使用 8.1 版本运行以下脚本:
<?php
enum Sort: string {
case ASC = 'ASC';
const SortType = [self::ASC->name => self::ASC->value];
}
上述脚本会导致如下错误信息:
常量表达式包含无效操作 PHP 8.2 允许在常量表达式中使用
-> ?->
运算符获取枚举属性,如下所示:
<?php
enum Sort: string {
case ASC = 'ASC';
const SortType = [self::ASC->name => self::ASC->value];
}
function get()
{
static $g = Sort::ASC->value;
}
#[Attr(Sort::ASC->name)]
class SortClass
{
}
function set($s = Sort::ASC->value,)
{
}
class SortClass2
{
public string $n = Sort::ASC->name;
}
// The rhs of -> allows other constant expressions
const DESC = 'DESC';
class SortClass3
{
const C = Sort::ASC->{DESC};
}
Trait
抽象 trait 方法验证
PHP 8.0 会在组合/使用类中验证抽象 trait 方法,以确保它们的签名匹配。实现方法必须与 trait 方法兼容,这里的兼容被定义为签名兼容:
参数数量兼容:函数参数的数量必须相同
逆变参数类型兼容
协变返回类型兼容另外,静态方法必须保持静态。抽象 trait 方法可以是私有的。下面的脚本演示了抽象 trait 方法的一个准确实现:
<?php
trait HelloTrait {
abstract private function hello(): string;
public function getMsgLength() {
return strlen($this->hello());
}
}
class A {
use HelloTrait;
private function hello(): string {return "Hello John"; }
}
为了演示不兼容的情况,将实现方法修改如下:
private function hello(): stdClass { }
在这种情况下,会报以下错误信息:
A::hello(): stdClass的声明必须与HelloTrait::hello(): string兼容
不能在类中将 trait 中的非静态方法变成静态。为了演示这一点,修改实现如下:
private static function hello(): string { }
上述脚本会报以下错误信息:
无法在类A中将非静态方法HelloTrait::hello()变成静态
弃用在 Trait 上调用静态元素的特性
PHP 8.1.0 放弃在 trait 上调用静态元素的支持,也就是说,不能直接在 trait 上调用静态方法或静态属性。只能通过使用 trait 的类访问 trait 的静态方法和属性。下面的脚本演示了这种情况:
<?php
trait HelloTrait {
public static $a = 'static property in trait';
public static function hello(): string {return "Hello";}
}
class A {
use HelloTrait;
}
echo A::$a;
echo A::hello();
echo HelloTrait::$a;
echo HelloTrait::hello();
输出如下:
Deprecated: 不能直接访问静态 trait 属性
HelloTrait::$a
,只能通过使用该 trait 的类访问 Deprecated: 不能直接访问静态 trait 方法HelloTrait::hello
,只能通过使用该 trait 的类访问
Trait 中的常量
PHP 8.1 不允许在 trait 中使用不变量(也称为常量)。PHP 8.2 增加了对在 trait 中使用常量的支持。这些常量可以由 trait 的方法使用,也可以在组合类中使用。为了演示常量在 trait 中的用处,请看下面的示例。其中,组合类中声明了一个名为 MAX_ARRAY_SIZE
的常量:
<?php
trait SortTrait
{
public function sortArray(array $arrayToSort): void
{
if (count($arrayToSort) > self::MAX_ARRAY_SIZE) {
throw new \Exception("array size out of range");
} else {
sort($arrayToSort);
foreach ($arrayToSort as $key => $val) {
echo "$key = $val ";
}
echo "<br/>";
}
}
}
class SortClass
{
private const MAX_ARRAY_SIZE = 10;
use SortTrait;
}
$arrayToSort = ["B", "A", "f", "C", 1, "a", "F", "B", "b", "d"];
$obj = new SortClass();
$obj->sortArray($arrayToSort);
运行上述脚本会生成以下输出:
0 = 1 1 = A 2 = B 3 = B 4 = C 5 = F 6 = a 7 = b 8 = d 9 = f
下面是同一脚本的 8.2 版本,在 trait 中声明了常量 MAX_ARRAY_SIZE
:
<?php
trait SortTrait
{
public const MAX_ARRAY_SIZE = 10;
public function sortArray(array $arrayToSort): void
{
if (count($arrayToSort) > self::MAX_ARRAY_SIZE) {
throw new \Exception("array size out of range");
} else {
sort($arrayToSort);
foreach ($arrayToSort as $key => $val) {
echo "$key = $val ";
}
echo "<br/>";
}
}
}
class SortClass
{
use SortTrait;
}
$arrayToSort = ["B", "A", "f", "C", 1, "a", "F", "B", "b", "d"];
$obj = new SortClass();
$obj->sortArray($arrayToSort);
输出相同:
0 = 1 1 = A 2 = B 3 = B 4 = C 5 = F 6 = a 7 = b 8 = d 9 = f
再看一个例子。下面的脚本在 trait 中声明了 3 个常量,并在 trait 中使用了它们:
<?php
trait SortTrait
{
public const SORT_TYPE_1 = "ASC";
public const SORT_TYPE_2 = "DESC";
public const SORT_TYPE_3 = "SHUFFLE";
public function getSortType(string $sortType): void
{
if (str_contains($sortType, self::SORT_TYPE_1)) {
echo "Sort type is ASC";
}
if (str_contains($sortType, self::SORT_TYPE_2)) {
echo "Sort type is DESC";
}
if (str_contains($sortType, self::SORT_TYPE_3)) {
echo "Sort type is SHUFFLE";
}
}
}
class SortClass
{
use SortTrait;
}
$obj = new SortClass();
$obj->getSortType("ASCending");
输出如下:
Sort type is ASC
Trait 常量无法通过TRAIT_NAME::CONSTANT
语法直接访问,就像下面的脚本这样:
<?php
trait SortTrait
{
public const SORT_TYPE_1 = "ASC";
public const SORT_TYPE_2 = "DESC";
public const SORT_TYPE_3 = "SHUFFLE";
public function getSortType(string $sortType): void
{
if (str_contains($sortType, SortTrait::SORT_TYPE_1)) {
echo "Sort type is ASC";
}
if (str_contains($sortType, self::SORT_TYPE_2)) {
echo "Sort type is DESC";
}
if (str_contains($sortType, self::SORT_TYPE_3)) {
echo "Sort type is SHUFFLE";
}
}
}
class SortClass
{
use SortTrait;
}
$obj = new SortClass();
$obj->getSortType("ASCending");
执行上述脚本会输出以下错误信息:
Uncaught Error: 不能直接访问 trait 常量 SortTrait::SORT_TYPE_1 使用
$this
就可以,如下所示:
if (str_contains($sortType, $this::SORT_TYPE_1)) {
echo 'Sort type is ASC';
}
Trait 常量可以声明为 final 类常量。适用于 trait 属性的兼容性限制也适用于它的常量。
枚举可以使用包含常量的 trait,和直接在枚举中定义它们一样,如下所示:
<?php
trait SortTrait
{
private const SortType = "ASC";
}
enum Enum1: int
{
use SortTrait;
case CaseA = self::SortType;
}
逐步淘汰 Serializable
PHP 7.4 引入了自定义序列化机制,借助两个新的魔法方法:__serialize(): array
和__unserialize(array $data): void
。__serialize()
方法返回一个包含对象所有必要状态的数组,__unserialize()
方法从给定的数据数组中恢复对象状态。新的自定义序列化机制旨在逐步淘汰Serializable
接口。如果一个非抽象类实现了Serializable
,但没有实现__serialize()
和__unserialize()
, PHP 8.1 就会生成一条弃用警告。这样的一个类被称为“only Serializable”。为了演示这一点,可以运行下面的脚本:
<?php
class A implements Serializable {}
执行上述脚本会显示以下弃用信息:
Deprecated: A 实现了 Serializable 接口,该接口已弃用。如果需要支持旧的 PHP 版本,请实现__serialize()和__unserialize()Fatal error: 类 A 包含 2 个抽象方法,因此必须声明为抽象的,或者实现其余的方法 (Serializable::serialize, Serializable::unserialize)
弃用动态属性
动态类属性是在声明之前被引用的属性。动态属性是自动创建的。PHP 8.2 已弃用动态属性。这主要是为了避免这样一种情况:用户无意创建新属性,但却因为输入了错误的属性名称而创建了新属性。为了演示这一点,在 PHP 8.2 中运行下面的脚本创建一个动态属性:
<?php
class A
{
public $name;
}
$a = new A();
// 给已声明的属性User::$name赋值
$a->name = "John";
$a->firstname = "John";
执行上述脚本会输出以下弃用信息:
Deprecated: 已弃用创建动态属性 A::$firstname
如果你仍然希望动态属性实现魔术方法__get/__set
,或使用新属性 #[AllowDynamicProperties]
,则预打包类 stdClass
已经用 #[AllowDynamicProperties]
属性标记。
弃用向内置函数的非可空参数传递 null 值的特性
当强类型模式设置为 (strict_types=1)
时,用户定义函数不接受向非空参数传递 null
值。在 PHP 8.1 中,即使是内置函数也不会接受向非空参数传递 null
值,如下所示,它会生成弃用通知:
<?php
$var=null;
strlen($var);
输出如下:
Deprecated: strlen(): 向 string 类型的参数 #1 ($string)传递 null 的特性已弃用在 PHP 9.0 中,
TypeError
弃用通知将被替换为错误。
在本文中,我们讨论了 PHP 8 中与数组、变量、运算符和异常处理相关的新特性。我们还讨论了一些与 trait、类和函数相关的特性。