PHP 8:数组、变量、操作符、异常处理

  • 2023-06-11
    北京
  • 本文字数:10145 字

    阅读完需:约 33 分钟

本文属于专题文章《深入浅出 PHP 8》

根据w3tech的数据,PHP 仍是互联网上使用最为广泛的脚本语言之一,77.3%的网站在服务器端均使用该编程语言。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);
复制代码

运算符解包$array1array2,前一个数组的键被后一个数组的键所覆盖。输出结果如下:

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就是为了完成这项工作。如果数组是一个列表,则该函数会返回一个booltrue,如果不是,则返回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));
复制代码

输出如下:

falsetrue

数组排序变得稳定

在 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

<?phpfunction 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,异常变量是可选的;不声明异常变量,因此也就不能使用,如下所示:

<?phpfunction sortArray(array $arrayToSort){        try {        if (count($arrayToSort) === 0) {            throw new Exception("Array is empty; please provide a non-empty array");        }            }    catch (Exception) {    }}
复制代码

多捕获catch语句也可以是非捕获catch,如下所示:

<?phpclass 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 和三元运算符(?)一起使用:

<?phptry {    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);
复制代码

输出如下:

truetruetruetruetruetrue
复制代码

限制 $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来输出类的属性,比如它是否是一个命名空间、类名、命名空间和类方法:

<?phpnamespace  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.231.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 允许在常量表达式中使用-> ?->运算符获取枚举属性,如下所示:

<?phpenum 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 expressionsconst 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的常量:

<?phptrait 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

<?phptrait 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 中使用了它们:

<?phptrait 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 ASCTrait 常量无法通过TRAIT_NAME::CONSTANT 语法直接访问,就像下面的脚本这样:

<?phptrait 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,和直接在枚举中定义它们一样,如下所示:

<?phptrait 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 中运行下面的脚本创建一个动态属性:

<?phpclass 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、类和函数相关的特性。

原文链接:

https://www.infoq.com/articles/php8-arrays-variables-operators/

相关阅读:

PHP 8:注解、match 表达式及其他改进

PHP 8:类和枚举

PHP 8:函数和方法