PHP8 都有哪些新功能,说说PHP8的新增特性

PHP8 都有哪些新功能,说说PHP8的新增特性

2020年10月29日,PHP8将发布 RC3的版本,11月26日将正式发布。

新增 union types

这里的 union 和c/c++的不同,是指PHP支持多类型定义,比如例子里的 $number 同时支持整型(int) 和 浮点型(float)。

如果这样使用 $number->setNumber(‘string’),将会引发错误提示。所以强类型定义的好处是提前在代码编译期就能发现错误,从而避免代码错误赋值。

当然这是强类型语言的特性,因为减少类型处理的麻烦,自然可以提升运行速度。这也是为啥强类型语言比脚本语言快的根本原因之一。如下面示例代码:

<?php
declare(strict_types=1);

class Number {
    private int|float $number;

    public function setNumber(int|float $number): void {
        $this->number = $number;
    }

    public function getNumber(): int|float {
        return $this->number;
    }
}

/**
 * We can pass both floats or integer values
 * to the number object. Try passing a string.
 */
$number = new Number();

$number->setNumber(5);

dump($number->getNumber());

$number->setNumber(11.54);

dump($number->getNumber());

输出:

5

11.54

WeakMap 弱键值引用

WeakMap不同于PHP7.4引入的Weak References(弱类型引用)。

当一个对象作为WeakMap的 Key 时,对象销毁时,Key 会自动从 WeakMap 里移除。

如下面示例代码:

$map = new WeakMap;
$obj = new stdClass;
$map[$obj] = 42;
var_dump($map);
// object(WeakMap)#1 (1) {
//   [0]=>
//   array(2) {
//     ["key"]=>
//     object(stdClass)#2 (0) {
//     }
//     ["value"]=>
//     int(42)
//   }
// }

// The object is destroyed here, and the key is automatically removed from the weak map.
unset($obj);
var_dump($map);
// object(WeakMap)#1 (0) {
// }

新增 ValueError 内部异常

<?php
declare(strict_types=1);

/**
 * array_rand 第一个参数不能是空数组,否则会引发 ValueError
 */
array_rand([], 0);

/**
 * 第三个参数 depth 必须是大于0的整数,否则会引发 ValueError
 */
json_decode('{}', true, -1);

错误输出:

ValueError array_rand(): Argument #1 ($array) cannot be empty

方法重写(overriding)里允许可变参数

对象语言多态的特性,这里我觉得叫 overloading(重载)更好点。因为保持原有参数不变的情况下,继承类中使用才叫 overriding。

原有PHP的多态没有c++/Java完善,这里就不咬文嚼字了,大家理解含义就好。这个新特性也恰恰弥补了这点。

三个点开头 …$param 这种叫可变参数,这个特性见如下代码:

<?php
declare(strict_types=1);

class A {
    public function method(int $many, string $parameters, $here) {

    }
}
class B extends A {
    public function method(...$everything) {
        dd($everything);
    }
}

$b = new B();
$b->method('i can be overwritten!');

输出:

array:1 [▼

0 => “i can be overwritten!”

]

返回静态类型

这个不是什么好玩的新特性,应该是针对 static语法冲突的解决方案。如果我理解有误,欢迎指正。先看这个代码:

class A {
    public function test(static $a) {}
}
class B extends A {}

function call_with_new_a(A $a) {
    $a->test(new A);
}

call_with_new_a(new B);
class A {
    // Is this an untyped static property,
    // or an instance property of type static?
    public static $a;
}

注意上面两段代码就是存在 static 冲突的代码。是无效代码,因为PHP8为了解决冲突,仅允许在函数返回类型中使用 static修饰,含义为返回当前类的定义。正确使用方法如下:

<?php
declare(strict_types=1);

class Test {
    public function doWhatever(): static {
        // Do whatever.
        return $this;
    }
}

$object::class返回类型名的语法糖

原来我们返回类型名,需要调用 get_class($object),PHP8新增了一个语法糖 $object::class,是比以前简单了很多,看来未来有可能删除掉 get_class。

<?php
declare(strict_types=1);

auth()->loginUsingId(1);

dump(auth()->user()::class);

// Or with a temporary variable
$user = auth()->user();

dump($user::class);

输出:

“App\User”
“App\User”

变量语法上的微调(Variable Syntax Tweaks)

这个是针对字符串连续性的语法问题,仔细研究后,感觉还存在一些问题,等PHP8正式发版再看看。或者我理解有误的地方,也欢迎指正。

/**
 * Variable Syntax Tweaks 
 * New and instanceof can now be used with arbitrary expressions, 
 * using new (expression)(...$args) and $obj instanceof (expression).
 */
<?php

class Foo {}

$f = 'Foo';
new $f;   // PHP7 需要个中间变量才合法

new 'Foo'; // PHP7 不合法,PHP8里变合法了

// PHP8的这个特性,目前根据RFC,以下代码还是存在一些问题,等待正式发布再看看

echo __FUNCTION__[0]; // RFC文档说明应该合法,但还是不合法

$k = 'o';
new "F$k$k";  // RFC文档说明应该合法,但还是不合法

下面代码在PHP8中合法:

<?php
declare(strict_types=1);

class Foo {}
class Bar {}

$class = new (collect(['Foo', 'Bar'])->random());

dd($class);

Stringable interface

PHP8引入了 Stringable,当一个类实现 __toString 时,类会自动转成 Stringable 的实例,而不需要显式声明,代码如下:

<?php
declare(strict_types=1);

class Foo {
    public function __toString() {
        return 'I am a class';
    }
}

$obj = new Foo;
dump($obj instanceof Stringable);  // 输出 true

Traits 新增 abstract private 方法

<?php
declare(strict_types=1);

trait MyTrait {
    abstract private function neededByTheTrait(): string;

    public function doSomething() {
        return strlen($this->neededByTheTrait());
    }
}

class TraitUser {
    use MyTrait;

    // This is allowed:
    private function neededByTheTrait(): string { }

    // This is forbidden (incorrect return type)
    // private function neededByTheTrait(): stdClass { }

    // This is forbidden (non-static changed to static)
    // private static function neededByTheTrait(): string { }
}

throw 可以作为表达式使用了

直接看下面代码中的差异:

<?php
declare(strict_types=1);

// 下面代码在PHP7中不合法,在PHP8中合法了
$callable = fn() => throw new Exception();

$nullableValue = null;

// $value is non-nullable.
$value = $nullableValue ?? throw new \InvalidArgumentException();

允许最后一个参数后面的逗号了 ,

直接看代码,$d, 4, 后面都允许有多余的逗号了:

<?php
declare(strict_types=1);

function method_with_many_arguments(
    $a, 
    $b,
    $c,
    $d,
) {
    dump("this is valid syntax");
}

method_with_many_arguments(
    1,
    2,
    3,
    4,
);

Catch 异常表达式,可以不定义变量了

以前必须定义 $e,像这样 catch($e \Exception),PHP8中可以不需要定义$e 了。

<?php
declare(strict_types=1);

$nullableValue = null;

try {
    $value = $nullableValue ?? throw new \InvalidArgumentException();
} catch (\InvalidArgumentException) {
    dump("Something went wrong");
}

新增 mixed 类型

mixed 是一种混合类型,等同于 array|bool|callable|int|float|null|object|resource|string。

<?php
declare(strict_types=1);

function debug_function(mixed ...$data) {
    dump($data);
}

debug_function(1, 'string', []);

输出:

array:3 [▼

0 => 1

1 => “string”

2 => []

]

新增属性定义

这个是PHP8比较大的改动,新语法 #[Attribute] 定义属性。以前操作需要使用反射,现在变简单了许多。代码后面使用 ReflectionClass 来验证 *#[xxxx] *这种语法。

<?php
declare(strict_types=1);
// First, we need to define the attribute. An Attribute itself is just a plain PHP class, that is annotated as an Attribute itself.

#[Attribute]
class ApplyMiddleware
{
    public array $middlware = [];

    public function __construct(...$middleware) {
        $this->middleware = $middleware;
    }
}

// This adds the attribute to the MyController class, with the "auth" middleware as an argument.

#[ApplyMiddleware('auth')]
class MyController
{
    public function index() {}
}

// We can then retrieve all ApplyMiddleware attributes on our class using reflection
// And read the given middleware arguments.

$reflectionClass = new ReflectionClass(MyController::class);

$attributes = $reflectionClass->getAttributes(ApplyMiddleware::class);

foreach ($attributes as $attribute) {
    $middlewareAttribute = $attribute->newInstance();
    dump($middlewareAttribute->middleware);
}

新语法:构造函数里添加属性

<?php
declare(strict_types=1);

class User {
    public function __construct(
        public int $id,
        public string $name,
    ) {}
}

$user = new User(1, 'Marcel');

dump($user->id);
dump($user->name);

新增 match 表达式

match 表达式 和swtich 条件分支类似,主要作用是直接返回值。

<?php
declare(strict_types=1);

echo match (1) {
    0 => 'Foo',
    1 => 'Bar',
    2 => 'Baz',
};

新增 nullsafe 操作符

这个改动很好,不过有点参照 typescript 和 swift 的影子。管他呢,好用就行,这个语法糖漂亮多了。

<?php
declare(strict_types=1);

class User {
    public function getAddress() {}
}

$user = new User();

$country = $user?->getAddress()?->country?->iso_code;

dump($country);

新增命名参数

python 的影子,管他呢,好用就行,这个特性也非常实用,不用再考虑必须保证参数顺序了,每个参数做啥的也能一目了然。

// 原来必须这样按顺序
array_fill(0, 100, 50);

// PHP8有了命名参数后,可以这样了
array_fill(start_index: 0, num: 100, value: 50);
array_fill(value: 50, num: 100, start_index: 0);

总结

以上关于PHP8的新特性,虽然没有介绍JIT相关,但比如针对强类型上的改进,union type, mixed, Weakmap等也是为了适应JIT的变化。

也有一些语法上的改进,尤其最后介绍的 nullsafe和命名参数,都是非常实用的。PHP8的发布值得期待。

本作品采用《CC 协议》,转载必须注明作者和本文链接
思考技术、产品
本帖由系统于 11个月前 自动加精
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 11
mowangjuanzi

新版的 match 相对于老版的 switch 的严格版。也就是说这玩意其实是严格比较key,不仅比较值还比较类型。具体可参考如下链接:

www.laruence.com/2020/07/13/6033.h...

11个月前 评论

@mowangjuanzi 正解,感谢补充

11个月前 评论

nullsafe挺实用的

11个月前 评论

发现一个高质量免费视频视频:whats-new-in-php-8

11个月前 评论

只能帮你喊个 666 大佬牛逼

11个月前 评论

搞的不错!

11个月前 评论

nullsafe 操作符 这个例子是什么意思?

11个月前 评论
justmd5 9个月前
晓鹤 9个月前

@609468798

$user?->getAddress()?->country?->iso_code;这种链式调用,不用担有空值null,造成出现程序异常。

只有要空值的,结果也会返回空值。所以叫 nullsafe

11个月前 评论

#新增命名参数 因为这个几度想搞python,现在可以安心了#新增命名参数 因为这个几度想搞python,现在可以安心了

11个月前 评论

楼主~,match表达式的详解可以鸟哥这篇Blogs应该比较详细了,www.laruence.com/2020/07/13/6033.h...

11个月前 评论

@做软件的禅师 你这个例子 原来我们 $user->getAddress()->country->iso_code; 这样取值 iso_code 现在改成 $user?->getAddress()?->country?->iso_code; 的语法取值iso_code 这样用的吗?

9个月前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!