编程

Laravel 13 升级指南

7 2026-03-20 18:50:00

预计升级时间:10 分钟

我们力求记录所有可能导致破坏性变更(Breaking Changes)的内容。鉴于其中部分变更仅涉及框架中较为隐蔽的角落,实际上可能只有一小部分变更会真正影响到您的应用程序。为了节省时间,你可以考虑使用 Shift。Shift 是一项由社区维护的服务,能够自动完成 Laravel 的版本升级工作。

利用 AI 进行升级

你可以通过 Laravel Boost 来实现升级过程的自动化。Boost 是一项第一方 MCP 服务,旨在为你的 AI 助手提供针对性的升级指导提示——只需将其安装至任意 Laravel 12 应用程序中,随后在 Claude Code、Cursor、OpenCode、Gemini 或 VS Code 编辑器内执行 /upgrade-laravel-v13 斜杠命令,即可启动向 Laravel 13 的升级流程。请注意,执行此命令需要安装 Laravel Boost ^2.0 或更高版本。

更新依赖项

受影响几率:高

你应当更新应用 composer.json 文件中的以下依赖项:

  • laravel/framework 升级到 ^13.0
  • laravel/boost 升级到 ^2.0
  • laravel/tinker 升级到 ^3.0
  • phpunit/phpunit 升级到 ^12.0
  • pestphp/pest 升级到 ^4.0

更新 Laravel Installer

如果你正使用 Laravel 安装 CLI 工具来创建新的 Laravel 应用,你应当更新该安装程序,以确保其与 Laravel 13.x 版本兼容。

如果你是通过 composer global require 命令安装的 Laravel installer,则可以使用 composer global update 命令来更新该安装程序:

composer global update laravel/installer

或者,如果你使用的是 Laravel Herd 内置的 Laravel installer 副本,你应当将 Herd 更新至最新版本。

缓存

缓存前缀及会话 Cookie 名

受影响几率:低

Laravel 的默认缓存和 Redis 键前缀现在统一使用连字符作为后缀。此外,默认的会话 Cookie 名称在处理应用名称时,现在统一使用 Str::snake(...) 方法进行格式化。

对于大多数应用而言,这一变更不会产生影响,因为应用层级的配置文件通常早已明确定义了这些数值。此项变更主要影响那些在缺少相应的应用配置值时,依赖于框架层级默认回退配置的应用。

如果你的应用依赖于这些自动生成的默认值,那么在升级之后,缓存键和会话 Cookie 的名称可能会发生变化:

/// Laravel <= 12.x
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_cache_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_database_';
Str::slug((string) env('APP_NAME', 'laravel'), '_').'_session';
 
// Laravel >= 13.x
Str::slug((string) env('APP_NAME', 'laravel')).'-cache-';
Str::slug((string) env('APP_NAME', 'laravel')).'-database-';
Str::snake((string) env('APP_NAME', 'laravel')).'_session';

要保留此前的行为,请在环境中显示配置 CACHE_PREFIXREDIS_PREFIXSESSION_COOKIE

StoreRepository 契约:touch

受影响几率:非常低

缓存契约现引入一个用于延长缓存项 TTL(生存时间)的 touch 方法。如果你维护着自定义的缓存存储实现,应当添加此方法:

// Illuminate\Contracts\Cache\Store
public function touch($key, $seconds);

缓存(Cache) serializable_classes 配置

受影响几率:中等

默认的应用缓存配置现在包含一个设置为 falseserializable_classes 选项。这会强化缓存反序列化行为,以帮助防止应用的 APP_KEY 泄露时遭受 PHP 反序列化 gadget chain 攻击。如果你的应用程序有意将 PHP 对象存储在缓存中,则应明确列出可能被反序列化的类:

'serializable_classes' => [
    App\Data\CachedDashboardStats::class,
    App\Support\CachedPricingSnapshot::class,
],

如果你的应用之前依赖于反序列化任意缓存对象,则需要将该用法迁移到显式类允许列表或非对象缓存有效负载(例如数组)。

容器

Container::call 和可空类(Nullable)默认值

受影响几率:低

Container::call 现在在没有绑定时会遵循可空类参数的默认值,与 Laravel 12 中引入的构造函数注入行为一致:

$container->call(function (?Carbon $date = null) {
    return $date;
});
 
// Laravel <= 12.x: Carbon instance
// Laravel >= 13.x: null

如果你的方法调用注入逻辑依赖于之前的行为,你可能需要更新它。

契约

Dispatcher 契约: dispatchAfterResponse

受影响几率:非常低

Illuminate\Contracts\Bus\Dispatcher 合约现在包含 dispatchAfterResponse($command, $handler = null) 方法。

如果你维护自定义调度器实现,请将此方法添加到你的类中。

ResponseFactory 契约: eventStream

受影响几率:非常低

The Illuminate\Contracts\Routing\ResponseFactory 合约现在包含 eventStream 签名。

如果你维持该合约的自定义实现,你应该添加该方法。

MustVerifyEmail 契约: markEmailAsUnverified

受影响几率:非常低

Illuminate\Contracts\Auth\MustVerifyEmail 合约现在引入 markEmailAsUnverified().

如果你有该合约的自定义实现,请添加该方法以维持兼容性。

数据库

MySQL DELETE 查询使用 JOIN, ORDER BYLIMIT

受影响几率:低

Laravel 现在会为 MySQL 语法编译完整的 DELETE ... JOIN 查询,包括 ORDER BYLIMIT 子句。

在之前的版本中,连接删除操作可能会忽略 ORDER BY / LIMIT 子句。在 Laravel 13 中,这些子句会被包含在生成的 SQL 语句中。因此,不支持此语法的数据库引擎(例如标准的 MySQL / MariaDB 变体)现在可能会抛出 QueryException 异常,而不是执行无限制删除操作。

Eloquent

模型启动和嵌套实例化

受影响几率:非常低

现在禁止在模型仍在启动时创建新的模型实例,否则将抛出 LogicException 异常。

这会影响在模型启动方法或 trait boot* 方法内部实例化模型的代码:

protected static function boot()
{
    parent::boot();
 
    // No longer allowed during booting...
    (new static())->getTable();
}

将此逻辑移出启动周期,以避免嵌套启动。

多态中间表名称生成

受影响几率:低

当 Laravel 使用自定义 Pivot 模型类推断多态 Pivot 模型的表名时,现在会生成复数形式的表名。

如果你的应用依赖于之前推断出的单数形式的 Pivot 表名,并且使用了自定义 Pivot 类,则应该在 Pivot 模型中显式定义表名。

集合模型序列化恢复了预先加载的关联

受影响几率:低

当 Eloquent 模型集合被序列化和恢复时(例如在队列作业中),集合中模型的预加载关系现在也会被恢复。

如果你的代码依赖于反序列化后关系不存在,你可能需要调整相关的逻辑。

HTTP 客户端

HTTP Client Response::throwthrowIf 签名

受影响几率:非常低

HTTP 客户端响应方法现在在方法签名中声明了回调参数:

public function throw($callback = null);
public function throwIf($condition, $callback = null);

如果你在自定义响应类中重写这些方法,请确保方法签名兼容。

通知

默认密码重置标题

受影响几率:非常低

Laravel 密码重置邮件默认主题改变:

// Laravel <= 12.x
Reset Password Notification

// Laravel >= 13.x
Reset your password

如果你的测试、断言或翻译覆盖项依赖于先前的默认字符串,请相应地进行更新。

队列通知与缺失模型

受影响几率:非常低

现在,已排队的通知会遵循通知类中定义的 #[DeleteWhenMissingModels] 注解和 $deleteWhenMissingModels 属性。

在之前的版本中,即使预期某些模型会被删除,缺失的模型仍然可能导致已排队的通知任务失败。

队列

JobAttempted 时间异常负载(Payload)

受影响几率:低

Illuminate\Queue\Events\JobAttempted 事件现在通过 $exception 暴露异常对象(null),取代之前的布尔值属性 $exceptionOccurred:

// Laravel <= 12.x
$event->exceptionOccurred;

// Laravel >= 13.x
$event->exception;

如果你监听了该事件,请相应地更新监听器代码。

QueueBusy 事件属性重命名

受影响几率:低

Illuminate\Queue\Events\QueueBusy 事件属性 $connection 已被重命名为 $connectionName 以保持与其他队列事件的一致性。

如果你的监听器音乐了 $connection,请将其更新为 $connectionName

Queue 合约方法补充

受影响几率:非常低

Illuminate\Contracts\Queue\Queue 合约现在包含了之前仅在文档块中声明的队列大小检查方法。

如果你维护了此合约的自定义队列驱动程序实现,请添加以下实现:

  • pendingSize
  • delayedSize
  • reservedSize
  • creationTimeOfOldestPendingJob

路由

域名路由注册优先级

受影响几率:低

在路由匹配中,具有显式域名的路由现在优先于无域名路由。

这使得所有子域名路由(包括所有子域名路由)的行为保持一致,即使非域名路由注册较早。如果你的应用程序依赖于域名路由和非域名路由之间的先前注册优先级,请检查路由匹配行为。

日程

withScheduling 注册时间

受影响几率:非常低

通过 ApplicationBuilder::withScheduling() 注册的调度现在会延迟到 Schedule 解析后再执行。

如果你的应用在启动过程中依赖于立即注册调度,则可能需要调整相关逻辑。

安全

请求伪造保护

受影响几率:高

Laravel 的 CSRF 中间件已从 VerifyCsrfToken 更名为 PreventRequestForgery,并且现在包含使用 Sec-Fetch-Site 标头的请求源验证。

VerifyCsrfTokenValidateCsrfToken 仍然是已弃用的别名,但应将直接引用更新为 PreventRequestForgery,尤其是在测试或路由定义中排除中间件时:

use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
 
// Laravel <= 12.x
->withoutMiddleware([VerifyCsrfToken::class]);
 
// Laravel >= 13.x
->withoutMiddleware([PreventRequestForgery::class]);

中间件配置 API 现在也提供了 preventRequestForgery(...)

支持

Manager extend 调用绑定

受影响几率:低

通过管理器 extend 方法注册的自定义驱动程序闭包现在绑定到管理器实例。

如果你之前在这些回调函数中使用了另一个绑定对象(例如服务提供者实例)作为 $this,则应该使用 use(...) 将这些值移到闭包捕获中。

Str 工厂在测试之间重置

受影响几率:低

Laravel 现在会在测试清理期间重置自定义字符串工厂。

如果你的测试依赖于在测试方法之间保留的自定义 UUID/ULID/随机字符串工厂,你应该在每个相关的测试或设置钩子中设置它们。

Js::from 默认使用未转义的 Unicode 字符

受影响几率:非常低

Illuminate\Support\Js::from 现在默认使用 JSON_UNESCAPED_UNICODE

如果你的测试或前端输出比较依赖于转义的 Unicode 序列(例如 \u00e8),请更新你的预期。

视图

分页 Bootstrap 视图名

受影响几率:低

Bootstrap 3 默认的内部分页视图名称现在是显式的:

// Laravel <= 12.x
pagination::default
pagination::simple-default

// Laravel >= 13.x
pagination::bootstrap-3
pagination::simple-bootstrap-3

如果你的应用直接引用了旧的分页视图名称,请更新这些引用。

其他事项

我们也建议你查看 laravel/laravel GitHub 代码库中的更改。虽然其中许多更改并非必需,但你可能希望保持这些文件与你的应用同步。本升级指南将涵盖部分更改,但其他更改(例如配置文件或注释的更改)则不会。你可以使用 GitHub 的比较工具轻松查看更改,并选择对你重要的更新。

 

下一篇