最近花了一个多月的时间,把公司一个运行了五年的老PHP系统,从PHP 5.6迁移到了PHP 7.2,并且做了兼容PHP 7.3的准备,整个过程踩了不少坑,也积累了一些经验。今天就来分享一下这次PHP迁移的实战经验,聊聊为什么要升级PHP 7.2/7.3,迁移前需要做哪些准备,迁移过程中会遇到哪些常见问题,怎么解决,迁移后怎么优化性能,以及一些注意事项和最佳实践,希望能给正在做或者准备做PHP升级的朋友一些参考,少走弯路,少踩坑。
一、为什么要升级PHP 7.2/7.3
在说迁移过程之前,先说说为什么要升级PHP 7.2/7.3,升级到底有什么好处。
我们公司的老系统,一直用的是PHP 5.6,跑了五年了,一直很稳定,本来不想升级的,但是最近遇到了一些问题,不得不升级。
首先是性能问题。PHP 5.6的性能确实不行,随着业务增长,用户量越来越大,系统的压力越来越大,服务器的CPU和内存使用率越来越高,响应速度越来越慢,高峰期经常出现超时。我们做了很多优化,数据库优化、缓存优化、代码优化,但是效果都有限,因为PHP本身的性能瓶颈在那里。后来我们测试了一下,同样的代码,在PHP 7.2上运行,性能比PHP 5.6快了一倍还多,内存占用也少了很多,这让我们下定决心升级。
然后是安全问题。PHP 5.6已经在2018年底停止官方支持了,也就是说,以后不会再有安全更新了,如果发现了安全漏洞,官方不会再修复,系统就会有安全风险。对于一个在线的业务系统来说,安全是第一位的,不能因为PHP版本的问题,导致系统被攻击,数据泄露,那损失就大了。所以,从安全的角度考虑,也必须升级到还在官方支持期内的PHP版本。
第三是新特性的问题。PHP 7.x引入了很多新特性,比如标量类型声明、返回类型声明、匿名类、null合并运算符、太空船运算符、常量数组、分组use声明等,这些新特性能让代码更规范、更安全、更易读。而且,很多新的PHP框架和库,已经不再支持PHP 5.6了,只支持PHP 7.x,如果不升级,就用不了这些新的框架和库,技术栈就会越来越落后。
第四是团队和招聘的问题。现在的PHP开发者,基本上都是从PHP 7开始学的,很多人甚至没有用过PHP 5.6,对PHP 5.6的一些特性和坑不熟悉。如果公司的系统还在用PHP 5.6,招聘的时候就会遇到困难,很多优秀的开发者不愿意来,因为技术栈太老了,学不到新东西,对职业发展不利。而且,团队内部的技术分享和学习,也会因为版本太老而受到限制。
综合考虑这些因素,我们决定把系统从PHP 5.6升级到PHP 7.2,并且做兼容PHP 7.3的准备,这样至少在未来两三年内,不需要再担心PHP版本的问题了。
二、迁移前的准备工作
升级PHP不是一件小事,不能说升就升,必须做好充分的准备,不然很容易出问题,影响线上业务。我们在正式迁移之前,做了大量的准备工作,下面分享一下。
1. 代码审计和兼容性检查
首先,我们对整个系统的代码做了全面的审计,检查有哪些代码在PHP 7.x下会有兼容性问题。PHP 7.x虽然大部分是向后兼容的,但是也有一些不兼容的变更,比如废弃的函数、变更的语法、移除的扩展等,如果代码里用了这些东西,升级之后就会报错。
我们用了几个工具来做兼容性检查:
- PHP_CodeSniffer:用PHPCompatibility标准,可以检查代码的PHP版本兼容性,能找出哪些代码在PHP 7.x下有问题。
- php7mar:PHP 7 Migration Assistant Report,专门用来检查代码从PHP 5.x迁移到PHP 7.x的兼容性问题。
- 手动检查:工具只能检查出一部分问题,还有很多问题需要手动检查,比如业务逻辑的变化、扩展的变化、配置的变化等。
通过代码审计,我们列出了所有的兼容性问题,按照严重程度分类,制定了修改计划,先改严重的,再改轻微的,确保所有问题都在迁移前解决。
2. 第三方依赖和扩展检查
除了自己写的代码,第三方依赖和扩展也需要检查。我们用了很多第三方库,比如Composer安装的库,这些库是不是支持PHP 7.2,有没有更新的版本,都需要检查。如果某个库不支持PHP 7.2,就需要升级到新版本,或者找替代方案。
PHP扩展也是一样,我们用了很多扩展,比如mysqli、pdo、gd、redis、memcached、imagick、xdebug等,这些扩展在PHP 7.2下有没有对应的版本,能不能正常编译安装,都需要检查。特别是一些比较冷门的扩展,可能没有及时更新,不支持PHP 7.2,就需要找替代方案,或者自己修改扩展代码。
我们把所有的第三方依赖和扩展都列了出来,逐一检查兼容性,对于不兼容的,提前做好升级或替换的准备,避免迁移的时候才发现问题,手忙脚乱。
3. 搭建测试环境
迁移不能直接在生产环境做,必须先在测试环境验证。我们搭建了一套和生产环境一样的测试环境,服务器配置、数据库版本、PHP版本(PHP 7.2)、扩展、第三方依赖,都和生产环境保持一致,然后把代码部署到测试环境,进行全面的测试。
测试包括:
- 单元测试:运行所有的单元测试,确保核心逻辑正确。
- 功能测试:对系统的所有功能进行测试,确保每个功能都能正常工作。
- 性能测试:进行压力测试,对比PHP 5.6和PHP 7.2的性能差异,确保性能有提升,没有性能瓶颈。
- 兼容性测试:测试各种边界情况、异常情况,确保系统在各种情况下都能正常运行。
我们在测试环境测了将近两周,发现了很多问题,都一一修复了,直到测试环境完全稳定,没有问题了,才开始准备生产环境的迁移。
4. 制定迁移方案和回滚方案
迁移之前,必须制定详细的迁移方案,包括迁移的时间、步骤、人员分工、验证方法等。而且,必须制定回滚方案,如果迁移过程中出现问题,怎么快速回滚到PHP 5.6,确保业务不中断。
我们的迁移方案是:
- 选择在业务低峰期(凌晨)进行迁移。
- 先把PHP 7.2的环境准备好,和PHP 5.6并存。
- 修改Nginx配置,把流量切到PHP 7.2。
- 观察系统运行情况,监控各项指标。
- 如果一切正常,迁移完成;如果出现问题,立即切回PHP 5.6,回滚。
回滚方案很简单,因为PHP 5.6的环境还在,只要把Nginx配置改回去,重启PHP-FPM,就能回滚到PHP 5.6,整个过程只需要几分钟,不会对业务造成太大影响。
5. 团队培训和沟通
迁移之前,我们还对团队进行了培训,让大家了解PHP 7.2的新特性、新语法,以及和PHP 5.6的区别,确保大家在迁移后能写出符合PHP 7.2规范的代码。同时,和产品、运营等相关部门沟通,告知迁移的时间和可能的影响,让大家有心理准备,配合迁移工作。
三、迁移过程中遇到的常见问题和解决方法
在迁移过程中,我们遇到了很多问题,下面分享一些最常见的问题,以及我们的解决方法,希望能帮大家避坑。
1. 废弃的函数和特性
PHP 7.x废弃和移除了很多函数和特性,如果代码里用了这些,升级之后就会报错,甚至无法运行。
我们遇到的主要有:
- mysql扩展:PHP 7.0已经移除了mysql扩展,只支持mysqli和pdomysql。我们的老系统里,还有一些地方用了mysql函数,升级之后直接报错。解决方法是把所有的mysql函数,替换成mysqli*函数,或者改成PDO,我们是统一改成了PDO,更规范,也更安全。
- ereg系列函数:ereg、eregi、ereg_replace等函数,在PHP 7.0中被移除了,需要用preg系列函数替代。我们把所有的ereg函数都改成了preg函数,注意正则表达式的语法也要相应调整。
- mcrypt扩展:PHP 7.1开始废弃mcrypt扩展,PHP 7.2中已经移除了。我们的系统里,有一些加密解密用了mcrypt,升级之后无法使用。解决方法是用openssl扩展替代mcrypt,或者用paragonie/halite等现代加密库,我们是改成了openssl,性能更好,也更安全。
- each函数:PHP 7.2开始废弃each函数,我们的代码里有几处用了each遍历数组,升级之后会有废弃警告。解决方法是用foreach替代each,这也是更推荐的写法。
- createfunction函数:PHP 7.2开始废弃createfunction,我们的代码里有一处用了create_function创建匿名函数,解决方法是用匿名函数(闭包)替代,这是PHP 5.3就有的特性,更安全,也更高效。
建议大家在迁移前,用PHP_CodeSniffer的PHPCompatibility标准检查一遍,能找出大部分废弃函数和特性的问题,然后逐一修复。
2. 语法变化和严格模式
PHP 7.x引入了一些语法变化,还有严格模式(declare(strict_types=1)),如果代码不规范,可能会有问题。
我们遇到的主要有:
- 类型声明:PHP 7.x支持标量类型声明(int、string、float、bool)和返回类型声明,如果开启了严格模式,类型不匹配会抛出TypeError。我们的老代码里,有些函数参数和返回值的类型不严格,比如传了字符串给int类型的参数,在PHP 5.6里会自动转换,但是在严格模式下会报错。解决方法是,要么不开启严格模式(默认是弱类型模式,会自动转换),要么规范代码,确保类型匹配。我们是逐步开启严格模式,先在新代码里用,老代码慢慢改。
- foreach的变化:PHP 7.x中,foreach的行为有一些变化,比如在循环中修改数组的内部指针,不再影响循环;在循环中给数组赋值,行为更一致。我们的老代码里,有几处依赖了PHP 5.6的foreach行为,升级之后逻辑不对了。解决方法是修改代码,不要依赖foreach的内部行为,用更规范的写法。
- list的变化:PHP 7.x中,list()的行为有变化,比如list()不再按照逆序赋值,而是按照顺序赋值;list()可以直接用于foreach循环。我们的代码里,有一处list()赋值依赖了逆序的行为,升级之后值不对了。解决方法是修改代码,明确指定list()的键,不要依赖顺序。
- 整数溢出的变化:PHP 7.x中,整数溢出的行为有变化,无效的八进制数会抛出解析错误,按位左移负数会抛出警告。我们的代码里,有一处八进制数写错了,在PHP 5.6里会被忽略,但是在PHP 7.x里会报错。解决方法是修正八进制数的写法。
3. 异常和错误处理的变化
PHP 7.x改变了错误处理机制,很多以前的错误(EERROR、EPARSE等),现在会抛出Error异常,可以用try-catch捕获。如果代码里的错误处理逻辑还是按照PHP 5.6的方式写的,可能会有问题。
我们遇到的主要有:
- 致命错误变成异常:在PHP 7.x中,调用不存在的方法、类型不匹配等,会抛出Error异常,而不是直接致命错误。如果代码里没有捕获这些异常,程序会终止,但是错误信息和PHP 5.6不一样。解决方法是,在关键的地方用try-catch捕获Error异常,或者设置全局的异常处理函数,统一处理。
- seterrorhandler的变化:PHP 7.x中,seterrorhandler的行为有变化,有些错误不再触发errorhandler,而是抛出异常。我们的老代码里,有一个全局的错误处理函数,用来记录错误日志,升级之后有些错误捕获不到了。解决方法是同时设置seterrorhandler和setexception_handler,分别处理错误和异常。
- @错误抑制符的变化:PHP 7.x中,@错误抑制符的行为有变化,被@抑制的错误,不再触发error_handler,但是会被异常处理捕获。我们的代码里,有几处用@抑制了可能的错误,升级之后行为不一样了。解决方法是尽量不要用@,而是用try-catch或者条件判断来处理可能的错误。
4. 扩展的变化
PHP 7.x中,有些扩展被移除了,有些扩展的API有变化,如果代码依赖了这些扩展,就会有问题。
我们遇到的主要有:
- 移除的扩展:除了前面说的mysql、mcrypt,还有一些扩展在PHP 7.x中被移除了,比如ereg、mssql、sybase_ct、oci8(旧版本)等。如果代码里用了这些扩展,需要找替代方案。
- 扩展API的变化:有些扩展虽然还在,但是API有变化,比如redis扩展、imagick扩展等,在PHP 7.x中,有些方法的参数和返回值有变化。我们的代码里,有几处redis的用法,在PHP 7.2的redis扩展下行为不一样了。解决方法是查看扩展的更新文档,修改代码,适配新的API。
- 扩展的兼容性:有些第三方扩展,可能没有及时更新,不支持PHP 7.2,编译安装的时候会报错。我们遇到了一个比较冷门的扩展,不支持PHP 7.2,找了很久都没有找到支持的版本。解决方法是,要么自己修改扩展代码,适配PHP 7.2,要么找替代方案,用其他扩展或者纯PHP实现替代。我们最后是找了一个替代的纯PHP库,解决了问题。
5. 性能问题和内存问题
虽然PHP 7.2的性能比PHP 5.6好很多,但是迁移之后,我们也遇到了一些性能和内存问题。
我们遇到的主要有:
- 内存占用增加:PHP 7.x中,有些数据结构的内存占用有变化,比如数组的内存占用,在某些情况下比PHP 5.6高。我们的系统里,有一处处理大数组的逻辑,迁移之后内存占用增加了不少,差点超出内存限制。解决方法是优化代码,避免一次性加载大数组,用生成器(Generator)或者分批处理,减少内存占用。
- GC的变化:PHP 7.x的垃圾回收机制有变化,回收的时机和效率不一样。我们的系统里,有一处长时运行的脚本,迁移之后内存泄漏了,因为PHP 7.x的GC没有及时回收。解决方法是在脚本中手动调用gccollectcycles(),或者优化代码,避免循环引用。
- OPcache的配置:PHP 7.x的OPcache有一些新的配置项,默认配置可能不是最优的。我们迁移之后,发现OPcache的命中率不高,性能没有完全发挥出来。解决方法是调整OPcache的配置,比如opcache.memoryconsumption、opcache.maxacceleratedfiles、opcache.validatetimestamps等,根据服务器的配置和代码量,调整到合适的值。
四、迁移后的性能优化
迁移到PHP 7.2之后,我们做了一些性能优化,让PHP 7.2的性能完全发挥出来。
1. 开启OPcache并优化配置
OPcache是PHP的字节码缓存,能大大提高PHP的性能,减少CPU占用。PHP 7.2默认安装了OPcache,但是默认配置可能不是最优的,我们做了以下优化:
- opcache.memory_consumption = 256(根据代码量调整,我们的代码量比较大,设了256MB)
- opcache.maxacceleratedfiles = 20000(根据文件数量调整)
- opcache.validate_timestamps = 0(生产环境关闭时间戳验证,避免每次请求都检查文件是否修改,提高性能,但是代码更新后需要重启PHP-FPM)
- opcache.revalidate_freq = 0(配合上面的配置)
- opcache.fast_shutdown = 1(开启快速关闭,提高性能)
- opcache.enable_cli = 1(CLI模式也开启OPcache,对长时运行的CLI脚本有帮助)
优化之后,OPcache的命中率达到了99%以上,性能提升很明显。
2. 升级PHP-FPM的配置
PHP-FPM的配置对性能也有很大影响,我们根据服务器的配置和业务特点,调整了PHP-FPM的配置:
- pm = dynamic(动态进程管理,适合流量变化大的场景)
- pm.max_children = 50(最大子进程数,根据服务器内存调整,每个PHP-FPM进程大概占用20-50MB内存)
- pm.start_servers = 10(启动时的进程数)
- pm.minspareservers = 5(最小空闲进程数)
- pm.maxspareservers = 20(最大空闲进程数)
- pm.max_requests = 500(每个进程处理多少请求后重启,避免内存泄漏)
调整之后,PHP-FPM的进程管理更合理,高峰期能及时扩容,低峰期能节省资源,性能和稳定性都有提升。
3. 代码层面的优化
除了配置优化,我们还在代码层面做了一些优化,充分利用PHP 7.2的新特性:
- 使用类型声明:给函数参数和返回值加上类型声明,能让PHP引擎做更多的优化,提高性能,同时也让代码更规范、更安全。
- 使用null合并运算符:用??替代isset()的判断,代码更简洁,性能也更好。
- 使用太空船运算符:用<=>做比较,在排序等场景下更高效。
- 使用常量数组:PHP 5.6之后支持常量数组,把一些配置数据定义成常量数组,比变量更高效。
- 优化数据库查询:减少不必要的查询,使用缓存,优化SQL语句,数据库往往是性能瓶颈,优化数据库比优化PHP代码效果更明显。
- 使用缓存:对热点数据使用Redis、Memcached缓存,减少数据库查询,提高响应速度。
4. 使用最新的稳定版PHP
我们迁移到PHP 7.2之后,也关注PHP 7.3的进展,PHP 7.3在性能上又有提升,特别是在GC、字符串处理、数组操作等方面,性能比PHP 7.2又提高了5-10%。我们在测试环境测试了PHP 7.3,兼容性很好,大部分代码不需要修改,所以我们计划在PHP 7.3稳定版发布之后,尽快升级到PHP 7.3,享受更好的性能。
五、注意事项和最佳实践
最后,分享一些PHP迁移的注意事项和最佳实践,都是我们踩坑之后总结出来的。
1. 不要跳过版本,逐步升级
如果你的系统还在用PHP 5.3、5.4这样的老版本,不要直接升级到PHP 7.2,最好逐步升级,先从5.3升到5.4,再升到5.5、5.6,然后再升到7.0、7.1、7.2。因为每个版本之间都有不兼容的变更,逐步升级能更容易发现和解决问题,避免一次性升级太多,问题太多,无从下手。
当然,如果你的系统代码比较规范,兼容性好,也可以直接升级到PHP 7.2,但是必须做好充分的测试,确保没有问题。
2. 先在测试环境充分测试,再上生产环境
这是最重要的一点,千万不要直接在生产环境升级PHP,必须先在测试环境充分测试。测试环境要尽量和生产环境保持一致,包括服务器配置、PHP版本、扩展、第三方依赖、数据库版本等。测试要全面,包括单元测试、功能测试、性能测试、兼容性测试,确保所有功能都正常,性能达标,没有兼容性问题,才能在生产环境升级。
3. 做好回滚方案,确保能快速回滚
升级之前,必须做好回滚方案,确保升级出问题的时候,能快速回滚到旧版本,减少对业务的影响。回滚方案要简单可靠,最好能在几分钟内完成回滚。我们的做法是,新旧PHP版本并存,通过Nginx配置切换流量,出问题了只要改配置重启,就能回滚,非常方便。
4. 选择业务低峰期升级
升级要选择业务低峰期,比如凌晨或者周末,这时候用户量少,即使出问题,影响也比较小。不要在业务高峰期升级,万一出问题,影响就大了。
5. 升级后密切监控,及时发现问题
升级之后,要密切监控系统的运行情况,包括错误日志、慢查询、CPU、内存、响应时间、错误率等指标,一旦发现异常,及时处理。最好在升级后的几天内,安排专人值班,确保出现问题能及时响应。
6. 逐步切换流量,不要一刀切
如果系统比较大,用户量比较多,可以采用灰度发布的方式,逐步切换流量,先切10%的流量到PHP 7.2,观察一段时间,没问题再切30%、50%,最后全部切过去。这样即使有问题,也只影响一小部分用户,风险可控。
7. 升级后更新代码规范,使用新特性
升级到PHP 7.x之后,要更新团队的代码规范,鼓励大家使用PHP 7.x的新特性,比如类型声明、返回类型声明、null合并运算符、匿名类等,让代码更规范、更安全、更高效。同时,要把废弃的函数和特性,加入代码检查规则,禁止在新代码中使用,逐步淘汰老的写法。
8. 持续关注PHP版本更新,及时升级
PHP的版本更新很快,每个版本都有性能提升和安全修复,要持续关注PHP的版本更新,在稳定版发布之后,及时评估和升级,不要让系统的PHP版本太老,避免出现安全风险和性能瓶颈。
六、写在最后
PHP 7.2/7.3迁移实战:从旧系统到新系统。
以上就是我们这次PHP迁移的实战经验,从为什么要升级,到迁移前的准备工作,到迁移过程中遇到的常见问题和解决方法,到迁移后的性能优化,再到注意事项和最佳实践,做了一个比较全面的分享。
总的来说,PHP 7.x的升级是非常值得的,性能提升明显,安全性更好,新特性丰富,生态也越来越完善。虽然迁移过程中会遇到一些问题,需要花一些时间和精力,但是长远来看,收益是很大的。如果你的系统还在用PHP 5.6或者更老的版本,建议尽快规划升级,不要等到官方停止支持了,出了安全问题才着急。
当然,升级PHP也不是一件小事,需要做好充分的准备,制定详细的计划,充分测试,确保平稳迁移,不要影响线上业务。希望我们的经验能帮到大家,少走弯路,少踩坑,顺利完成PHP升级。
最后,用一句话结尾:"技术在不断进步,我们也要不断学习,不断升级,才能跟上时代的步伐,不被时代淘汰。"愿我们都能保持学习的热情,不断提升自己的技术能力,写出更好的代码,做出更好的产品。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录