首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

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

本文属于专题文章《深入浅出 PHP 8》。 根据w3tech的数据,PHP 仍是互联网上使用最为广泛的脚本语言之一,77.3%的网站在服务器端均使用该编程语言。PHP 8 为我们带来了许多新功能与优化,具体将在本系列文章中分析。

本篇介绍几个新特性以及与数组、变量、运算符、异常处理和 trait 等有关的改进。

数组与字符串

弃用 false 值的 Autovivification

Autovivification是指当引用数组中未定义的元素时自动创建新数组,例如:

代码语言:javascript
复制
<?php
$arr['a'][1] = 'a';
var_dump($arr);

复制代码

新数组$arr是自动创建出来的,它在被引用之前并不存在。输出如下:

代码语言:javascript
复制
array(1) { ["a"]=> array(1) { [1]=> string(1) "a" } }

Autovivification 允许开发人员引用结构化变量(如数组)及其子元素,而无需首先显式地创建该结构化变量。

PHP 8.0 支持未定义变量、空值和false值的 Autovivification 。下面的脚本演示了null值的 Autovivification:

代码语言:javascript
复制
<?php
$arr   = null;
$arr[] = 1;
var_dump($arr);

输出如下:

代码语言:javascript
复制
array(1) { [0]=> int(1) }

下面的脚本演示了未定义变量的 Autovivification:

代码语言:javascript
复制
<?php
$arr[]                     = 'undefined value';
$arr['variableNotExist'][] = 1;

var_dump($arr);

上述脚本输出如下:

代码语言:javascript
复制
array(2) { [0]=> string(15) "undefined value" ["variableNotExist"]=> array(1) { [0]=> int(1) } }

PHP 8.0 甚至允许false的 Autovivification。不过,PHP 8.0 不支持标量值的 Autovivification,如下面的脚本所示:

代码语言:javascript
复制
<?php
$arr   = 1;
$arr[] = 1;
var_dump($arr);

上述脚本输出如下:

Uncaught Error: 不能将标量值作为数组来使用在 PHP 8.1 中,Autovivification 只支持未定义的变量和null 值,已放弃支持false值的 Autovivification。想证明这一点,可以运行以下脚本:

代码语言:javascript
复制
<?php
 
$arr = false;
$arr[] = 1;

上述脚本输出如下:

Deprecated: 从 false 到数组的自动转换已弃用

字符串键数组解包

解包的使用语境是逐项列出数组元素,或使用解包运算符...取出数组元素。PHP 8.0 不允许对数组中的字符串键进行解包,就像不允许对函数的参数进行解包一样。PHP 8.1 通过引入命名参数提供了函数参数解包支持;命名参数可以在参数解包之后使用,条件是命名参数不能覆盖已解包的参数。“PHP 8:函数和方法的新特性”一文中有一个演示函数命名参数解包的示例。

此外,PHP 8.1 还允许使用运算符将字符串键解包到数组中,如下所示:

代码语言:javascript
复制
<?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)

新特性仅影响字符串键,而整数键将重新编号;原来的整数键不会保留。使用整数键的示例如下:

代码语言:javascript
复制
<?php

$array1 = [1 => 1];
$array2 = [2 => 2];
$array1 = [1 => "one"];
$array2 = [2 => "two"];
$array = [1 => 0, 2 => 10, ...$array1, ...$array2];
var_dump($array); 

下面给出了解包字符串键数组的另一个示例,其中引号括起来的整数键实际上会被当作整数键。包含整数的字符串键会被强制转换为整数类型,例如:

代码语言:javascript
复制
<?php  

$array1 = ["1" => 1];
$array2 = ["2" => 2];
$array1 = ["1" => "one"];
$array2 = ["2" => "two"];
$array = ["one" => 0, "two" => 10, ...$array1, ...$array2];
var_dump($array);

输出如下:

代码语言:javascript
复制
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。下面的例子演示了这个新函数:

代码语言:javascript
复制
<?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 中排序变得稳定。如果输入数组中有多个元素相等,则它们在排序完成后总是邻接。换句话说,相等的元素会保持它们在原数组中的顺序。当按照复杂数据的特定属性进行排序时,这一特性特别有用。在这种情况下,如果排序不稳定,则可能会导致输出不一致。下面的脚本演示了稳定排序:

代码语言:javascript
复制
<?php
 
$array = [
    'd' => 'c',
    'c' => 'c',
    'b' => 'a',
    'a' => 'a',
]; 
asort($array);

foreach ($array as $key => $val) {
                echo "array[" . $key . "] = " . $val . "\n";
             }  

在稳定排序中,结果总是:

代码语言:javascript
复制
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}

代码语言:javascript
复制
<?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

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

代码语言:javascript
复制
<?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,如下所示:

代码语言:javascript
复制
<?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表达式。

代码语言:javascript
复制
<?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); 

输出如下:

代码语言:javascript
复制
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与空值合并运算符(??)一起使用:

代码语言:javascript
复制
<?php
 
$name = $_GET['name'] ?? throw new Exception("请提供请求参数'name'");

echo "Hello " . htmlspecialchars($name)."<br>";

输出如下:

Uncaught Exception: 请提供请求参数'name'在下面的例子中,throw 和三元运算符(?)一起使用:

代码语言:javascript
复制
<?php

try {
    function fn1(bool $param1)
    {
     $value = $param1 ? true: throw new InvalidArgumentException();
    }
    
    fn1(true);
    fn1(false);
}
catch (Exception $exc) {
    echo $exc;
} 

输出如下:

代码语言:javascript
复制
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表示法。下面的脚本演示了显式八进制表示法:

代码语言:javascript
复制
<?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);

输出如下:

代码语言:javascript
复制
true
true
true
true
true
true

限制 $GLOBALS 的使用

通过$GLOBALS变量可以直接访问内部符号表,新版本对它的使用增加了一些限制。从 PHP 8.1 开始,$GLOBALS只能使用$GLOBALS[$name] = $value语法来修改。为了证明这一点,可以运行下面这个直接访问$GLOBALS的脚本:

代码语言:javascript
复制
<?php
$x=1;  
$GLOBALS = 1;
$GLOBALS += 1;
$GLOBALS =& $x;
$x =& $GLOBALS;
unset($GLOBALS);

上述脚本会导致以下错误信息:

GLOBALS只能使用GLOBALS[name]=value 语法来修改下面这种使用GLOBALS 的方法就没问题:

代码语言:javascript
复制
<?php
$GLOBALS['a'] = 1;
$GLOBALS['a']--;
var_dump($GLOBALS['a']);

输出如下:

代码语言:javascript
复制
int(0)

将命名空间名称视为单个标记

为了使保留关键字可以出现在命名空间名称中,PHP 8.0 将把命名空间名称视为单个标记。这降低了引入新的保留字时,因为现有命名空间名称已使用该保留字而导致向后不兼容的可能性。

为了说明这一点,下面的脚本在命名空间名称中使用了保留字fn。该脚本还使用了ReflectionClass来输出类的属性,比如它是否是一个命名空间、类名、命名空间和类方法:

代码语言:javascript
复制
<?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);

输出如下:

代码语言:javascript
复制
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 会在转换类型并进行数字比较之前确保字符串是一个数字。否则,它会将数字转换为字符串,并进行字符串比较。新特性不适用于严格比较运算符===!==。这些运算符要求两个操作数具有相同的类型,并且不执行隐式类型转换。受影响的只有非严格比较运算符==!=>>=<<=。下面的脚本演示了新的字符串数字比较:

代码语言:javascript
复制
<?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比较,对整数值的加法进行空值安全的比较:

代码语言:javascript
复制
<?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时,它将停止所有后续的比较。对于上述加法操作,以下脚本演示了链接操作数,使用新运算符?-> 进行空值安全比较。

代码语言:javascript
复制
echo $a->addA($a,1)?->addB($a,2)?->addC($a,3)?->getSum($a);

与语言环境无关的浮点数字符串转换

在 PHP 8.0 之前,浮点数到字符串类型的转换依赖于语言环境,也就是说,小数分隔符会因语言环境而异。这可能会导致一些不一致,例如将字符串解释为格式错误,或将字符串解释为数值。开发人员非常需要一致的浮点数字符串表示,而 PHP 8.0 正好提供了这一点。下面的脚本演示了与本地语言环境无关的浮点数字符串转换。

代码语言:javascript
复制
<?php
 
setlocale(LC_ALL, "de_DE");
$f = 1.23;
 
echo (string) $f;      
echo strval($f);

输出结果如下:

代码语言:javascript
复制
1.23
1.23

对算术/位运算符进行更严格的类型检查

算术/位运算符+-*/**%<<>>&|^~++-只能应用于支持这些运算符的操作数。这些运算符不能用于数组、资源或非重载对象操作数。PHP 8.0 会进行严格的类型检查,如果操作数与算术/位运算符不兼容,则抛出TypeError。为了演示这一点,下面的脚本对数组使用了减法运算符(-):

代码语言:javascript
复制
<?php
 
 $arrayToSort = array(3, 1, 0, 2);
 var_dump($arrayToSort - [1]);

上述脚本会导致以下错误:

代码语言:javascript
复制
Uncaught TypeError: 不支持的操作数类型: array - array 

在常量表达式中使用->/?- >运算符获取枚举属性

枚举对象不允许出现在常量表达式中,如数组键,这样你就无法在常量表达式中获取枚举属性的名称和值。为了演示这一点,请使用 8.1 版本运行以下脚本:

代码语言:javascript
复制
<?php

 
enum Sort: string {
    case ASC = 'ASC';
    
    const SortType = [self::ASC->name => self::ASC->value];
}

上述脚本会导致如下错误信息:

常量表达式包含无效操作 PHP 8.2 允许在常量表达式中使用-> ?->运算符获取枚举属性,如下所示:

代码语言:javascript
复制
<?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 方法的一个准确实现:
代码语言:javascript
复制
<?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"; }
}

为了演示不兼容的情况,将实现方法修改如下:

代码语言:javascript
复制
private function hello(): stdClass { }

在这种情况下,会报以下错误信息:

代码语言:javascript
复制
A::hello(): stdClass的声明必须与HelloTrait::hello(): string兼容

不能在类中将 trait 中的非静态方法变成静态。为了演示这一点,修改实现如下:

代码语言:javascript
复制
private static function hello(): string { }

上述脚本会报以下错误信息:

代码语言:javascript
复制
无法在类A中将非静态方法HelloTrait::hello()变成静态

弃用在 Trait 上调用静态元素的特性

PHP 8.1.0 放弃在 trait 上调用静态元素的支持,也就是说,不能直接在 trait 上调用静态方法或静态属性。只能通过使用 trait 的类访问 trait 的静态方法和属性。下面的脚本演示了这种情况:

代码语言:javascript
复制
<?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的常量:

代码语言:javascript
复制
<?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

代码语言:javascript
复制
<?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 中使用了它们:

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

代码语言:javascript
复制
<?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就可以,如下所示:

代码语言:javascript
复制
if (str_contains($sortType, $this::SORT_TYPE_1)) {
            echo 'Sort type is ASC';
        }

Trait 常量可以声明为 final 类常量。适用于 trait 属性的兼容性限制也适用于它的常量。

枚举可以使用包含常量的 trait,和直接在枚举中定义它们一样,如下所示:

代码语言:javascript
复制
<?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”。为了演示这一点,可以运行下面的脚本:

代码语言:javascript
复制
<?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 中运行下面的脚本创建一个动态属性:

代码语言:javascript
复制
<?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值,如下所示,它会生成弃用通知:

代码语言:javascript
复制
<?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/

  • 发表于:
  • 本文为 InfoQ 中文站特供稿件
  • 首发地址https://www.infoq.cn/article/cctqEbwE08k1OuZJYvtV
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券