2015年12月,PHP 7正式发布。
当时整个PHP社区都沸腾了——PHP 7号称性能比PHP 5.6提升一倍,甚至能和HHVM媲美。对于我们这些PHPer来说,这简直是天大的好消息。不用改代码,性能就能翻倍,谁不心动?
但是心动归心动,真正要把线上项目从PHP 5.6升级到PHP 7,还是需要勇气的。毕竟线上项目稳定运行了很久,谁也不想因为升级搞出线上故障。
2016年中,我终于下定决心,把公司的一个中型项目从PHP 5.6升级到了PHP 7。整个过程花了大约两周,性能确实提升了很多,但是也踩了不少坑。
今天就来聊聊,我的PHP 7升级经历,以及那些你可能会踩到的坑。
一、为什么要升级PHP 7
在聊升级过程之前,先说说为什么要升级PHP 7。
1. 性能翻倍
这是PHP 7最大的卖点。PHP 7使用了全新的Zend Engine 3,对内存管理和数据结构做了大量优化。官方 benchmarks 显示,PHP 7的性能比PHP 5.6提升了约一倍,有些场景甚至提升了2-3倍。
实际项目中,我们的API接口响应时间平均下降了40%-50%,服务器CPU使用率下降了约30%。这意味着,同样的服务器,能承载更多的流量,节省了服务器成本。
2. 更低的内存占用
PHP 7对数据结构做了优化,同样的数据,内存占用比PHP 5.6少了约30%-50%。对于大数组、大对象的场景,内存节省更加明显。
3. 新的语言特性
PHP 7引入了很多新的语言特性,让代码更现代、更安全:
- 标量类型声明(int、string、bool、float)
- 返回值类型声明
- 匿名类
- null合并运算符(??)
- 太空船运算符(<=>)
- 常量数组
- 更多的Error可捕获
4. 长期支持
PHP 5.6在2018年底就停止支持了,而PHP 7有长期支持版本(PHP 7.0支持到2018年底,PHP 7.1支持到2019年底,PHP 7.2支持到2020年底)。升级到PHP 7,能获得更长时间的安全更新和bug修复。
5. 生态趋势
越来越多的PHP框架和库开始要求PHP 7+,比如Laravel 5.5+、Symfony 4+、PHPUnit 6+等。不升级PHP 7,就无法使用这些新版本的框架和库。
二、PHP 7的新特性
升级之前,先了解一下PHP 7的新特性,这样升级后才能用上这些新特性。
1. 标量类型声明
PHP 7支持标量类型声明(int、string、bool、float):
function sum(int $a, int $b): int {
return $a + $b;
}
sum(1, 2); // 3
sum('1', '2'); // 在严格模式下会报错,非严格模式下会转换为1+2=3默认是弱类型(会自动转换),如果要严格类型,需要在文件开头加declare(strict_types=1);。
2. 返回值类型声明
function getUser(): array {
return ['id' => 1, 'name' => '张三'];
}如果返回值类型不匹配,会报错。
3. null合并运算符(??)
// PHP 5
$name = isset($_GET['name']) ? $_GET['name'] : '默认';
// PHP 7
$name = $_GET['name'] ?? '默认';
// 还可以链式
$name = $_GET['name'] ?? $_POST['name'] ?? '默认';这个运算符非常实用,能让代码简洁很多。
4. 太空船运算符(<=>)
// 比较两个值,返回-1、0或1
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1
// 用于排序
usort($array, function($a, $b) {
return $a['price'] <=> $b['price'];
});5. 匿名类
$logger = new class {
public function log($msg) {
echo $msg;
}
};
$logger->log('hello');匿名类适合简单的一次性对象,不需要单独定义一个类。
6. 常量数组
define('CONFIG', [
'host' => 'localhost',
'port' => 3306,
]);
echo CONFIG['host']; // localhostPHP 5.6只支持const定义数组常量,PHP 7支持define定义数组常量。
7. 更多的Error可捕获
PHP 7中,很多以前的Fatal Error变成了可捕获的Error:
try {
$result = 1 / 0; // DivisionByZeroError
} catch (DivisionByZeroError $e) {
echo '错误:' . $e->getMessage();
}
try {
$obj = null;
$obj->method(); // Error: Call to a member function method() on null
} catch (Error $e) {
echo '错误:' . $e->getMessage();
}这让错误处理更加灵活。
三、升级前的准备
升级PHP 7之前,需要做一些准备工作,避免升级后出问题。
1. 检查PHP版本要求
首先,确认你的项目依赖的框架、库、扩展是否支持PHP 7。大部分主流框架和库都已经支持PHP 7,但是一些老旧的、不再维护的库可能不支持。
如果有不支持PHP 7的依赖,需要先升级或替换这些依赖。
2. 代码兼容性检查
使用PHP 7的兼容性检查工具,扫描代码中可能不兼容的地方:
- php7mar(PHP 7 Migration Assistant Report):官方推荐的迁移工具,能扫描代码中不兼容的地方
- PHP_CodeSniffer:配合PHP 7的标准,能检查代码兼容性
- Phan/PHPStan:静态分析工具,能发现类型相关的问题
我们用php7mar扫描了整个项目,发现了几十处可能不兼容的地方,大部分是小问题,但是有几处是必须修改的。
3. 测试环境升级
先在测试环境升级到PHP 7,跑一遍所有测试用例,确保功能正常。不要直接在生产环境升级,那样风险太大。
我们在测试环境升级后,跑了单元测试、集成测试、功能测试,发现了一些问题,修复后才在生产环境升级。
4. 备份
升级前,备份代码、数据库、配置文件,以防升级出问题可以回滚。
四、升级过程中踩的坑
接下来是重点——升级过程中踩的坑。
坑1:mysql扩展被移除
PHP 7移除了老旧的mysql扩展(mysql*函数),只保留了mysqli和PDOMySQL。
我们的项目中有一些老代码还在用mysqlquery()、mysqlconnect()等函数,升级到PHP 7后直接报错。
解决方案:
- 把所有
mysql函数替换为mysqli或PDO - 如果代码量大,可以先写一个兼容层(用mysqli封装mysql_*函数),临时过渡,然后逐步替换
我们项目中mysql扩展的使用不算太多,花了一天时间全部替换为mysqli。
坑2:ereg扩展被移除
PHP 7移除了ereg扩展(ereg_*函数),这些函数已经被废弃很久了,PHP 5.3就开始Deprecated了。
我们项目中有几处用了ereg()做正则匹配,升级后报错。
解决方案:
- 把
ereg()替换为preg_match() - 注意正则表达式的语法差异:ereg用的是POSIX正则,preg用的是PCRE正则,需要转换
// 旧代码
if (ereg('^[0-9]+$', $str)) { ... }
// 新代码
if (preg_match('/^[0-9]+$/', $str)) { ... }坑3:mcrypt扩展被废弃
PHP 7.1开始废弃mcrypt扩展,PHP 7.2移除。虽然PHP 7.0还能用mcrypt,但是已经不推荐了。
我们项目中用mcrypt做加密,升级后虽然能用,但是会有Deprecated警告。
解决方案:
- 用openssl扩展替换mcrypt
- 或者用paragonie/halite等现代加密库
// 旧代码(mcrypt)
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data, MCRYPT_MODE_CBC, $iv);
// 新代码(openssl)
$encrypted = openssl_encrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);坑4:不兼容的语法变化
PHP 7有一些语法变化,可能导致旧代码报错:
- foreach的list()语法变化:PHP 7中,
foreach ($array as list($a, $b))的行为有变化 - 十六进制字符串的数值转换:PHP 7不再把十六进制字符串当作数字
\u{}Unicode转义:PHP 7新增了Unicode转义语法,如果旧代码中有类似的字符串可能有问题global变量的变化:PHP 7中,global $$variable的行为有变化funcgetarg()等函数的变化:PHP 7中,这些函数返回的是当前值,而不是传入时的值
我们项目中遇到了十六进制字符串的问题——有一处代码用is_numeric('0x123')判断,PHP 5.6返回true,PHP 7返回false,导致逻辑错误。
解决方案:
- 仔细阅读PHP 7的迁移文档,了解所有不兼容的变化
- 用兼容性检查工具扫描代码
- 测试环境充分测试
坑5:第三方扩展兼容性
PHP 7需要重新编译所有第三方扩展,有些扩展可能不支持PHP 7。
我们项目中用了几个第三方扩展,其中一个比较冷门的扩展没有PHP 7版本,导致升级受阻。
解决方案:
- 升级前检查所有第三方扩展是否支持PHP 7
- 不支持的扩展,找替代品,或者自己编译适配
- 尽量减少对冷门扩展的依赖
我们最后把那个冷门扩展替换成了纯PHP实现的方案,虽然性能稍差,但是能兼容PHP 7。
坑6:opcache配置变化
PHP 7的opcache有一些新的配置项,而且默认配置和PHP 5.6有所不同。
升级后,我们发现opcache的命中率不高,性能没有达到预期。后来调整了opcache配置,性能才提升上来。
推荐的opcache配置:
[opcache]
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.max_wasted_percentage=10
opcache.revalidate_freq=60
opcache.validate_timestamps=1
opcache.save_comments=1
opcache.fast_shutdown=1注意:
opcache.memory_consumption:PHP 7建议至少128MB,PHP 5.6默认64MB可能不够opcache.maxacceleratedfiles:根据项目文件数量调整,大项目建议10000以上opcache.revalidate_freq:生产环境可以设大一些(如60秒),减少文件检查开销
坑7:错误处理变化
PHP 7中,很多Fatal Error变成了可捕获的Error。如果你的代码中有seterrorhandler()或者registershutdownfunction()做错误处理,可能需要调整。
我们项目中有一个全局错误处理,用registershutdownfunction()捕获Fatal Error。升级到PHP 7后,有些Error不再触发shutdown function,而是可以被try-catch捕获,导致错误处理逻辑不一致。
解决方案:
- 统一用try-catch捕获Error和Exception
- 调整
seterrorhandler()和registershutdownfunction()的逻辑 - 考虑用Whoops等错误处理库
坑8:大整数处理变化
PHP 7中,64位系统上的整数处理有变化。以前超出PHPINTMAX的整数会自动转为float,PHP 7中在某些场景下行为不同。
我们项目中有一处处理大整数的代码,升级后结果不一致,排查了很久才发现是这个问题。
解决方案:
- 处理大整数时,用字符串或BC Math扩展
- 不要依赖整数自动转换为float的行为
五、性能测试对比
升级完成后,我们做了性能测试,对比PHP 5.6和PHP 7的性能。
测试环境:
- 服务器:4核8G,SSD
- 压测工具:ab(Apache Bench)
- 测试接口:首页列表、文章详情、搜索API
测试结果:
| 接口 | PHP 5.6 (QPS) | PHP 7 (QPS) | 提升 |
|---|---|---|---|
| 首页列表 | 320 | 680 | 112% |
| 文章详情 | 450 | 920 | 104% |
| 搜索API | 180 | 350 | 94% |
平均QPS提升了约100%,也就是性能翻倍。
响应时间对比:
| 接口 | PHP 5.6 (平均响应时间) | PHP 7 (平均响应时间) | 下降 |
|---|---|---|---|
| 首页列表 | 120ms | 58ms | 52% |
| 文章详情 | 85ms | 42ms | 51% |
| 搜索API | 220ms | 110ms | 50% |
平均响应时间下降了约50%。
内存占用对比:
| 场景 | PHP 5.6 (内存峰值) | PHP 7 (内存峰值) | 下降 |
|---|---|---|---|
| 首页列表 | 8.5MB | 4.2MB | 51% |
| 文章详情 | 6.8MB | 3.5MB | 49% |
| 搜索API | 12MB | 6.5MB | 46% |
平均内存占用下降了约50%。
服务器资源对比:
- CPU使用率:PHP 5.6平均65%,PHP 7平均40%,下降约38%
- 内存使用率:PHP 5.6平均70%,PHP 7平均45%,下降约36%
性能提升确实非常明显,官方宣传的"性能翻倍"不是吹的。
六、升级建议
根据我的升级经验,给准备升级PHP 7的朋友几个建议:
1. 不要急,先做足准备
升级前,充分了解PHP 7的新特性和不兼容变化,用兼容性检查工具扫描代码,在测试环境充分测试。不要直接在生产环境升级。
2. 从小项目开始
如果有多个项目,先从一个小项目开始升级,积累经验,再升级大项目。不要一上来就升级核心项目。
3. 预留足够的时间
升级PHP 7不是改个配置就完事的,可能需要修改代码、替换扩展、调整配置。预留足够的时间,不要赶时间。
我们的中型项目花了约两周时间(一周准备+测试,一周修改+上线),大项目可能需要更久。
4. 做好回滚方案
升级前,做好回滚方案。如果升级后出问题,能快速回滚到PHP 5.6,减少影响。
我们用的是多服务器部署,先升级一台服务器,观察一段时间没问题,再逐步升级其他服务器。如果出问题,把流量切回PHP 5.6的服务器即可。
5. 升级后用上新特性
升级到PHP 7后,不要只是"能跑就行",要逐步用上PHP 7的新特性——标量类型声明、返回值类型声明、null合并运算符等。这些新特性能让代码更现代、更安全、更易维护。
6. 关注后续版本
PHP 7之后还有PHP 7.1、7.2、7.3、7.4等版本,每个版本都有新特性和性能优化。升级到PHP 7后,关注后续版本,适时升级。
七、写在最后
PHP 7升级,性能翻倍,但是也踩了不少坑。从mysql扩展移除、ereg扩展移除、mcrypt废弃,到语法变化、第三方扩展兼容性、opcache配置、错误处理变化,每一个坑都需要认真对待。
但是,这些坑都是值得的。升级到PHP 7后,性能提升明显,服务器资源占用下降,代码也能用上更多现代特性。对于PHP项目来说,升级PHP 7是性价比最高的优化之一。
如果你还在使用PHP 5.6,我强烈建议你尽快升级到PHP 7。PHP 5.6已经停止支持了,继续使用有安全风险。而且,PHP 7的性能提升和新特性,能让你的项目更上一层楼。
最后,用一句话总结:PHP 7升级,痛并快乐着。痛的是踩坑,快乐的是性能翻倍。
愿每一个PHPer,都能顺利升级到PHP 7,享受性能翻倍的快乐。愿PHP这门"世界上最好的语言",在PHP 7之后越来越好。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录