本文目录导读:

迁移老旧PHP项目到新环境是一个需要谨慎处理的过程,主要难点在于环境差异、PHP版本差异以及隐式依赖。
以下是一套经过验证的迁移流程和关键避坑指南,适用于PHP 5.x/4.x迁移到PHP 7.x/8.x或不同操作系统。
第一阶段:诊断与盘点(最重要的一步)
目标: 摸清项目底细,避免盲拆。
-
确认PHP版本与扩展:
- 在旧服务器上执行
php -v和php -m,记录当前PHP版本和所有已安装的扩展(如mysql,mysqli,gd,mcrypt,mbstring,openssl等)。 - 特别注意:
mysql_*函数在 PHP 7 中被移除,mcrypt在 PHP 7.2 中被移除,很多老项目依赖它们。
- 在旧服务器上执行
-
检查废弃/不兼容语法:
- 运行官方迁移工具:
php -l检查语法错误(仅检查单个文件)。 - 使用代码扫描工具(如 PHPCompatibility 或 Rector):它们能自动报告哪些代码在新版本中会报错或废弃。
- 重点查找:
mysql_connect(),each(),ereg(),split(),变量引用传参&,__autoload()等。
- 运行官方迁移工具:
-
识别隐藏依赖:
- 数据库:MySQL版本?使用了什么字符集(如
latin1还是utf8mb4)? - Web服务器:是否使用了
.htaccess(Apache)或nginx rewrite规则?是否有URL重写依赖? - 第三方服务:是否连接了旧版Redis/Memcached?
- 数据库:MySQL版本?使用了什么字符集(如
第二阶段:搭建一致的新环境(推荐使用容器化)
原则: 尽量模拟旧环境,然后再升级。
-
直接使用Docker(最推荐)
- 创建
docker-compose.yml,其中包含:- PHP镜像(先用旧版PHP,如
php:5.6-apache或php:7.0-fpm) - MySQL镜像(与旧版版本一致,如
mysql:5.6) - nginx/apache镜像
- PHP镜像(先用旧版PHP,如
- 目的:先保证在新机器上原样跑起来,确认不是你引入的新环境问题。
- 创建
-
手动安装(适用于生产固定环境)
- 安装特定版本的PHP(如 PHP 7.4,这是目前兼容性与老旧平衡较好的版本)。
- 安装所有旧版扩展(
mysqlnd,mysqli,gd,mbstring等)。 - 关键命令:
sudo apt-get install php7.4-mysql php7.4-gd php7.4-xml php7.4-mbstring php7.4-curl等。
第三阶段:迁移数据与文件
-
源代码:
- 使用
rsync或scp完整复制wwwroot目录,保持文件权限(chmod -R 755或644)。 - 特别注意上传目录(如
uploads/,attachments/),检查.htaccess或目录权限是否一致。
- 使用
-
数据库:
- 备份:
mysqldump -u root -p --all-databases --routines --events > old_db.sql - 字符集处理:如果你的旧库是
latin1而新库是utf8mb4,建议不直接导入,先用sed替换latin1为utf8mb4,或者使用mysql -h newhost -u user -p --default-character-set=utf8mb4 old_db < old_db.sql。 - 导入:在新服务器上创建同名数据库,
mysql -u root -p new_db < old_db.sql。
- 备份:
第四阶段:代码适配与修复(核心难点)
这是最耗时的步骤,按优先级处理:
-
优先处理致命错误:
mysql_*函数替换为mysqli_*或PDO。- 应急方案:如果不想或无法重写全部SQL,可以安装
mysql_*兼容扩展(如php5.6-mysql),但强烈不建议用在PHP 7+/8+上,性能和安全都有问题。
- 应急方案:如果不想或无法重写全部SQL,可以安装
each()函数替换为foreach ($array as $key => $value)。ereg()/eregi()替换为preg_match()。
-
修复隐式错误:
- 检查魔术引号:如果旧项目依赖
magic_quotes_gpc(PHP 5.4移除),需要在代码入口处手动stripslashes()一遍$_POST/$_GET/$_COOKIE。 - 检查
register_globals:如果旧项目依赖它,需要在代码中显式通过$_GET/$_POST获取变量,或者写一个兼容层(但建议直接重构)。 - 检查
session_start()位置:PHP 7以上要求session_start()必须在任何输出之前调用。
- 检查魔术引号:如果旧项目依赖
-
处理PHP 8.0+的严格类型:
- 如果直接跳到 PHP 8,需要关注:
gettype()返回值变化(如"integer"变成"int")。strpos()严格模式下返回值对比错误(如if (strpos($str, 'a') == false)应改为=== false)。
- 建议开启
error_reporting(E_ALL)和display_errors=On在开发环境,直接看报错。
- 如果直接跳到 PHP 8,需要关注:
第五阶段:测试与验证(别跳过)
-
基本冒烟测试:
- 访问首页、登录、注册、搜索、提交表单、上传文件。
- 测试数据库写入(新增、编辑、删除记录)。
- 测试文件上传功能(注意
php.ini的upload_max_filesize和post_max_size)。
-
日志监控:
- 查看
/var/log/nginx/error.log或/var/log/apache2/error.log以及 PHP的php_errors.log。
- 查看
-
性能测试(可选):
- 用
ab(ApacheBench)或siege压一下首页,看是否有明显的慢查询(对比旧环境)。
- 用
常见翻车点总结
| 问题 | 症状 | 解决方案 |
|---|---|---|
| 数据库连接失败 | 白屏或 Connection refused |
检查MySQL host(是否要写成 0.0.1 而不是 localhost),检查密码和权限。 |
| 编码乱码 | 页面中文乱码或问号 | 检查PHP文件编码(必须是UTF-8无BOM),检查数据库连接设置 SET NAMES utf8mb4,检查HTML的<meta charset="utf-8">。 |
| URL路由404 | 访问除首页外都404 | 检查 .htaccess 是否生效(Apache需启用 mod_rewrite),或nginx的 try_files 配置是否正确。 |
| 函数报错 | Call to undefined function mysql_connect() |
未安装/启用对应扩展,或PHP版本太高。 |
| Session失效 | 登录后跳转又回到登录页 | 检查 session.save_path 是否存在且可写,或者浏览器域名/cookie路径设置。 |
高效迁移建议
- 先在本地/测试环境用旧版PHP(如5.6)+新版系统(如Ubuntu 22.04)复现,排查掉系统库差异。
- 不要强行一步跳到PHP 8.2,建议路径:PHP 5.6 -> PHP 7.4 -> PHP 8.1,每步都运行通过再前进。
- 利用自动化工具:
- Rector:可以自动重构代码(如替换
each,mysql->mysqli)。 - PHPCompatibility:作为PHP CodeSniffer的规则,静态检查。
- Rector:可以自动重构代码(如替换
- 准备回滚方案:记录所有改动文件,上传前保留完整备份。
如果你能提供具体的旧PHP版本和数据库类型(如是否是mysql扩展),我可以给出更精确的命令行和代码调整示例。