> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 套件設定的合併與快取

> 從 Laravel 13 的 ServiceProvider 實作出發，說明淺層合併與遞迴取代的差異、數值鍵陣列的陷阱，以及考量設定快取的套件維護方式。

即使為套件新增了設定，使用者先前公開的設定檔也不會自動寫入新的項目。若要在不破壞已公開設定的前提下補上預設值，你需要同時設計陣列的合併方針與設定快取。

本頁將閱讀 Laravel 13 的 `ServiceProvider`，整理如何將設定視為套件的公開 API 來維護。本頁以[Laravel 套件開發](/zh-TW/advanced/package-development)為前提，並使用 `laravel/framework` 的 `v13.34.0` 確認實作。

## 公開與合併是不同的處理

`publishes()` 會註冊複製來源與複製目的地。在 `vendor:publish` 複製檔案之前，使用者的 `config` 目錄不會改變。另一方面，`mergeConfigFrom()` 會更新啟動時的設定儲存庫，而不會改寫檔案本身。

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->mergeConfigFrom(
            __DIR__.'/../config/courier.php', 'courier'
        );
    }

    public function boot(): void
    {
        $this->publishes([
            __DIR__.'/../config/courier.php' => config_path('courier.php'),
        ], 'courier-config');
    }
}
```

使用者只在需要時公開設定檔。即使不公開，在沒有快取的一般啟動中，也會透過 `register()` 的合併使用預設值。

```bash theme={null}
php artisan vendor:publish --tag=courier-config
```

<Warning>
  若在更新步驟中以 `--force` 重新公開設定檔，會覆寫使用者的編輯內容。如果只是新增設定項目，請優先採用補上預設值並說明變更內容的方式。
</Warning>

## mergeConfigFrom() 只合併最上層

`ServiceProvider::mergeConfigFrom()` 會先傳入套件的設定，再傳入應用程式既有的設定，並執行 `array_merge()`。若字串鍵相同，應用程式端的值會優先。

以下範例僅使用陣列重現框架的合併處理。

```php theme={null}
<?php

$defaults = [
    'enabled' => true,
    'transport' => [
        'timeout' => 10,
        'retries' => 3,
    ],
];

$overrides = [
    'transport' => [
        'timeout' => 30,
    ],
];

$config = array_merge($defaults, $overrides);

var_export($config);
```

```text theme={null}
array (
  'enabled' => true,
  'transport' =>
  array (
    'timeout' => 30,
  ),
)
```

最上層的 `enabled` 會被補上，但 `transport` 會整個陣列被取代，`transport.retries` 不會保留。重點在於：若使用者已公開舊的 `transport` 陣列，即使你在同一個陣列中新增鍵，也不會被補上。

## 以 replaceConfigRecursivelyFrom() 補上巢狀設定

Laravel 13 的 `ServiceProvider` 中，還有 protected 方法 `replaceConfigRecursivelyFrom()`。此方法以相同的順序執行 `array_replace_recursive()`。

若你希望設定 API 能個別覆寫巢狀的字串鍵，請如下修改提供者的 `register()`。對於同一個設定鍵，不需要與前述的 `mergeConfigFrom()` 併用。

```php theme={null}
public function register(): void
{
    $this->replaceConfigRecursivelyFrom(
        __DIR__.'/../config/courier.php', 'courier'
    );
}
```

使用前述的 `$defaults` 與 `$overrides`，結果如下。

```php theme={null}
$config = array_replace_recursive($defaults, $overrides);

var_export($config);
```

```text theme={null}
array (
  'enabled' => true,
  'transport' =>
  array (
    'timeout' => 30,
    'retries' => 3,
  ),
)
```

| 設定的約定 | 選擇的處理 | 注意事項 |
| - | - | - |
| 巢狀陣列由使用者完整指定 | `mergeConfigFrom()` | 陣列中未指定的鍵不會被補上 |
| 巢狀項目由使用者僅指定一部分 | `replaceConfigRecursivelyFrom()` | 數值鍵的清單也會成為遞迴取代的對象 |

<Info>
  `replaceConfigRecursivelyFrom()` 是本頁所確認的 Laravel 13 原始碼中存在的方法。請將其與官方套件開發文件所介紹的 `mergeConfigFrom()` 區分，並確認對應版本的框架實作後再使用。
</Info>

### 數值鍵的清單不會被整體取代

遞迴取代並不是「將陣列全部替換為使用者的值」的處理。即使是數值鍵，也只會取代相同鍵的值，使用者未指定的鍵則會保留。

```php theme={null}
<?php

$defaults = ['channels' => ['mail', 'database']];
$overrides = ['channels' => ['slack']];

$config = array_replace_recursive($defaults, $overrides);

var_export($config['channels']);
```

```text theme={null}
array (
  0 => 'slack',
  1 => 'database',
)
```

即使使用者只指定 `['slack']`，`database` 仍會保留。此外，即使傳入 `['channels' => []]`，預設的清單也不會變成空的。對於通知目的地或中介層等「指定整個清單」具有意義的設定，請特別注意。

若有這類設定，請從設定結構著手檢討，例如將清單與可部分覆寫的關聯陣列分到不同的最上層鍵，並使用淺層合併。若在已公開的套件中變更合併方式，即使是相同的設定檔，行為也會改變，因此請不要將其視為單純的實作替換。

## 設定快取中保存的是合併後的值

當應用程式實作了 `CachesConfiguration`，且 `configurationIsCached()` 為 `true` 時，兩個方法都會略過合併處理。在一般的 Laravel 應用程式中，存在設定快取的啟動即屬於此情況。

`ConfigCacheCommand` 會刪除舊的設定快取，啟動新的應用程式並取得整個設定儲存庫。在該次啟動中，提供者的設定會被合併，結果則保存到快取檔案。之後的啟動中，`LoadConfiguration` 會讀取這些值。

```mermaid theme={null}
flowchart TD
    A["php artisan config:cache"] --> B["刪除舊的設定快取"]
    B --> C["啟動新的應用程式"]
    C --> D["讀取設定檔<br>由提供者合併"]
    D --> E["保存整個設定儲存庫"]
    E --> F["之後的啟動使用已保存的設定<br>略過兩個合併方法"]
```

因此，即使更新套件後預設值或合併方式有所改變，持續使用舊快取的應用程式也不會反映這些變更。在使用設定快取的部署中，請以更新後的程式碼重新建立快取。

```bash theme={null}
php artisan config:cache
```

若在開發中想恢復為從檔案重新讀取的狀態，請使用 `php artisan config:clear`。請不要自行決定快取的產生位置，交由 Laravel 的指令管理即可。

<Warning>
  請不要在設定檔中定義 Closure，因為 `config:cache` 無法正確序列化。若想傳入回呼，請在設定中放置類別名稱等資訊，並在提供者中進行實際服務的註冊。
</Warning>

## 發布設定變更前的確認

在套件的測試中，除了未公開的設定之外，也要將舊版本殘留的設定作為輸入。

* 即使未公開設定，也能取得必要的預設值。
* 使用舊的已公開設定時，使用者的值會優先，且新的項目會依設計被補上。
* 對於巢狀陣列、數值鍵的清單與空陣列，覆寫的約定不會改變。
* 在使用端應用程式中 `config:cache` 能成功執行，且在使用快取的另一次啟動中也會得到相同的設定。

請在發布說明中記載新鍵的預設值與必要的快取重建。鍵的刪除、更名或合併方式的變更，請連同與使用者已公開設定的相容性一併判斷。

## 相關頁面

<Columns cols={2}>
  <Card title="套件的測試" icon="flask" href="/zh-TW/advanced/package-testing">
    註冊服務提供者，驗證設定與服務的運作。
  </Card>

  <Card title="版本相容性管理" icon="code-branch" href="/zh-TW/advanced/package-versioning">
    將公開 API 的變更與發布方針及持續維護連結起來。
  </Card>
</Columns>

## 參考的第一手資料

* [Laravel 官方文件：套件設定](https://github.com/laravel/docs/blob/13.x/packages.md#configuration)
* [ServiceProvider：合併與公開的實作](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [ConfigCacheCommand：設定快取的產生](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ConfigCacheCommand.php)
* [LoadConfiguration：設定的讀取](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Bootstrap/LoadConfiguration.php)
* [ConfigClearCommand：設定快取的刪除](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/ConfigClearCommand.php)


## Related topics

- [Laravel 套件開發](/zh-TW/advanced/package-development.md)
- [設定](/zh-TW/configuration.md)
- [快取](/zh-TW/cache.md)
- [套件自動偵測的內部結構](/zh-TW/advanced/package-discovery.md)
- [2026 年 6 月 Laravel 更新](/zh-TW/blog/changelog/202606.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.