Skip to content

升級指南

高影響變更

中影響變更

低影響變更

從 12.x 升級至 13.0

預計升級時間:10 分鐘

📌 備註

我們嘗試記錄每一個可能的破壞性變更 (Breaking Change)。由於其中某些破壞性變更位於框架中較冷門的部分,因此實際上只有一部分變更會影響您的應用程式。為了節省時間,您可以使用 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 來更新安裝程式:

shell
composer global update laravel/installer

或者,如果您正使用 Laravel Herd 內建的 Laravel 安裝程式,則應將您的 Herd 安裝更新至最新版本。

快取

影響可能性:低

Laravel 的預設快取與 Redis 鍵名前綴現在使用連字號 (Hyphen) 作為後綴。

在大多數應用程式中,這項變更不會產生影響,因為應用程式層級的設定檔已經定義了這些值。這主要會影響那些依賴框架層級備用 (Fallback) 設定(即應用程式設定值不存在時)的應用程式。

如果您的應用程式依賴這些自動產生的預設值,升級後快取鍵名與 Session Cookie 名稱可能會改變:

php
// 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_PREFIXREDIS_PREFIXSESSION_COOKIE

StoreRepository 契約(Contracts):touch

影響可能性:極低

快取契約(Contracts) 現在包含一個 touch 方法,用於延長項目的存活時間 (TTL)。如果您有維護自定義的快取儲存實作,則應新增此方法:

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

快取 serializable_classes 設定

影響可能性:中

應用程式預設的 cache 設定現在包含一個設為 falseserializable_classes 選項。這強化了快取反序列化 (Unserialization) 行為,以助於在應用程式的 APP_KEY 洩漏時,防止 PHP 反序列化小工具鏈 (Gadget Chain) 攻擊。如果您的應用程式有刻意在快取中儲存 PHP 物件,則應明確列出允許反序列化的類別:

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

如果您的應用程式先前依賴反序列化任意快取物件,則需要將該用法遷移至明確的類別白名單,或是改用非物件的快取內容(例如陣列)。

容器 (Container)

Container::call 與可為 Null 的類別預設值

影響可能性:低

Container::call 現在於綁定 (Binding) 不存在時,會尊重可為 Null 的類別參數預設值,這與 Laravel 12 中引入的建構子注入行為一致:

php
$container->call(function (?Carbon $date = null) {
    return $date;
});

// Laravel <= 12.x: Carbon instance
// Laravel >= 13.x: null

如果您的方法呼叫注入邏輯依賴先前的行為,則可能需要進行更新。

契約 (Contracts)

Dispatcher 契約(Contracts):dispatchAfterResponse

影響可能性:極低

Illuminate\Contracts\Bus\Dispatcher 契約(Contracts) 現在包含 dispatchAfterResponse($command, $handler = null) 方法。

如果您有維護自定義的 Dispatcher 實作,請將此方法新增至您的類別中。

ResponseFactory 契約(Contracts):eventStream

影響可能性:極低

Illuminate\Contracts\Routing\ResponseFactory 契約(Contracts) 現在包含 eventStream 方法簽署。

如果您有維護此契約(Contracts) 的自定義實作,則應新增此方法。

MustVerifyEmail 契約(Contracts):markEmailAsUnverified

影響可能性:極低

Illuminate\Contracts\Auth\MustVerifyEmail 契約(Contracts) 現在包含 markEmailAsUnverified()

如果您有提供此契約(Contracts) 的自定義實作,請新增此方法以保持相容性。

資料庫

MySQL 或 MariaDB 的資料庫 upsert

影響可能性:中

Laravel 現在會驗證呼叫者是否為 uniqueBy 提供了一個非空值,若為空值則會拋出 InvalidArgumentException,而非產生無效的 SQL。

雖然 MariaDB 與 MySQL 資料庫驅動程式會忽略 uniqueBy 的值,並始終使用資料表的初級金鑰 (Primary Key) 與唯一索引來偵測現有記錄,但此驗證仍然適用。如果 uniqueBy 為空值,將會拋出 InvalidArgumentException

包含 JOINORDER BYLIMIT 的 MySQL DELETE 查詢

影響可能性:低

Laravel 現在會針對 MySQL 語法編譯完整的 DELETE ... JOIN 查詢,包含 ORDER BYLIMIT

在先前的版本中,ORDER BY / LIMIT 子句在連結刪除 (Joined Deletes) 時可能會被無聲地忽略。在 Laravel 13 中,這些子句會被包含在產生的 SQL 中。因此,不支援此語法的資料庫引擎(例如標準的 MySQL / MariaDB 變體)現在可能會拋出 QueryException,而非執行一個未限定範圍的刪除。

Eloquent

Model Booting and Nested Instantiation

影響可能性:極低

在 Model 仍在啟動 (Booting) 時建立新的 Model 實例現在是不被允許的,並且會拋出 LogicException

這會影響在 Model 的 boot 方法或 Trait 的 boot* 方法中實例化 Model 的程式碼:

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

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

請將此邏輯移出啟動週期以避免巢狀啟動。

Polymorphic Pivot Table Name Generation

影響可能性:低

當使用自定義樞紐 Model 類別為多型樞紐 Model 推斷資料表名稱時,Laravel 現在會生成複數名稱。

如果您的應用程式依賴先前推斷出的單數多型樞紐資料表名稱,且使用了自定義樞紐類別,則應在樞紐模型中明確定義資料表名稱。

Collection Model Serialization Restores Eager-Loaded Relations

影響可能性:低

當 Eloquent Model Collection 被序列化並還原時(例如在佇列工作中),系統現在會為該 Collection 的 Model 還原預載 (Eager-loaded) 的關聯。

如果您的程式碼依賴反序列化後關聯不存在的行為,您可能需要調整該邏輯。

HTTP Client

HTTP Client Response::throw and throwIf Signatures

影響可能性:極低

HTTP Client 的回應方法現在於方法定義特徵 (Signatures) 中宣告了其回呼 (Callback) 參數:

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

如果您在自定義的回應類別中重寫了這些方法,請確保您的方法定義特徵是相容的。

Notifications

Default Password Reset Subject

影響可能性:極低

Laravel 預設的密碼重設郵件主旨已更改:

text
// Laravel <= 12.x
Reset Password Notification

// Laravel >= 13.x
Reset your password

如果您的測試、斷言或翻譯覆蓋設定依賴先前的預設字串,請進行相應更新。

Queued Notifications and Missing Models

影響可能性:極低

佇列通知現在會遵循通知類別中定義的 #[DeleteWhenMissingModels] 屬性與 $deleteWhenMissingModels 變數。

在先前版本中,即便您預期缺失的 Model 應該被刪除,但在某些情況下仍可能導致佇列通知工作失敗。

Queue

JobAttempted Event Exception Payload

影響可能性:低

Illuminate\Queue\Events\JobAttempted 事件現在透過 $exception 公開例外物件 (或 null),取代了先前布林值的 $exceptionOccurred 屬性:

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

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

如果您有監聽此事件,請相應更新您的監聽器程式碼。

QueueBusy Event Property Rename

影響可能性:低

Illuminate\Queue\Events\QueueBusy 事件的屬性 $connection 已重新命名為 $connectionName,以與其他佇列事件保持一致。

如果您的監聽器引用了 $connection,請更新為 $connectionName

Queue Contract Method Additions

影響可能性:極低

Illuminate\Contracts\Queue\Queue 契約 (Contracts) 現在包含了先前僅在 Docblocks 中宣告的佇列大小檢視方法。

如果您維護實作此契約的自定義佇列驅動程式,請為以下內容新增實作:

  • pendingSize
  • delayedSize
  • reservedSize
  • creationTimeOfOldestPendingJob

Routing

Domain Route Registration Precedence

影響可能性:低

在路由匹配中,具有明確網域的路由現在優先於非網域路由。

這使得萬用字元 (Catch-all) 子網域路由即使在非網域路由較早註冊的情況下,也能表現一致。如果您的應用程式依賴先前網域與非網域路由之間的註冊優先順序,請檢視路由匹配行為。

Scheduling

withScheduling Registration Timing

影響可能性:極低

透過 ApplicationBuilder::withScheduling() 註冊的排程現在會延遲到 Schedule 被解析 (Resolved) 時才處理。

如果您的應用程式依賴引導 (Bootstrap) 期間的立即排程註冊時機,您可能需要調整該邏輯。

Security

Request Forgery Protection

影響可能性:高

Laravel 的 CSRF 中介層已從 VerifyCsrfToken 重新命名為 PreventRequestForgery,現在包含使用 Sec-Fetch-Site 標頭的請求來源驗證。

VerifyCsrfTokenValidateCsrfToken 仍作為已棄用的別名保留,但應將直接引用更新為 PreventRequestForgery,特別是在測試或路由定義中排除中介層時:

php
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(...)

Support

Manager extend Callback Binding

影響可能性:低

透過 Manager 的 extend 方法註冊的自定義驅動程式閉包 (Closures) 現在會綁定到該 Manager 實例。

如果您先前在這些回呼中依賴另一個綁定物件(例如服務提供者實例)作為 $this,您應使用 use (...) 將這些值移入閉包擷取中。

Str Factories Reset Between Tests

影響可能性:低

Laravel 現在會在測試拆解 (Teardown) 期間重置自定義的 Str 工廠。

如果您的測試依賴於在測試方法之間持久存在的自定義 UUID / ULID / 隨機字串工廠,您應該在每個相關測試或 Setup 鉤子中設定它們。

Js::from Uses Unescaped Unicode By Default

影響可能性:極低

Illuminate\Support\Js::from 現在預設使用 JSON_UNESCAPED_UNICODE

如果您的測試或前端輸出比較依賴轉義的 Unicode 序列(例如 \u00e8),請更新您的預期值。

Utilities

Symfony PHP 8.5 Polyfill and Global Function Conflicts

影響可能性:低

Laravel 13 引入了對 symfony/polyfill-php85 的依賴。在低於 PHP 8.5 的版本上,此 Polyfill 會定義如 array_first()array_last() 之類的全域函式,除非它們在引導期間已提前定義。

這些函式可能與舊有的輔助函式套件(如 laravel/helpers)或使用相同名稱的自定義全域輔助函式發生衝突。例如,歷史悠久的 array_first() 輔助函式接受一個回呼來回傳第一個匹配的元素,而 Polyfill 版本僅回傳陣列的第一個元素。

為了避免衝突並確保跨 PHP 版本的一致行為,您應該優先使用 Illuminate\Support\Arr 方法:

php
use Illuminate\Support\Arr;

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

視圖 (Views)

Bootstrap 分頁視圖名稱

影響可能性:低

Bootstrap 3 預設的內部分頁視圖名稱現在已改為明確的名稱:

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

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

如果您的應用程式直接引用了舊的分頁視圖名稱,請更新這些引用。

其他 (Miscellaneous)

我們也鼓勵您查看 laravel/laravel GitHub 儲存庫 中的變更。雖然其中許多變更並非必須,但您可能希望讓這些檔案與您的應用程式保持同步。本升級指南將涵蓋其中部分變更,但其他變更(例如對設定檔或註解的修改)則不會。您可以透過 GitHub 比較工具 輕鬆查看變更,並選擇對您而言重要的更新。