PHP7.4和8.3在Laravel框架里兼容性怎么样

发布时间:2026/10/7 21:30:40
PHP7.4和8.3在Laravel框架里兼容性怎么样 前言我们这个 Laravel 项目现在跑在 PHP 7.4 上能不能直接换到 8.3这是很常见的一个问题也最容易得到错误答案。错误答案通常来自两种经验一种是我们线上跑了没问题另一种是改个版本号就行。前者不可复现后者忽略了两个硬约束。第一个硬约束来自 Laravel 本身每个 Laravel 主版本都有明确的 PHP 最低版本要求Laravel 8 支持 PHP 7.3 起Laravel 10 要求 PHP 8.1 起Laravel 11 要求 PHP 8.2 起。你的项目用的是哪个 Laravel直接决定了能不能把 PHP 换成 8.3——不是改个配置能解决的。第二个硬约束来自语言层从 7.4 到 8.3 跨了四个小版本期间有被移除的函数、有从警告变成错误的写法、有从返回 false变成抛异常exception的默认行为。这些改动里有相当一部分不会报错只会静默改变结果比如数字与字符串的比较规则、null传给内部函数的处理方式。本文给出两件事一张 Laravel 与 PHP 的对应关系表用来判断能不能升以及一分按版本归类的代码风险清单用来判断升了之后要改什么。一、先看硬约束Laravel 版本与 PHP 版本Laravel 版本PHP 最低要求能否运行在 PHP 8.3 上Laravel 67.2.5不适合目标环境是 7.2.5 ~ 7.4Laravel 87.3官方范围内不含 8.3需自行验证Laravel 98.0.2可行但仍建议升到更高主版本Laravel 108.1可行Laravel 118.2可行是较稳妥的选择Laravel 128.2可行表中的最低版本以框架官方发布说明为准补丁版本可能微调迁移前请再核对一次你项目锁定的具体版本。在 7.4 的项目上结论是清楚的PHP 7.4 到 8.3 这个跨度里Laravel 也必须跟着升主版本。想升到 PHP 8.3框架至少要达到能跑在 8.1 以上的版本也就是 Laravel 10 起。PHP 7.4 的安全支持已经在 2022 年底结束继续留在 7.4 上只是把风险往后推。至于 PHP 8.3 本身它在 2023 年底发布属于还在安全维护期内的版本支持周期临近结束具体截止日期以官方支持时间表为准。如果你的升级目标只是跑起来没问题8.3 是可用选项如果考虑长期维护直接规划到更新的版本更省事。二、判断能不能升的两条命令不要靠读composer.json猜Composer 自己就能给出答案。# 明确告诉我是哪个包不让我用 PHP 8.3 composer why-not php 8.3 # 校验当前依赖集合对平台的实际要求 composer check-platform-reqs # 看看框架本身要求的 PHP 范围 composer show laravel/framework | grep -i requires -A 5composer why-not php 8.3是最有价值的一条命令——它会把阻碍升级的包名和版本约束直接列出来比人工翻几十个依赖快得多。另外项目的composer.json里通常会有两处需要改{ require: { php: ^8.1 }, config: { platform: { php: 8.3.0 } } }config.platform.php的作用是即使你的本地开发机装的是 PHP 8.1Composer 也会按 8.3.0 来解析依赖避免本地能装、服务器装不上。这是一个能省掉大量沟通成本的设置。三、7.4 升到 8.3 会踩到的语言层变更下面这张表按症状是否明显排列越靠下越隐蔽。变更引入版本症状危险性未定义常量变成 Error8.0脚本直接中断明显不再抑制致命错误8.0以前被压住的错误全部暴露明显PDO 默认错误模式改为抛异常8.0查询失败从 false 变成未捕获异常明显mysqli默认报错模式改为抛异常8.1老数据库代码直接抛异常明显传 null 给非空内部函数参数被弃用8.1大量弃用日志中等整体写入$GLOBALS被禁止8.1直接报错明显动态属性被弃用8.2自定义类上写新属性会刷弃用日志中等utf8_encode/utf8_decode被弃用8.2弃用日志中等无参调用get_class()被弃用8.3弃用日志低数字与字符串比较规则变更8.0不报错结果变了高3.1 传 null 给内部函数Laravel 项目里最高发的一条这一条特别值得单列因为 Laravel 的取值方式天然会产出null?php // 需要 PHP 8.0用了 ?-与 Laravel 的请求对象 $name $request-input(name); // 字段不存在时返回 null // ❌ PHP 8.1 起Deprecated: strlen(): Passing null to parameter #1 of type string $len strlen($name); // ✅ 显式给默认值或先做类型转换 $len strlen((string) $request-input(name, ));同样的模式会出现在strlen()、strpos()、htmlspecialchars()、trim()、number_format()等一大批内部函数上。弃用提示本身不影响功能但两个问题会随之而来一是如果框架或监控把E_DEPRECATED当成异常处理请求会直接 500二是这些日志会淹没真正需要关注的错误。所以升级时应当把它当成必改项而不是以后再清理的技术债。3.2 实现内部接口的类需要补返回类型PHP 8.1 给一批内部接口如ArrayAccess、Countable、Iterator、IteratorAggregate加上了暂定返回类型tentative return types。自己写的类如果实现了这些接口却没声明返回类型升级后会出现弃用提示。老项目里自定义集合类、配置类最容易中招?php // 需要 PHP 8.0 class ConfigBag implements ArrayAccess, Countable { private array $items []; // ❌ PHP 8.1 起产生弃用提示没有声明与接口一致的返回类型 // public function offsetExists($offset) { return isset($this-items[$offset]); } // ✅ 声明返回类型 public function offsetExists(mixed $offset): bool { return isset($this-items[$offset]); } public function offsetGet(mixed $offset): mixed { return $this-items[$offset] ?? null; } public function offsetSet(mixed $offset, mixed $value): void { $this-items[$offset] $value; } public function offsetUnset(mixed $offset): void { unset($this-items[$offset]); } public function count(): int { return count($this-items); } }如果因为兼容多个版本的原因暂时没法加返回类型可以在方法上挂#[\ReturnTypeWillChange]属性PHP 8.1 引入来消除提示但更彻底的做法还是补上真实类型。注意mixed这个类型是 PHP 8.0 引入的。3.3 动态属性被弃用Eloquent 不受影响自定义类会中招PHP 8.2 起给未声明的属性赋值会产生弃用提示。很多人第一反应是那 Eloquent 模型不都要炸了吗——不会。Eloquent 模型通过__get、__set、__isset这些魔术方法处理字段访问走的是魔术方法路径不触发动态属性检查。真正会中招的是自己写的、没有魔术方法的数据类?php // 需要 PHP 8.0 class UserDto { public int $id; } $dto new UserDto(); $dto-id 1; // ❌ PHP 8.2 起Deprecated: Creation of dynamic property UserDto::$name is deprecated // $dto-name tom; // ✅ 方案一在类里把属性显式声明出来赋值就不会再有提示 class UserDtoV2 { public int $id 0; public string $name ; } $dto2 new UserDtoV2(); $dto2-name tom; // ✅ 方案二确实需要动态属性时显式声明意图PHP 8.2 引入的属性 #[\AllowDynamicProperties] class LegacyContainer { }判断方法很简单跑一遍全量回归在日志里搜Deprecated和dynamic property命中的类逐个处理。3.4 数字与字符串比较不报错的那一类这条之所以排在危险性最高是因为它完全静默?php var_dump(0 abc); // PHP 7.4true PHP 8.0 起false var_dump(1 01); // 两个版本都是 true都是数字字符串Laravel 项目里常见的写法是if ($request-input(type) 0)、in_array($id, $arrayOfStrings)。在 7.4 上恰好能进的分支升级后进不去了而且没有任何报错。迁移时把这类比较逐个改成严格比较是最省事的做法。代码实战一份兼容性自检脚本下面这个脚本面向 Laravel 项目用 Artisan 命令的方式把当前环境和代码里的风险点一起报出来。保存为app/Console/Commands/CompatCheck.php然后执行php artisan compat:check。?php // 需要 PHP 8.1 与 Laravel 10 namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use FilesystemIterator; class CompatCheck extends Command { protected $signature compat:check {--pathapp : 要扫描的目录}; protected $description 检查运行环境与代码中的 PHP 版本兼容风险; public function handle(): int { $this-info( 运行环境 ); $this-line(PHP 版本 : . PHP_VERSION); $this-line(Laravel 版本 : . app()-version()); $this-line(error_reporting : . error_reporting()); $this-newLine(); $this-info( 数据库与连接 ); $row DB::selectOne(SELECT VERSION() AS v); $this-line(MySQL 版本 : . ($row-v ?? 未知)); // 连接是否可用能跑通说明 PDO 的异常模式没把请求打断 try { DB::select(SELECT 1); $this-line(数据库连通 : OK); } catch (\Throwable $e) { $this-error(数据库连通 : 失败 —— . $e-getMessage()); } $this-newLine(); $this-info( 代码风险扫描 ); $patterns [ 空值传入内部函数8.1 弃用 /\b(strlen|strpos|trim|htmlspecialchars|number_format)\s*\(\s*\$/, 宽松比较8.0 规则变更 /[^!](?!)\s*[^]/, ]; $root base_path($this-option(path)); $hits 0; $it new RecursiveIteratorIterator( new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS) ); foreach ($it as $file) { if ($file-getExtension() ! php) { continue; } $lines file($file-getPathname(), FILE_IGNORE_NEW_LINES); foreach ($lines as $no $line) { foreach ($patterns as $label $re) { if (preg_match($re, $line)) { $hits; $this-line(sprintf( [%s] %s:%d %s, $label, $file-getPathname(), $no 1, trim($line) )); } } } } $this-newLine(); $this-line(共命中 {$hits} 处请逐条确认是否为真的风险点。); return self::SUCCESS; } }要注意脚本里的模式匹配只是初筛宽松比较那一条尤其容易误报命中的行必须人工确认。真正可信的信号来自运行期把error_reporting设为E_ALL、打开log_errors在预发环境跑一轮完整的业务回归然后按错误类型统计日志。常见坑点1. 只看框架能装不看依赖能不能装❌composer update报错就手动改composer.lock绕过平台检查✅ 先跑composer why-not php 8.3把阻碍升级的包逐个解决绕过平台检查装上去运行时照样出问题2. 把 Deprecated 当成不影响❌ 升级后页面上有弃用提示觉得无所谓先上线✅ 弃用提示意味着下个大版本会变成错误。而且如果监控把E_DEPRECATED当异常请求会直接 5003. 只测功能不测日志❌ 手工点几个页面都正常就当升级完成✅ 跑全量接口与后台任务统计日志里的Deprecated、Warning、Fatal error尤其是定时任务和队列消费者往往没人点4. 忽略数字与字符串比较❌ 参数校验里的全部保留✅ 升级前统一改成或显式类型转换。这条不报错只能在测试用例里发现5. 自定义类实现内部接口不声明返回类型❌ 升级到 8.1 后日志里全是 tentative return type 的弃用提示靠逐个忽略过日子✅ 补上返回类型短期可用#[\ReturnTypeWillChange]压制但这不是长期方案6. 本地与服务器 PHP 小版本不同❌ 开发机 8.3.1服务器 8.3.0测试结论不可迁移✅ 用config.platform.php固定解析基准并保证php -m的扩展清单在两端一致7. 升级期间同时改业务代码❌ 一边升 Laravel 主版本一边顺手重构业务模块出问题无法二分定位✅ 把升级拆成框架与依赖升级和业务改造两步各自独立提交、独立回归8. 忘记队列与计划任务❌ Web 请求全部正常queue:work因为 PHP 版本升级抛异常任务 silently 失败✅ 升级后手动触发一次队列消费和schedule:run并检查failed_jobs表总结问题结论Laravel 8 能跑 PHP 8.3 吗官方要求是 PHP 7.3 起8.3 需要自行验证稳妥做法是升到 Laravel 10 及以上升到 PHP 8.3 的前提框架版本必须满足最低要求依赖包也必须支持用composer why-not php 8.3确认最主要的代码改动空值传内部函数、实现内部接口要补返回类型、宽松比较改严格比较、动态属性显式声明最危险的一类变更不报错的那种比如0 abc的比较规则怎么验证预发环境跑全量回归统计日志里的 Deprecated 与 Fatal error别只看页面能不能打开兼容性问题的答案从来不是能或不能而是要改多少、改在哪里。判断路径可以固定下来先用composer why-not php 8.3解决依赖层的硬约束再用错误日志把语言层的问题一个个捞出来。7.4 到 8.3 的跨度里真正危险的不是那些会报错的地方——报错至少让你知道它在哪——而是那些安静改变行为的规则它们只会在某个业务场景里以数据不对的形式出现。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询