PHP8新特性解读

PHP技术
479
2
2
2023-02-11
标签   PHP8

PHP8.0已经在2020年11月26号发布,到现在最新版,已经是PHP8.2.2,本站leyeah.com已经升级至最新版,PHP8的升级势在必行,可能很多网站,依旧还是在使用PHP7,可以考虑找个时间升级一下,毕竟新版发布都2年有余,性能方便提升不少,也增加了一些新功能,阿里云的Alibaba Cloud Linux 3或Centos的用户可以参考下这篇文章。

Alibaba Cloud Linux 3或centos如何升级到PHP8以上

PHP8提供了哪些新特性呢,可以参照下PHP8的官方release note,上面有对功能做了详细的说明。

1. 命名参数

在一个函数参数多达5,6个时,并且需要传最后一个参数时,前面本来可以默认的也必须传过去,有了命名参数后,就会方便很多,举个例子来说明下。

function test($a, $b = 1, $c = 2, $d = 3, $e = 4, $f = 5)
{
  echo $a + $b + $c + $d + $e + $f;
}

//PHP7
test(0, 1, 2, 3, 4, 10); //输出 20
//PHP8
test(0, f: 10); //输出 20

对比一下,命名参数前面不能带上$符号,这个要注意一下,看起来是不是方便很多,可读性也强一些。

2. 注解的变化,就引用下官方的例子,看起来方式变得简便一些。

//PHP 7
class PostsController
{
    /**
     * @Route("/api/posts/{id}", methods={"GET"})
     */
    public function get($id) { /* ... */ }
}

//PHP 8
class PostsController
{
    #[Route("/api/posts/{id}", methods: ["GET"])]
    public function get($id) { /* ... */ }
}

3. 构造器属性提升

//PHP 7
class Point {
  public float $x;
  public float $y;
  public float $z;
  public function __construct(
    float $x = 0.0,
    float $y = 0.0,
    float $z = 0.0
  ) {
    $this->x = $x;
    $this->y = $y;
    $this->z = $z;
  }
}

//PHP8
class Point {
  public function __construct(
    public float $x = 0.0,
    public float $y = 0.0,
    public float $z = 0.0,
  ) {}
}

可以看出,写法上简便很多。

4. 增加了联合类型

//PHP8
function test(int $a, int|string $b): mixed
{
    if (is_numeric($b)) {
        return $a + $b;
    } else {
       return $a . ' ' . $b;
    }
}

echo test(2, 6); //输出 8
echo test(2, 'hello');//输出 2 hello

从列子中看出,$b参数可以传入多个类型声明,这个对某些情况会很有用,以前只能在注释里标记,现在方法里可以直接声明,方便不少。

mixed类型是以下里面的一种。

  • array
  • bool
  • callable
  • int
  • float
  • null
  • object
  • resource
  • string

需要注意的是,null不能作为单独的类型,如下:

function test (null $a); // 不可以
function test (int|null $a); // 可以

还有oject和resource也不能一起用,如下:

function test (object|resource $a); // 不可以
function test (int|resource $a); // 可以

5. 增加了match表达式,看下面例子

//PHP 7 or PHP 8
$a = 1;
switch ($a) {
    case 1:
        echo 'hello 1';
        break;
    case 2:
        echo 'hello 2';
        break;
    default:
        echo 'hello';
        break;
}
//上述结果 hello 1

//PHP 8
$a = 1;
echo match ($a) {
    1 => 'hello 1',
    2 => 'hello 2',
    default => 'hello',
};
//上述结果 hello 1

需要注意PHP8中变量$a是区分数据类型的,变成这样,结果就不同,这点需要格外注意。

//PHP7
$a = '1';
switch ($a) {
    case 1:
        echo 'hello 1';
        break;
    case 2:
        echo 'hello 2';
        break;
    default:
        echo 'hello';
        break;
}
//上述结果 hello 1

//PHP 8
$a = '1';
echo match ($a) {
    1 => 'hello 1',
    2 => 'hello 2',
    default => 'hello',
};
//上述结果 hello

6. 增加了nullsafe操作符,看以下例子:

//PHP8
class TestClass
{
    public function __construct(object|null $dog)
    {
        ######## 写法简单 #######
        $dog?->cat?->saying();
    }
}

class Dog
{
    public $cat;
    public function __construct()
    {
        $this->cat = new Cat();
    }
    public function saying(): void{
        echo 'Wang wang';
    }
}

class Cat
{
    public function saying(): void{
        echo 'Miao miao';
    }
}

new TestClass(new Dog()); //输出 Miao miao
new TestClass(null); //输出为空

本来多种判断的,现在加几个?就解决了,$dog?->cat?->saying(); 确实方便很多了。

第一个变量还是要有的,如果你直接写$dog?->cat?->saying(); 还是会报错的。


7. 改进了字符串和数字的比较,看下例子:

//PHP7
var_dump(0 == '0');  // 输出 bool(true)
var_dump(0 == '');   // 输出 bool(true)
var_dump('0' == ''); // 输出 bool(false) 不能理解吧,为何会不相等,上面都连等了。

//PHP8
var_dump(0 == '0');  // 输出 bool(true)
var_dump(0 == '');   // 输出 bool(false)
var_dump('0' == ''); // 输出 bool(false) 

与其说改进,还不如说修了个bug,这个可能对有些对于空或0的判断要格外注意,一不小心,可能就是一个大bug,类型这种情况。

//PHP7
var_dump(44 == '44aaa'); 输出 bool(true)

//PHP8
var_dump(44 == '44aaa'); 输出 bool(false)

8. 内部函数类型错误的一致性,这个看下就行,错误级别不太一样而已。

//PHP 7
strlen([]); // Warning: strlen() expects parameter 1 to be string, array given
array_chunk([], -1); // Warning: array_chunk(): Size parameter expected to be greater than 0

//PHP8
strlen([]); // TypeError: strlen(): Argument #1 ($str) must be of type string, array given
array_chunk([], -1); // ValueError: array_chunk(): Argument #2 ($length) must be greater than 0

9. 即时编译,这个看看官方的说明就行。

PHP 8 引入了两个即时编译引擎。 Tracing JIT 在两个中更有潜力,它在综合基准测试中显示了三倍的性能, 并在某些长时间运行的程序中显示了 1.5-2 倍的性能改进。 典型的应用性能则和 PHP 7.4 不相上下。

关于 JIT 对 PHP 8 性能的贡献

Just-In-Time compilation

10. static和self可以作为返回类型,看一下例子:

//PHP8
class Animal
{
    public static function instance(): self|static
    {
        return new static();
    }

    public function saying(): void
    {
        echo 'No sound';
    }
}

class Dog extends Animal
{
    public function saying(): void
    {
        echo 'Wang wang';
    }
}

class Cat extends Animal
{
    public function saying(): void
    {
        echo 'Miao miao';
    }
}

Animal::instance()->saying(); //输出 No sound
Dog::instance()->saying(); //输出 Wang wang
Cat::instance()->saying(); //输出 Miao miao

11. throw new \Exception('') 从以前的声明用,现在也可用于表达式结构,看一下例子:

//PHP7
try {
    $foo = $bar['offset'] ?? throw new \Exception('test');
} catch(\Exception $e) {
    echo 'get exception';
} 
//以上结果会出现语法解析错误:
#PHP Parse error:  syntax error, unexpected 'throw' (T_THROW) in /test/php8.php on line 3

//PHP8
try {
    $foo = $bar['offset'] ?? throw new \Exception('test');
} catch(\Exception $e) {
    echo 'get exception';
} 
//以上结果输出 get exception

12. 增加了类Weakmap,看下面的例子,看看它有什么用:

//PHP7 or PHP8
// 房子
class House
{
    public $rooms;
    
    public function __construct(array $rooms = [])
    {
        $this->rooms = $rooms;
    }

    // 添加人
    public function addPerson(Person $person)
    {
       $this->rooms[$person->name] = $person;
    }
}

// 人
class Person
{
    public $name;
    public function __construct(string $name)
    {
        $this->name = $name;
    }
}

$house = new House();
$lucy = new Person('lucy');
$jony = new Person('jony');
// 为房子添加人
$house->addPerson($lucy);
$house->addPerson($jony);
//计数
echo count($house->rooms); // 输出 2
unset($lucy); // 删除1人
//$luch已经删除,想得到的结果是1人
echo count($house->rooms); // 输出 2    
// 这个人还在
$lucyObj = $house->rooms['lucy']; 
//理论上$luch应该不存在了
echo $lucyObj->name; // 输出 lucy   


//PHP8 Weakmap写法
class House
{
    public WeakMap $rooms;

    public function __construct()
    {
        // 实例化 WeakMap
        $this->rooms = new WeakMap();
    }

    public function addPerson(Person $person)
    {
        // 注意,这里的键一定是要一个对象
        $this->rooms[$person] = $person->name;
    }
}

class Person
{
    public function __construct(public string $name){}
}

// 房东
$house new House();
// 租房的人
$lucy new Person('lucy');
$jony new Person('jony');
// 租房并给要钥匙
$house->addPerson($lucy);
$house->addPerson($jony);
// 查看租房人数
echo count($house->rooms); // 输出 2
// lucy不租了(删除引用)
unset($lucy);
// 再次查看租房人数
echo count($house->rooms); // 输出 1
$lucyObj $house->rooms['lucy'];
// 会报以下错误,lucy这个人已经走了
#PHP Fatal error:  Uncaught TypeError: WeakMap key must be an object in /test/php8.php:44
#Stack trace:
#0 {main}
#  thrown in /test/php8.php on line 44

13. ::class可以用于实例化后的类,看以下例子

//PHP7
$obj = new \stdClass();
echo $obj::class;
//结果会报以下错误:
#PHP Fatal error:  Dynamic class names are not allowed in compile-time ::class fetch in /test/php8.php on line 4

//PHP8
$obj = new \stdClass();
echo $obj::class;
//输出 stdClass

14. Exception可以不用定义变量,看以下例子:

//PHP7
try {
    throw new \Exception();
} catch (\Exception) {
    echo "Something went wrong";
}
//结果报以下错误:
#PHP Parse error:  syntax error, unexpected ')', expecting '|' or variable (T_VARIABLE) in /test/php8.php on line 5

//PHP8
try {
    throw new \Exception();
} catch (\Exception) {
    echo "Something went wrong";
}
//结果输出 Something went wrong

15. 方法参数最后的逗号被允许,看以下例子:

//PHP7
function test($a, $b,)
{
    echo $a + $b;
}
test(3, 5);
//结果会报以下错误:
#PHP Parse error:  syntax error, unexpected ')', expecting variable (T_VARIABLE) in /test/php8.php on line 2

//PHP8
function test($a, $b,)
{
    echo $a + $b;
}
test(3, 5);
//输出 8

16. 增加了Stringable的接口,看一下例子:

//PHP8
class Foo
{
    public function __toString(): string
    {
        return 'foo';
    }
}

function bar(string|Stringable $str)
{
    echo $str;
}
bar(new Foo()); //输出 foo
bar('abc');//输出 abc

17. 增加了str_contains, str_starts_with, str_ends_with, fdiv, get_debug_type, get_resource_id方法

var_dump(str_contains('string with lots of words', 'words')); //输出 bool(true)
var_dump(str_starts_with('haystack', 'hay')); //输出 bool(true)
var_dump(str_ends_with('haystack', 'stack')); //输出 bool(true)

以上是一些PHP8的新特性,当然还有一些其他方面的改动,请自行查阅相关文档即可。