兰空图床 PHP 8.0 升级 PHP 8.2 后 500 错误:Carbon 兼容性问题排查与修复

最近对服务器上的兰空图床(Lsky Pro)进行 PHP 版本升级,将 PHP 8.0 升级到了 PHP 8.2.28

升级完成后,网站出现了 HTTP 500 错误。

经过逐步排查,最终发现问题主要涉及两个方面:

  1. PHP 8.0 升级到 PHP 8.2 后,Nginx 仍然指向旧的 PHP-FPM Socket。
  2. PHP-FPM 修复后,兰空图床使用的旧版本 Carbon 与 PHP 8.2 存在兼容性问题。

本文记录整个排查和修复过程。


一、运行环境

本次故障环境:

操作系统:OpenCloudOS
Web Server:Nginx
PHP:8.0 → 8.2.28
PHP-FPM:PHP 8.2
项目:兰空图床(Lsky Pro)
框架:Laravel
日期处理库:nesbot/carbon

项目目录:

/www/wwwroot/img.hecady.com

网站实际入口:

/www/wwwroot/img.hecady.com/public/index.php

使用的域名:

img.hecady.com
cdn-img.hecady.com

二、PHP 8.0 升级到 PHP 8.2 后网站出现 500

升级 PHP 后,访问兰空图床出现:

HTTP 500 Internal Server Error

首先没有直接修改兰空图床代码,而是从 Web Server 到 PHP,再到 Laravel 应用层逐级排查。


三、检查 PHP-FPM Socket

PHP 8.0 时使用:

/tmp/php-cgi-80.sock

升级到 PHP 8.2 后使用:

/tmp/php-cgi-82.sock

首先检查 PHP-FPM:

ps -ef | grep -E 'php-fpm|php-cgi' | grep -v grep

检查 Socket:

ls -lah /tmp/php-cgi-*.sock

也可以:

ss -lx | grep php-cgi

确认 PHP 8.2 的 PHP-FPM 已经正常运行,并且:

/tmp/php-cgi-82.sock

存在。


四、检查 Nginx 配置

继续检查 Nginx:

nginx -T

最终确认兰空图床的 PHP 配置:

location ~ [^/]\.php(/|$)
{
    try_files $uri =404;
    fastcgi_pass unix:/tmp/php-cgi-82.sock;
    fastcgi_index index.php;
    include fastcgi.conf;
    include pathinfo.conf;
}

这里需要特别注意。

PHP 8.0:

/tmp/php-cgi-80.sock

PHP 8.2:

/tmp/php-cgi-82.sock

如果 PHP 已经升级到 8.2,但是 Nginx 仍然连接:

/tmp/php-cgi-80.sock

就会出现:

connect() to unix:/tmp/php-cgi-80.sock failed

因此升级 PHP 后,一定要同时检查 PHP-FPM Socket 和 Nginx 的 fastcgi_pass

修改完成后执行:

nginx -t

确认:

syntax is ok
test is successful

五、测试 Nginx 到 PHP-FPM 是否正常

为了排除 Nginx 和 PHP-FPM 问题,可以临时创建一个 PHP 测试文件。

进入兰空图床目录:

cd /www/wwwroot/img.hecady.com

创建:

cat > public/test.php <<'EOF'
<?php
phpinfo();
EOF

然后访问:

https://你的域名/test.php

如果能够正常显示 PHPInfo 页面,说明:

Nginx
 ↓
PHP-FPM 8.2
 ↓
PHP

整个 PHP 执行链路正常。

测试完成后立即删除:

rm -f public/test.php

六、真正的问题:兰空图床 Carbon 报错

确认 Nginx 和 PHP-FPM 正常后,继续查看兰空图床的 Laravel 错误日志。

最终发现真正的异常:

Carbon\Carbon::setLastErrors():
Argument #1 ($lastErrors) must be of type array, bool given

错误文件:

/www/wwwroot/img.hecady.com/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php

这说明:

Nginx 正常
PHP-FPM 正常
PHP 8.2 正常
↓
兰空图床 Laravel 应用层出现异常
↓
Carbon 兼容性问题

七、Carbon 与 PHP 8.2 的兼容性问题

检查 Carbon 源码后,发现第一处代码:

self::setLastErrors(parent::getLastErrors());

问题在于:

parent::getLastErrors()

在某些情况下可能返回:

false

但是 setLastErrors() 需要的是数组。

于是出现:

false
 ↓
setLastErrors(false)
 ↓
TypeError
 ↓
HTTP 500

这就是本次兰空图床升级 PHP 8.2 后出现 500 的核心原因。


八、修复 Carbon 第一处代码

修改之前先备份:

cd /www/wwwroot/img.hecady.com

cp vendor/nesbot/carbon/src/Carbon/Traits/Creator.php \
vendor/nesbot/carbon/src/Carbon/Traits/Creator.php.bak

将:

self::setLastErrors(parent::getLastErrors());

修改为:

self::setLastErrors(parent::getLastErrors() ?: [
    "warning_count" => 0,
    "warnings" => [],
    "error_count" => 0,
    "errors" => []
]);

也可以直接执行:

sed -i 's/self::setLastErrors(parent::getLastErrors());/self::setLastErrors(parent::getLastErrors() ?: ["warning_count" => 0, "warnings" => [], "error_count" => 0, "errors" => []]);/' \
/www/wwwroot/img.hecady.com/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php

修改完成后,兰空图床前台恢复正常。


九、登录兰空图床后台后再次出现错误

前台恢复后,继续登录兰空图床后台测试。

结果后台又出现相同类型的错误:

Carbon\Carbon::setLastErrors():
Argument #1 ($lastErrors) must be of type array, bool given

继续检查:

sed -n '630,675p' \
/www/wwwroot/img.hecady.com/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php

发现另外一处:

$lastErrors = parent::getLastErrors();

后续又执行:

$instance::setLastErrors($lastErrors);

因此这里同样存在:

false → setLastErrors(false)

的问题。


十、修复 Carbon 第二处代码

将:

$lastErrors = parent::getLastErrors();

修改为:

$lastErrors = parent::getLastErrors() ?: [
    "warning_count" => 0,
    "warnings" => [],
    "error_count" => 0,
    "errors" => []
];

也可以直接执行:

cd /www/wwwroot/img.hecady.com

sed -i 's/\$lastErrors = parent::getLastErrors();/\$lastErrors = parent::getLastErrors() ?: ["warning_count" => 0, "warnings" => [], "error_count" => 0, "errors" => []];/' \
vendor/nesbot/carbon/src/Carbon/Traits/Creator.php

检查:

grep -n -C 2 "getLastErrors" \
/www/wwwroot/img.hecady.com/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php

确认两处代码都已经处理。


十一、清理兰空图床 Laravel 缓存

修改完成后清理 Laravel 缓存:

cd /www/wwwroot/img.hecady.com

php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear

然后重启 PHP-FPM:

systemctl restart php-fpm-82

重新测试:

兰空图床前台
↓
用户登录
↓
图片上传
↓
后台管理

最终网站前台和后台均恢复正常。


十二、Primary script unknown 并不是本次故障根因

排查 Nginx 日志时,还发现:

Primary script unknown
GET /webroot/index.php/_environment

但是兰空图床实际入口是:

/www/wwwroot/img.hecady.com/public/index.php

并不存在:

/webroot/index.php

因此这类请求更符合外部扫描或路径探测。

所以:

Primary script unknown 并不是本次兰空图床 500 错误的主要原因。

排查服务器日志时,不应该看到某一条错误就直接认定它是根因,需要结合项目实际目录、Nginx 配置、请求路径和异常堆栈综合判断。


十三、为什么不建议直接执行 composer update

发现 Carbon 兼容性问题后,不建议直接执行:

composer update

因为兰空图床属于 Laravel 项目,Composer 可能同时更新:

Carbon
Laravel
Symfony
Illuminate
Guzzle
Monolog

最终可能导致整个项目产生新的依赖冲突。

建议先检查当前版本:

composer show nesbot/carbon

查看 Laravel:

composer show laravel/framework

也可以检查:

grep -A5 -B2 '"name": "nesbot/carbon"' composer.lock

以及:

grep -A5 -B2 '"name": "laravel/framework"' composer.lock

确认当前兰空图床版本和依赖关系后,再制定正式升级方案。


十四、直接修改 vendor 只是临时解决方案

本次为了快速恢复兰空图床正常访问,直接修改了:

vendor/nesbot/carbon/src/Carbon/Traits/Creator.php

这种方式可以作为生产环境故障时的紧急修复方案,但并不是长期最佳方案。

因为以后执行:

composer install

或者重新部署兰空图床时,vendor 目录可能会重新生成,之前修改的代码也可能消失。

长期解决方案应该是:

升级兼容 PHP 8.2 的 Carbon 版本

同时确认当前兰空图床版本对应的 Laravel 是否支持 PHP 8.2。


十五、PHP 升级兰空图床前需要注意什么?

以后升级兰空图床 PHP 版本,建议提前检查:

PHP 版本

php -v

Laravel 版本

composer show laravel/framework

Carbon 版本

composer show nesbot/carbon

PHP-FPM

ps -ef | grep -E 'php-fpm|php-cgi' | grep -v grep

PHP-FPM Socket

ls -lah /tmp/php-cgi-*.sock

Nginx 配置

nginx -T

重点检查:

fastcgi_pass

是否指向当前 PHP 版本对应的 Socket。

同时建议提前备份:

兰空图床项目文件
数据库
composer.json
composer.lock
Nginx 配置
PHP 配置

十六、本次兰空图床故障完整流程

整个故障可以概括为:

兰空图床
    ↓
PHP 8.0
    ↓
升级到 PHP 8.2
    ↓
Nginx 仍连接 PHP 8.0 Socket
    ↓
修复 fastcgi_pass
    ↓
PHP-FPM 8.2 正常
    ↓
兰空图床继续出现 500
    ↓
查看 Laravel 错误日志
    ↓
发现 Carbon TypeError
    ↓
DateTime::getLastErrors() 返回 false
    ↓
旧 Carbon 直接传递给 setLastErrors()
    ↓
setLastErrors(false)
    ↓
TypeError
    ↓
修复 Carbon 第一处代码
    ↓
前台恢复
    ↓
登录后台再次触发第二处代码
    ↓
修复 Carbon 第二处代码
    ↓
清理 Laravel 缓存
    ↓
重启 PHP-FPM
    ↓
兰空图床前台、后台恢复正常

十七、总结

这次兰空图床从 PHP 8.0 升级到 PHP 8.2 后出现 500,实际上经历了两个阶段。

第一阶段是 PHP-FPM Socket 切换问题

PHP 8.0
↓
/tmp/php-cgi-80.sock

升级 PHP 8.2 后变成:

/tmp/php-cgi-82.sock

如果 Nginx 没有同步修改 fastcgi_pass,就会导致 PHP 请求无法正常转发。

第二阶段是 Carbon 与 PHP 8.2 的兼容性问题

旧版本 Carbon 中存在:

parent::getLastErrors()

直接传递给:

setLastErrors()

的代码。

getLastErrors() 返回 false 时,就会触发:

Argument #1 ($lastErrors) must be of type array, bool given

最终导致兰空图床返回 HTTP 500。

本次通过临时修复 Carbon 两处代码、清理 Laravel 缓存并重启 PHP-FPM,最终恢复了兰空图床前台和后台。

不过从长期维护角度来看,直接修改 vendor 只是临时方案。更推荐根据当前兰空图床版本,升级到兼容 PHP 8.2 的 Carbon 和 Laravel 依赖版本。

这次故障也再次说明:

PHP 升级不能只关注 PHP 本身,还需要同时检查 Nginx、PHP-FPM、Laravel、Carbon 以及 Composer 依赖之间的兼容性。

对于运行中的兰空图床,升级 PHP 前最好先做好完整备份,并确认当前兰空图床版本与目标 PHP 版本之间的兼容性。

上一篇