最近把项目的PHP版本从7.0升级到了7.1,顺便系统学习了一下PHP 7.1的新特性,收获很大。PHP 7.1是PHP 7系列的一个重要版本,在PHP 7.0的基础上,增加了很多新特性,也做了很多优化,性能更好,语法更现代,开发体验也更好。

但是很多PHP开发者,可能还在用PHP 5.x,或者虽然升级到了7.x,但是对7.1的新特性不了解,还是在用老的写法,没有享受到新版本带来的便利。今天就来分享一下我学习PHP 7.1新特性的路线和经验,聊聊PHP 7.1到底有哪些重要的新特性,怎么系统地学习,怎么在项目中应用,以及我踩过的一些坑,希望能给想学习PHP 7.1的朋友一些参考,少走弯路,快速上手。

一、为什么要升级到PHP 7.1

在说新特性之前,先说说为什么要升级到PHP 7.1,升级有什么好处。

1. 性能提升

PHP 7.0相比PHP 5.6,性能就有了巨大的提升,几乎是翻倍的,而PHP 7.1在7.0的基础上,又做了很多优化,性能进一步提升,特别是在数组操作、字符串处理、函数调用等方面,都有优化。对于大流量的网站,升级到PHP 7.1,能显著提升性能,降低服务器负载,节省成本。

我们的项目升级到7.1之后,接口的平均响应时间降低了大概15%,服务器的CPU使用率也降低了一些,效果还是很明显的。

2. 新特性,开发效率更高

PHP 7.1增加了很多新特性,比如可空类型、void返回类型、类常量可见性、多异常捕获、list解构、负字符串偏移等,这些新特性能让代码更简洁,更健壮,更易读,开发效率更高,也能减少bug。

3. 更严格的类型检查,代码更健壮

PHP 7.0引入了标量类型声明和返回类型声明,PHP 7.1又增加了可空类型和void返回类型,类型系统越来越完善,能在开发阶段就发现很多类型错误,让代码更健壮,更易维护,也更适合大型项目和团队协作。

4. 官方支持,安全更新

PHP 5.6已经在2018年底停止官方支持了,PHP 7.0也在2018年底停止安全更新了,而PHP 7.1还在官方支持期内,会有安全更新和bug修复,用新版本更安全,更有保障。对于生产环境来说,用官方支持的版本是很重要的,不然出了安全漏洞,没有官方补丁,就很危险。

5. 生态完善,主流框架都支持

现在主流的PHP框架,比如Laravel、Symfony、ThinkPHP、Yii等,都已经支持PHP 7.1,甚至要求PHP 7.1以上,很多新的框架和扩展,也都是基于PHP 7+开发的,升级到7.1,能更好地使用这些新的框架和工具,跟上生态的发展。

所以,不管是从性能、开发效率、代码质量,还是从安全、生态的角度,升级到PHP 7.1都是很有必要的,如果你还在用PHP 5.x或者7.0,建议尽快升级。

二、PHP 7.1的重要新特性

接下来,说说PHP 7.1有哪些重要的新特性,这也是学习的重点,我按照重要程度和使用频率,整理了一下。

1. 可空类型(Nullable Types)

这是PHP 7.1最重要的新特性之一,在类型声明的时候,在类型前面加个问号,就表示这个参数或者返回值可以是null,也可以是指定的类型。

比如:

function test(?string $name) {
    var_dump($name);
}

test('hello'); // string(5) "hello"
test(null); // NULL

返回类型也可以是可空的:

function getAge(): ?int {
    return null; // 或者返回一个int
}

这个特性非常实用,以前如果一个参数可以是null,也可以是string,就不能用类型声明,或者用两个类型(PHP 8.0才支持联合类型),现在有了可空类型,就可以很方便地声明,让类型系统更完善,代码更健壮。

在项目中,很多参数都是可空的,比如查询条件,可选参数等,用可空类型,能让代码更清晰,也能在开发阶段发现类型错误。

2. void返回类型

PHP 7.1增加了void返回类型,用来表示函数没有返回值。

比如:

function log(string $message): void {
    echo $message;
    // 不需要return,或者return;
}

void返回类型的函数,不能有返回值,哪怕return null也不行,只能return;或者不写return。这个特性很适合那些没有返回值的函数,比如日志、输出、设置属性等,让函数的意图更清晰,也能防止不小心返回了值。

以前PHP没有void返回类型,函数有没有返回值全靠注释和约定,现在有了void,就能在语法层面保证,代码更严谨。

3. 类常量可见性

PHP 7.1之前,类的常量都是public的,不能设置可见性,PHP 7.1开始,类常量也可以设置public、protected、private可见性了,和类的属性、方法一样。

比如:

class Test {
    public const PUBLIC_CONST = 'public';
    protected const PROTECTED_CONST = 'protected';
    private const PRIVATE_CONST = 'private';
}

这个特性很实用,以前类常量都是public的,外部都能访问,没有封装性,现在可以设置private或者protected,把内部用的常量隐藏起来,只暴露需要公开的,封装性更好,代码更安全。

在项目中,很多类常量都是内部用的,不需要对外暴露,用private或者protected,能更好地封装,避免外部依赖内部常量,减少耦合。

4. 多异常捕获(Multi-catch)

PHP 7.1之前,try-catch的时候,一个catch只能捕获一种异常,如果要捕获多种异常,并且处理方式一样,就要写多个catch,代码很冗余。PHP 7.1开始,一个catch可以用竖线分隔,捕获多种异常。

比如:

try {
    // 一些代码
} catch (FirstException | SecondException $e) {
    // 处理这两种异常
}

这个特性很实用,很多时候,几种异常的处理方式是一样的,比如记录日志,返回错误信息,以前要写好几个catch,代码重复,现在一个catch就搞定了,代码更简洁,更易维护。

5. list()解构增强

PHP 7.1对list()做了很多增强,更灵活,更强大。

首先,list()现在可以指定键名,用来解构关联数组:

$data = ['id' => 1, 'name' => 'test'];
list('id' => $id, 'name' => $name) = $data;
echo $id; // 1
echo $name; // test

其次,list()可以嵌套,用来解构多维数组:

$data = [['id' => 1, 'name' => 'test'], ['id' => 2, 'name' => 'test2']];
list(list('id' => $id1, 'name' => $name1), list('id' => $id2, 'name' => $name2)) = $data;

还有,list()可以跳过某些元素,用空的位置就行:

list($a, , $c) = [1, 2, 3];
echo $a; // 1
echo $c; // 3

另外,PHP 7.1还引入了list()的简写语法[],和数组的语法一样:

[$a, $b, $c] = [1, 2, 3];
['id' => $id, 'name' => $name] = ['id' => 1, 'name' => 'test'];

这个简写语法非常好用,现在很多代码都用[]来解构数组,比list()简洁多了,特别是在遍历数据库查询结果的时候,很方便。

这些list()的增强,让数组解构变得非常灵活和强大,在处理数组数据的时候,能少写很多代码,更简洁,更易读。

6. 负字符串偏移

PHP 7.1开始,字符串的偏移量可以用负数了,表示从字符串末尾开始数,和数组的负偏移一样。

比如:

$str = 'hello';
echo $str[-1]; // o
echo $str[-2]; // l

以前字符串的偏移量只能用正数,要取最后一个字符,得用strlen($str)-1,很麻烦,现在直接用-1就行,方便多了,也更直观。

这个特性虽然小,但是很实用,经常要取字符串末尾的字符,用负偏移就很方便。

7. 迭代器(iterable)伪类型

PHP 7.1增加了iterable伪类型,用来表示参数或者返回值可以是数组,也可以是实现了Traversable接口的对象(也就是迭代器,比如Generator、ArrayObject等)。

比如:

function traverse(iterable $items) {
    foreach ($items as $item) {
        // ...
    }
}

这个特性很适合那些可以接受数组或者迭代器的函数,比如遍历、处理数据的函数,以前要么不写类型,要么写两个重载(PHP不支持函数重载),现在用iterable,就能同时接受数组和迭代器,类型更清晰,也更灵活。

8. Closure::fromCallable()

PHP 7.1增加了Closure::fromCallable()静态方法,用来把一个可调用的结构(比如函数、方法)转换成一个Closure对象。

比如:

function test($a, $b) {
    return $a + $b;
}
$closure = Closure::fromCallable('test');
echo $closure(1, 2); // 3

这个特性在一些需要把函数当成闭包传递的场景很有用,比如回调函数、函数式编程等,比以前用匿名函数包装更方便,也更高效。

9. 异步信号处理(pcntlasyncsignals)

PHP 7.1增加了pcntlasyncsignals()函数,用来开启异步信号处理,以前处理信号需要用declare(ticks=1),性能很差,现在用pcntlasyncsignals(true),就能开启异步信号处理,性能更好,也更方便。

这个特性在需要处理信号的场景,比如守护进程、命令行脚本、多进程编程等,很有用,比以前的ticks方式高效多了。

10. 其他小特性和优化

除了上面这些比较重要的,PHP 7.1还有一些小特性和优化:

  • 支持在闭包中使用$this,不用再use ($this)了,PHP 7.1开始,闭包默认绑定了$this。
  • 增加了mbord()、mbchr()、mb_scrub()等多字节字符串函数。
  • 增加了hash_hkdf()函数,实现HKDF密钥派生。
  • 增加了opensslgetcurve_names()函数,获取支持的椭圆曲线名称。
  • 优化了数组操作、字符串处理、内存使用等,性能进一步提升。
  • 废弃了一些旧的特性,比如mcrypt扩展(移到PECL)、e修饰符等,让语言更干净。

这些小特性和优化,虽然不常用,但是在特定场景下还是很有用的,了解一下也好。

三、我的学习路线和方法

了解了有哪些新特性之后,说说我是怎么学习的,我的学习路线和方法,供大家参考。

第一步:先看官方文档,全面了解

我学习新特性,第一步都是先看官方文档,PHP的官方文档写得非常好,很详细,每个新特性都有说明,有例子,是最好的学习资料。

PHP 7.1的新特性,官方文档有专门的页面:从PHP 7.0.x 移植到 PHP 7.1.x,里面详细列出了所有的新特性、不兼容的变更、废弃的特性、新增的函数和类等,非常全面,我第一遍就是把这个页面从头到尾看了一遍,对7.1的新特性有了一个全面的了解,知道有哪些东西,大概是干什么的。

看官方文档的时候,不用一下子全部记住,先有个印象,知道有哪些特性,大概怎么用,后面用到的时候再回来细看就行。

第二步:重点特性,动手写代码练习

看完官方文档,有了全面了解之后,第二步就是针对那些重要的、常用的特性,动手写代码练习,加深理解,真正掌握。

我把上面列的那些重要新特性,一个一个地写了小例子,动手试了试,比如可空类型,我就写了几个函数,试了试传null和传正常类型的情况,看看报错是什么样的;比如多异常捕获,我就写了个try-catch,抛出不同的异常,看看是不是都能捕获到;比如list解构,我就写了各种数组,试了试指定键名、嵌套、跳过元素、简写语法等。

动手写代码是最重要的,光看文档是记不住的,只有自己动手写一写,试一试,才能真正理解,才能记住,也才能发现一些文档里没说的细节和坑。

第三步:在实际项目中应用,逐步替换

学完了,练习完了,第三步就是在实际项目中应用,把老的写法逐步替换成新特性的写法,在实践中巩固,真正把新特性用起来。

我是先在新写的代码里用新特性,比如新写的函数,都加上类型声明,可空的就用可空类型,没有返回值的就用void,类常量设置合适的可见性,异常捕获用多异常捕获,数组解构用[]简写等。

然后,在重构老代码的时候,逐步把老的写法替换成新特性的写法,比如老的list()替换成[]简写,老的多个catch合并成多异常捕获,给老的函数加上类型声明和返回类型,给类常量加上可见性等。

在项目中应用,是学习的最好方式,因为实际项目中的场景更复杂,能遇到各种各样的情况,也能真正体会到新特性带来的好处,以及需要注意的地方。

第四步:注意不兼容变更,避免踩坑

学习新特性的同时,也要注意PHP 7.1的不兼容变更,也就是那些和老版本不一样的地方,升级的时候可能会导致老代码出问题,要特别注意,避免踩坑。

PHP 7.1比较重要的不兼容变更有:

  • 当传递参数过少时,会发出警告,以前只是Notice,现在是Warning。
  • 移除了e修饰符(pregreplace的e修饰符),要用pregreplace_callback代替。
  • 废弃了mcrypt扩展,移到PECL,如果项目用了mcrypt,要换成openssl或者其他加密库。
  • 字符串的非法偏移量(比如非整数的键),会抛出警告,并且结果是null。
  • list()不能再用来拆分字符串了,以前可以,现在不行了,要用str_split。
  • 一些函数的参数和返回值有变化,比如count()现在对不可数的对象会抛出警告。

这些不兼容变更,在升级之前一定要检查一下,看看项目里有没有用到这些特性,如果有,要先修改,再升级,不然升级之后可能会出问题。我升级的时候,就遇到了老代码用了pregreplace的e修饰符,升级之后报错了,后来改成pregreplace_callback就好了。

第五步:持续学习,关注最佳实践

最后,学习不是一蹴而就的,要持续学习,关注社区的最佳实践,看看别人是怎么用这些新特性的,有什么好的经验和坑,不断改进自己的代码。

我平时会关注PHP的官方博客、PHP社区、技术博客、GitHub上的开源项目,看看别人的代码是怎么写的,有什么好的实践,比如Laravel、Symfony这些主流框架,都是怎么用PHP新特性的,学习他们的写法,应用到自己的项目中。

另外,也要关注PHP的新版本,比如PHP 7.2、7.3、7.4、8.0,每个版本都有新特性,持续学习,跟上语言的发展,不要停留在老版本的写法上。

四、踩过的坑和注意事项

在学习和升级的过程中,我也踩了一些坑,分享给大家,避免踩同样的坑。

1. 可空类型和默认值的关系

用可空类型的时候,要注意和默认值的关系,如果一个参数有默认值null,那么它的类型自动就是可空的,不需要再加问号,当然加了也没错,但是要注意一致性。

比如:

function test(?string $name = null) {
    // 可以
}

function test(string $name = null) {
    // 也可以,PHP会自动把有默认null的参数当成可空的
}

这两种写法都可以,但是建议统一,要么都加问号,要么都不加,保持代码风格一致。我一般是如果参数可空,不管有没有默认值,都加上问号,这样更清晰,一眼就能看出是可空的。

2. void返回类型不能返回值

用void返回类型的时候,要注意,函数里不能有返回值,哪怕return null也不行,只能return;或者不写return,不然会报错。

比如:

function test(): void {
    return null; // 错误,void函数不能返回值
}

function test(): void {
    return; // 正确
}

function test(): void {
    // 不写return,正确
}

这个是刚开始用的时候容易犯的错,要注意,void就是没有返回值,什么都不能返回。

3. 类常量可见性,继承的时候要注意

用类常量可见性的时候,要注意继承的规则,和方法、属性的可见性规则一样,子类不能把父类的public常量改成protected或者private,但是可以把protected的改成public,也就是可见性只能放宽,不能收紧。

还有,接口里的常量,只能是public的,不能设置protected或者private,因为接口的常量本来就是公开的。

这些规则和类的属性、方法是一样的,但是刚开始用的时候,可能会忽略,要注意一下。

4. list()解构,键名要对应

用list()指定键名解构关联数组的时候,要注意,键名必须和数组里的键名完全对应,不然会得到null,并且会发出警告。

比如:

$data = ['id' => 1, 'name' => 'test'];
list('id' => $id, 'age' => $age) = $data;
// $age是null,并且会有警告,因为$data里没有'age'这个键

所以,解构的时候要确保键名是存在的,或者先判断一下,避免警告。

5. 负字符串偏移,老代码要注意

用负字符串偏移的时候,要注意,如果偏移量超出了字符串的长度,会返回空字符串,并且发出警告,和正偏移一样。

还有,老代码里如果有把字符串当数组用,并且用了负键名的,要注意,PHP 7.1之前,字符串的负偏移是不支持的,会被当成0,现在支持了,结果可能不一样,要检查一下老代码有没有这种情况。

6. 升级前要充分测试

最后,也是最重要的,升级PHP版本之前,一定要充分测试,把项目的所有功能都测一遍,特别是那些用到了不兼容变更的地方,确保没有问题,再上生产环境。

最好是先在测试环境升级,跑一遍单元测试、功能测试,看看有没有报错,有没有异常,没问题了,再灰度上线,先上一台机器,观察一段时间,没问题了再全量上线,不要一下子全量升级,不然出了问题影响很大。

我们升级的时候,就是先在测试环境测了一周,改了几个不兼容的地方,然后灰度上了一台机器,观察了两天,没问题了才全量上线的,整个过程很顺利,没有出大问题。

五、学习资源推荐

最后,给想学习PHP 7.1的朋友推荐一些学习资源:

1. 官方文档

首选肯定是官方文档,PHP的官方文档写得非常好,很详细,有中文,是最好的学习资料:

  • PHP 7.1 新特性:https://www.php.net/manual/zh/migration71.new-features.php
  • 从PHP 7.0移植到7.1:https://www.php.net/manual/zh/migration71.php
  • PHP手册:https://www.php.net/manual/zh/

2. 官方RFC

如果想深入了解每个新特性的设计思路和讨论过程,可以看PHP的RFC,每个新特性都是先有RFC,讨论通过了才会加入PHP,RFC里有详细的设计、讨论、投票过程,能帮助你更深入地理解:

  • PHP RFC:https://wiki.php.net/rfc

3. 技术博客和社区

  • 廖雪峰的PHP教程:比较基础,适合入门。
  • 菜鸟教程的PHP教程:也是基础教程,适合入门。
  • SegmentFault、掘金、知乎的PHP板块:有很多PHP相关的技术文章和讨论。
  • PHP官方博客:https://www.php.net/,会发布PHP的版本更新和新闻。

4. 开源项目

看优秀的开源项目的代码,是学习新特性最好的方式之一,看看别人是怎么用的:

  • Laravel:最流行的PHP框架,代码质量很高,用了很多PHP新特性。
  • Symfony:另一个优秀的PHP框架,代码很规范。
  • Composer:PHP的包管理工具,代码也很优秀。

看这些项目的源码,学习他们的写法和最佳实践,应用到自己的项目中,进步会很快。

六、写在最后

PHP 7.1新特性学习路线:我是怎么入门的。

以上就是我学习PHP 7.1新特性的路线和经验,从为什么要升级,到PHP 7.1的重要新特性,到我的学习方法,到踩过的坑,到学习资源,做了一个比较全面的分享,希望能给想学习PHP 7.1的朋友一些参考。

PHP是一门一直在进步的语言,从PHP 5到PHP 7,再到现在的PHP 8,每一个版本都有很多新特性,性能越来越好,语法越来越现代,开发体验越来越好,作为PHP开发者,我们也要持续学习,跟上语言的发展,不要一直停留在老版本的写法上,要学会用新特性,写出更简洁、更健壮、更易维护的代码。

当然,学习新特性不是为了炫技,不是为了用而用,而是为了提升开发效率,提升代码质量,解决实际问题,所以在使用的时候,要根据实际场景,选择合适的特性,不要滥用,也不要为了用新特性而把代码搞得很复杂,简洁、清晰、易维护,才是最重要的。

我自己也还在学习中,PHP的新特性还有很多,PHP 8的联合类型、命名参数、匹配表达式、属性等,也都很值得学习,后面我也会继续学习,继续分享。

最后,用一句话结尾:"技术在不断发展,学习是一个持续的过程,保持好奇心,保持学习的热情,才能不被时代淘汰。"愿我们都能持续学习,不断进步,写出更好的代码,做出更好的产品。