项目由PHP5.5切换至PHP7.1.15
背景
从2015年鸟哥的技术分享,我们知道PHP7是对底层实现得一次完全重构,函数调用机制和内存管理等很多方便做了优化,使PHP性能有了很大的提升,至今PHP7经历了多个版本的迭代,常用的扩展和插件也基本做到兼容了PHP7,升级PHP7的难度越来越低。
随着闪贷业务的快速发展,推广业务和API接口的代码变得越来越复杂,页面响应时间变得越来越慢,一定程度上影响到用户的访问体验。因此最近花费了很多精力在优化网站的性能上,力求使用户得到更好的体验。升级 PHP 7 作为其中一个重要环节,取得了可观的效果,后端 web 服务器的负载和核心页面的响应时间都降低了很多。
PHP代码升级
从PHP官方升级文来看,PHP7相较于PHP5语法基本改变不大,做到了向后99%的兼容,但是项目中还是有很多编码不规范或者使用建议废除的函数导致的不兼容,使用的ThinkPHP3.1框架也有很多不兼容的地方:
-
preg_replace的e修正符的弃用,使用preg_replace_callback函数替代,例如:
preg_replace("/_([a-zA-Z])/e", "strtoupper('\\1')", $name);
替换为
preg_replace_callback('/_([a-zA-Z])/', function($r) {return strtoupper($r[1]);},$name)
- eregi函数的弃用,使用preg_match函数配合/i修正符替换
- ereg函数使用preg_match
- 类名称和方法名同名会被解析为构造函数,PHP7以后会报错,使用__construct替换
- 方法中必传参数没有传,PHP5会报警告,但是项目会继续执行,但是PHP7会直接报错误终止程序运行
-
PHP7对间接变量的解析顺序和PHP5的解析顺序不一致,若想保持一致使用{}保证解析顺序
$foo->$bar['baz'] // 解释为 ($foo->$bar)['baz'] 替换为$foo->{$bar['baz']}
$foo->$bar['baz']() // 解释为 ($foo->$bar)['baz']() 替换为$foo->{$bar['baz']}()
Foo::$bar['baz']() // 解释为 (Foo::$bar)['baz']() 替换为Foo::{$bar['baz']}()
使用 php7cc 辅助进行代码检查,php7cc 可以检查出来项目中大部分代码不兼容的情况,然后输出到文本中逐个文件做兼容处理。也可以接入项目打包测试流程做代码检测。
扩展替换
PHP7以后废弃了mysql和mongo扩展,项目中使用的ucenter使用的db类使用的是已经废弃的mysql扩展,使用msyqli扩展做兼容替换。
因为mongo扩展在项目中使用太多,而且和mongodb扩展的API一点都不兼容,若是切换必须重写整个底层访问接口,工程量太大,所以使用了 mongo-php-adapter,它是基于 ext-mongodb 的 API 写的 ext-mongo 的适配器,可以不对代码进行任何修改,就迁移到 ext-mongodb 上,迁移成本低了很多。
测试方案
和大部分公司差不多,项目组划分了线下开发环境、预发布环境和生产环境三个环境。一个需求从开发到上线,一般经过几个步骤:工程师在统一的线下开发环境完成开发和简单的测试;再部署到预发布环境,由 测试进行统一的测试和回归;最后,再正式部署到生产环境上。我们在升级部署 PHP 7 时,也是按这样的顺序递进的:
- 升级线下开发环境,包括扩展升级和代码升级,进行测试;
- 升级预发布环境,测试;
- 灰度测试生产环境,抓取错误日志分析,稳定运行一段时间,再全量升级生产环境。