命名参数

命名函数允许根据参数名而不是参数位置向函数传参, 命名参数通过在参数名前加上冒号来传递, 允许使用保留关键字作为参数, 参数名必须是一个标识符, 不能是动态的, php函数自定义函数都可以

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php

function nameParameter($class, $b, $c = 0) {
    return [$class, $b, $c];
}

/**
 *array(3) {
    [0]=>
    int(3)
    [1]=>
    int(2)
    [2]=>
    int(0)
    }
 */
var_dump(nameParameter(b: 2, class : 3));// 注意这里是使用了保留关键字class

/**
 * 用给定的值填充数组
 * array(5) {
    [0]=>
    string(4) "shea"
    [1]=>
    string(4) "shea"
    [2]=>
    string(4) "shea"
    [3]=>
    string(4) "shea"
    [4]=>
    string(4) "shea"
    }
 */
var_dump(array_fill(0, 5, 'shea'));

var_dump(array_fill(value: 'shea', count: 5, start_index: 0));

注解

什么是注解

注解功能使得代码中的声明部分都可以添加结构化、机器可读的元数据, 注解的目标可以是类、方法、函数、参数、属性、类常量。 通过 反射 API 可在运行时获取注解所定义的元数据。 因此注解可以成为直接嵌入代码的配置式语言。

通过注解的使用,在应用中实现功能、使用功能可以相互解耦。 某种程度上讲,它可以和接口(interface)与其实现(implementation)相比较。 但接口与实现是代码相关的,注解则与声明额外信息和配置相关。 接口可以通过类来实现,而注解也可以声明到方法、函数、参数、属性、类常量中。 因此它们比接口更灵活。

使用注解

注解声明总是以 #[ 开头,以 ] 结尾来包围。 内部则是一个或以逗号包含的多个注解。 注解的名称按 使用命名空间:基础 章节中描述,可以是非限定、限定、完全限定的名称。 注解的参数是可以选的,以常见的括号()包围。 注解的参数可以是字面值或者常量表达式。 它同时接受位置参数和命名参数两种语法。

在以前, 我们要读取注释部分, 需要这么做

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<?php

/**
 * @param Foo $argument
 * @see https:/xxxxxxxx/xxxx/xxx.html
 */
function dummyOld($Foo) {}

$ref = new ReflectionFunction("dummyOld");
$doc = $ref->getDocComment();
var_dump($doc);

输出

1
2
3
4
string(72) "/**
 * @param Foo $argument
 * @see https:/xxxxxxxx/xxxx/xxx.html
 */"

把整块注释读出来,然后再匹配截断获取内容, 然而在php8, 把注释升级成注解, 这样会有不一样的变化

我们听得最多的注解使用是在java中, 其实都是类似的, 定义注解类(注解只能使用到上, 这是因为 Attribute 的类型为 TARGET_CLASS), 使用注解, 简单来说, 就是把注解实例化下面示例中, 使用的是反射api来读取注解内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php

// 多个注解可以以 逗号 分开
#[
    Params("Foo", "argument"),
    See("https://www.baidu.com"),
    MyAttribute("See", "https://xxxxxxxx/xxxx/xxx.html") // 这里使用 class为MyAttribute的注解类, 传入两个参数值
]
function dummy($argument) {}

// 这里是声明注解类, Attribute::TARGET_FUNCTION 规定只能在function中使用
#[Attribute(Attribute::TARGET_FUNCTION)]
/**
    TARGET_CLASS    //类的注解类
    TARGET_FUNCTION //函数注解类
    TARGET_METHOD   //方法注解类
    TARGET_PROPERTY //属性注解类
    TARGET_CLASS_CONSTANT //类常量注解类
    TARGET_PARAMETER //参数注解类
    TARGET_ALL // 默认
 */
class MyAttribute {
    public function __construct($name, $value) {
        var_dump($name);
        var_dump($value);
    }
}

$ref = new ReflectionFunction('dummy');

var_dump($ref->getAttributes('See')[0]->getName());// 通过反射api能查到注解名
var_dump($ref->getAttributes('See')[0]->getArguments());// 通过反射api能查到注解的参数值
var_dump($ref->getAttributes('MyAttribute')[0]->newInstance());// 输出了传入的两个参数值, 和类实例

/**
输出:
	string(3) "See"
    array(1) {
      [0]=>
      string(21) "https://www.baidu.com"
    }

    string(3) "See"
    string(30) "https://xxxxxxxx/xxxx/xxx.html"
    object(MyAttribute)#3 (0) {
   }
*/

// 这里也是声明注解类, 这里标记了只能在类上使用
#[Attribute(Attribute::TARGET_CLASS)]
class MyClass {
    public function __construct($name, $value) {
        var_dump($name);
        var_dump($value);
    }
}

#[MyClass("1", "2")]
class Annotation{}

$ref = new ReflectionClass(Annotation::class);

var_dump($ref->getAttributes('MyClass')[0]->newInstance());

/**
输出:
	string(1) "1"
    string(1) "2"
    object(MyClass)#2 (0) {
    }
*/

构造器属性提升

说明: 构造器的参数也能相应提升为类的属性, 在类的编写中, 构造器的参数赋值给类属性的行为很普遍, 来看下代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
    
class Point {
    protected int $c = 1;

    public function __construct(protected int $x = 0, protected int $y = 0)
    {

    }

    public function get()
    {
        return [$this->x, $this->y, $this->c];
    }
}
/**
 * array(3) {
        [0]=>
        int(0)
        [1]=>
        int(0)
        [2]=>
        int(1)
    }
 */
var_dump((new Point())->get());

注意:

对象属性的类型不能为 callable 以避免为引擎带来混淆。 因此提升的参数也不能是 callable。 其他任意 类型声明 是允许的。

注意:

放在构造器提升参数里的属性会同时复制为属性和参数。

联合类型

在PHP7中增加了标量类型声明返回值类型声明,在PHP8中再次进行强化, 可以进行联合类型声明, 顾名思义, 可以接收多个不同的类型作为参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php

/**
 * null类型可以在联合类型中使用, 但不能单独使用
 * 因为历史原因, 很多内部函数失败不是返回null, 是返回false, 所以false这个字面类型也一样可以在联合类型中使用, 但也不能单独使用
 * 没有 true 字面类型
 * mixed类型为独立类型
 * 类型名不区分大小写, 所以不能这样使用 int|INT
 */
function unionDeclarations(INT|array|null|false|callable|float|string|object $a): INT|array|null|false|callable|float|string|object {
    return fn() => [];// 在php7.4中箭头函数(匿名函数, 也叫闭包函数)的一种更简洁的写法
}

重复冗余的类型

为了能在联合类型声明中暴露简单的 bug,不需要加载 class 就可以在编译时让重复冗余的类型产生错误。 包含:

  • 解析出来的类型只能出现一次。例如这样的类型 int|string|INT 会导致错误。
  • 使用了 bool 时就不能再附带使用 false。
  • 使用了 object 时就不能再附带使用 class 类型。
  • 使用了 iterable 时,array、 Traversable 都不能再附带使用。

match表达式

这里参考PHP8新特性之match表达式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
/**
 * php8开始, match 为关键字
 * 类似于switch的case加上break, 并且多个条件可以写在一起
 * 使用严格模式(===), switch为(==)
 * match在所有条件无法满足时会抛出错误 Fatal error: Uncaught UnhandledMatchError: Unhandled match value of type string
 */
$input = 2;

$result = match ($input) {
    "true", "on" => 1,
    "2" => "string 2",
    2 => "int 2",
};

switch ($input) {
    case "2":
        echo "string 2";
        break;
    case 2:
        echo "int 2";
        break;
    default:
        break;
}

var_dump($result);

//输出 string 2string(5) "int 2"

Nullsafe 运算符

自 PHP 8.0.0 起,类属性和方法可以通过 “nullsafe” 操作符访问: ?->。 除了一处不同,nullsafe 操作符和以上原来的属性、方法访问是一致的: 对象引用解析(dereference)为 null 时不抛出异常,而是返回 null。 并且如果是链式调用中的一部分,剩余链条会直接跳过。

此操作的结果,类似于在每次访问前使用 is_null() 函数判断方法和属性是否存在,但更加简洁。

注意: 这里是用is_null判断的, 如果对js的es6有所了解的话, 可能会知道 ?. Null 传导运算符, 当其中一个返回null或者undefined则会短路, 返回undefined;

这里的es6的语法有点像php中的isset;需要区别开

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<?php

$anonymous = (new class {
    public $name;
    function get() {
        return null;
    }
});

var_dump($anonymous?->get()?->name);

//输出 NULL

JIT

JIT(即时编译器)作为PHP8很重要的新功能, 也是最广为人知的可以提升PHP性能的功能, 介绍JIT有比我更好的文章, 这里列举两篇

但是就测试而言, 以laravel 8.47.0为例, 提升并不显著,使用ab测试工具, 取第三次测试结果, 没有使用平均值, 看个大概, 具体可以自己试一下

未开启oPcache和JIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
root@DESKTOP-7SF8IGG:/mnt/c/Users/Administrator/Desktop# ab -n 1000 -c 100 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        17490 bytes

Concurrency Level:      100
Time taken for tests:   114.749 seconds
Complete requests:      1000
Failed requests:        0
Total transferred:      18611000 bytes
HTML transferred:       17490000 bytes
Requests per second:    8.71 [#/sec] (mean)
Time per request:       11474.942 [ms] (mean)
Time per request:       114.749 [ms] (mean, across all concurrent requests)
Transfer rate:          158.39 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   1.4      0       8
Processing:   106 10943 2119.9  11305   14637
Waiting:      105 10942 2119.9  11305   14636
Total:        113 10943 2118.7  11305   14637

Percentage of the requests served within a certain time (ms)
  50%  11305
  66%  11323
  75%  11348
  80%  11369
  90%  12965
  95%  13776
  98%  13795
  99%  14620
 100%  14637 (longest request)

开启oPcache,不开启JIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
root@DESKTOP-7SF8IGG:/mnt/c/Users/Administrator/Desktop/blog# ab -n 10000 -c 1000 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   6.459 seconds
Complete requests:      10000
Failed requests:        535
   (Connect: 0, Receive: 0, Length: 535, Exceptions: 0)
Total transferred:      9956885 bytes
HTML transferred:       9357150 bytes
Requests per second:    1548.31 [#/sec] (mean)
Time per request:       645.866 [ms] (mean)
Time per request:       0.646 [ms] (mean, across all concurrent requests)
Transfer rate:          1505.50 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   28  29.1     21     173
Processing:     2  178 782.6     21    6246
Waiting:        0  158 786.2      0    6246
Total:          6  206 801.1     41    6337

Percentage of the requests served within a certain time (ms)
  50%     41
  66%     48
  75%     52
  80%     53
  90%     62
  95%    559
  98%   3687
  99%   4889
 100%   6337 (longest request)

开启oPcache和JIT

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
root@DESKTOP-7SF8IGG:/mnt/c/Users/Administrator/Desktop/blog# ab -n 10000 -c 1000 http://127.0.0.1:8000/
This is ApacheBench, Version 2.3 <$Revision: 1843412 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            8000

Document Path:          /
Document Length:        0 bytes

Concurrency Level:      1000
Time taken for tests:   5.794 seconds
Complete requests:      10000
Failed requests:        603
   (Connect: 0, Receive: 0, Length: 603, Exceptions: 0)
Total transferred:      11222433 bytes
HTML transferred:       10546470 bytes
Requests per second:    1725.98 [#/sec] (mean)
Time per request:       579.380 [ms] (mean)
Time per request:       0.579 [ms] (mean, across all concurrent requests)
Transfer rate:          1891.58 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   26  23.9     21     147
Processing:     2  182 747.5     20    5601
Waiting:        0  164 751.1      0    5601
Total:          7  208 761.8     39    5648

Percentage of the requests served within a certain time (ms)
  50%     39
  66%     41
  75%     43
  80%     46
  90%     60
  95%   1210
  98%   3565
  99%   4672
 100%   5648 (longest request)