高影响变更

  • 更新依赖项
  • 更新 Laravel 安装器
  • 请求伪造防护

中影响变更

  • 缓存 serializable_classes 配置
  • 使用 MySQL 或 MariaDB 的数据库 upsert

低影响变更

  • 缓存前缀与会话 Cookie 名称
  • 集合模型序列化还原预加载关系
  • Container::call 与可空类默认值
  • 域路由注册优先级
  • JobAttempted 事件异常载荷
  • Manager extend 回调绑定
  • 带有 JOIN、ORDER BY 和 LIMIT 的 MySQL DELETE 查询
  • 分页 Bootstrap 视图名称
  • 多态中间表名称生成
  • QueueBusy 事件属性重命名
  • 会话 serialization 配置
  • 测试间重置 Str 工厂

从 12.x 升级到 13.0

预计升级时间:10 分钟

我们尝试记录每一个可能的破坏性变更。由于部分破坏性变更位于框架较为冷门的部分,实际上只有一部分变更可能会影响您的应用。为节省时间,您可以使用 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 安装器

如果您正在使用 Laravel 安装器 CLI 工具创建新的 Laravel 应用,则应更新您的安装器安装以兼容 Laravel 13.x。

如果您是通过 composer global require 安装 Laravel 安装器的,可以使用 composer global update 更新安装器:

composer global update laravel/installer

或者,如果您使用的是 Laravel Herd 自带的 Laravel 安装器,应将您的 Herd 安装更新至最新版本。

缓存

缓存前缀与会话 Cookie 名称

影响可能性:低

Laravel 的默认缓存和 Redis 键前缀现在使用带连字符的后缀。

在大多数应用中,此变更不会生效,因为应用级配置文件已经定义了这些值。该变更主要影响在对应应用配置值不存在时、依赖框架级回退配置的应用。

如果您的应用依赖这些生成的默认值,升级后缓存键与会话 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::slug((string) env('APP_NAME', 'laravel')).'-session';

如需保留之前的行为,请在环境变量中显式配置 CACHE_PREFIX、REDIS_PREFIX 和 SESSION_COOKIE。

Store 与 Repository 契约:touch

影响可能性:极低

缓存契约现在包含一个 touch 方法,用于延长条目的 TTL。如果您维护自定义缓存存储实现,应添加此方法:

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

缓存 serializable_classes 配置

影响可能性:中

默认应用 cache 配置现在包含一个设置为 false 的 serializable_classes 选项。这会加强缓存反序列化行为,以便在您的应用 APP_KEY 泄露时帮助防止 PHP 反序列化 gadget 链攻击。如果您的应用有意将 PHP 对象存储在缓存中,应明确列出允许反序列化的类:

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

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

容器

Container::call 与可空类默认值

影响可能性:低

当不存在绑定时,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) 方法。

如果您维护自定义 dispatcher 实现,请在类中添加此方法。

ResponseFactory 契约:eventStream

影响可能性:极低

Illuminate\Contracts\Routing\ResponseFactory 契约现在包含一个 eventStream 签名。

如果您维护该契约的自定义实现,应添加此方法。

MustVerifyEmail 契约:markEmailAsUnverified

影响可能性:极低

Illuminate\Contracts\Auth\MustVerifyEmail 契约现在包含 markEmailAsUnverified()。

如果您提供了该契约的自定义实现,请添加此方法以保持兼容。

数据库

使用 MySQL 或 MariaDB 的数据库 upsert

影响可能性:中

Laravel 现在会校验调用方为 uniqueBy 提供了非空值,并会抛出 InvalidArgumentException,而不是生成无效 SQL。

尽管 MariaDB 和 MySQL 数据库驱动会忽略 uniqueBy 的值并始终使用表的主键和唯一索引来检测已存在的记录,该校验仍然适用。如果 uniqueBy 为空,将抛出 InvalidArgumentException。

带有 JOIN、ORDER BY 和 LIMIT 的 MySQL DELETE 查询

影响可能性:低

Laravel 现在会为 MySQL 语法编译包含 ORDER BY 和 LIMIT 的完整 DELETE ... JOIN 查询。

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

Eloquent

模型引导与嵌套实例化

影响可能性:极低

在模型仍在引导时创建新的模型实例现已被禁止,并会抛出 LogicException。

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

protected static function boot()
{
    parent::boot();

    // No longer allowed during booting...
    (new static())->getTable();
}

请将此逻辑移出引导周期,以避免嵌套引导。

多态中间表名称生成

影响可能性:低

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

如果您的应用之前依赖多态中间表的单数推断名称,并使用了自定义中间表类,应显式定义中间表模型上的表名。

集合模型序列化还原预加载关系

影响可能性:低

当 Eloquent 模型集合被序列化并还原时(例如在排队任务中),现在会为集合中的模型还原预加载的关系。

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

HTTP 客户端

HTTP 客户端 Response::throw 和 throwIf 方法签名

影响可能性:极低

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

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

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

通知

默认密码重置主题

影响可能性:极低

Laravel 的默认密码重置邮件主题已更改:

// Laravel <= 12.x
重置密码通知

// Laravel >= 13.x
重置你的密码

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

队列通知与缺失模型

影响可能性:极低

队列通知现在会遵循在通知类上定义的 #[DeleteWhenMissingModels] 特性以及 $deleteWhenMissingModels 属性。

在之前的版本中,即使期望缺失的模型被删除,它们仍可能导致队列通知任务失败。

队列

JobAttempted 事件异常载荷

影响可能性:低

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

路由

域名路由注册优先级

影响可能性:低

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

这使得全匹配子域名路由即使在非域名路由更早注册的情况下,也能保持一致的行为。如果你的应用依赖此前域名路由与非域名路由之间的注册优先级,请审查路由匹配行为。

会话

会话 serialization 配置

影响可能性:低

为了帮助防止 PHP 反序列化 gadget 链攻击,默认应用骨架现在在 config/session.php 文件中将会话 serialization 选项设置为 json。

如果你正在升级现有应用并同步配置文件到 Laravel 13 骨架,将此值从 php 更新为 json 会导致所有活跃的用户会话失效。

如果你希望在升级过程中无缝保持活跃会话,应确保此值保持为 php。不过,如果你的应用不在会话中存储 PHP 对象,并且你愿意要求用户重新验证身份,我们建议将此值更新为 json,以提高安全性。

调度

withScheduling 注册时机

影响可能性:极低

通过 ApplicationBuilder::withScheduling() 注册的调度现在会延迟到 Schedule 被解析时。

如果你的应用依赖引导阶段立即注册调度的时机,可能需要调整相应逻辑。

安全

请求伪造防护

影响可能性:高

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

VerifyCsrfToken 和 ValidateCsrfToken 仍作为弃用的别名保留,但应将直接引用更新为 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(...)。

支持

管理器 extend 回调绑定

影响可能性:低

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

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

Str 工厂在测试间重置

影响可能性:低

Laravel 现在会在测试销毁阶段重置自定义的 Str 工厂。

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

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

影响可能性:极低

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

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

工具

Symfony PHP 8.5 Polyfill 与全局函数冲突

影响可能性:低

Laravel 13 引入了对 symfony/polyfill-php85 的依赖。在低于 8.5 的 PHP 版本上,除非这些全局函数在引导过程中已提前定义,否则该 polyfill 会定义诸如 array_first() 和 array_last() 等全局函数。

这些函数可能会与诸如 laravel/helpers 之类的旧版辅助包或使用相同名称的自定义全局辅助函数产生冲突。例如,历史上的 array_first() 辅助函数接受一个回调以返回第一个匹配的元素,而 polyfill 版本仅返回数组的第一个元素。

为了避免冲突并确保跨 PHP 版本的行为一致,建议优先使用 Illuminate\Support\Arr 的方法:

use Illuminate\Support\Arr;

Arr::first($array, function ($value) {
  return /* condition */;
});

视图

分页 Bootstrap 视图名称

影响可能性:低

Bootstrap 3 默认设置的内部分页视图名称现在变得更为明确:

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

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

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

其他

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