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

# Service Provider

> 說明如何使用 Laravel service provider 登記與啟動應用程式的服務。

## 什麼是 Service Provider

Service provider 是 Laravel 應用整體啟動的核心位置。無論是你自己的應用，或 Laravel 所有核心服務，都是透過 service provider 進行啟動。

「啟動」意指**登記**各種東西：向 service container 登記 binding、event listener、middleware、route 等。Service provider 是設定應用的核心處所。

```mermaid theme={null}
flowchart TD
    A["應用啟動"] --> B["讀取 bootstrap/providers.php"]
    B --> C["實例化所有 provider"]
    C --> D["執行所有 provider 的 register()<br>僅登記 service container binding"]
    D --> E["執行所有 provider 的 boot()<br>view composer、event listener 等"]
    E --> F["應用準備完成<br>開始處理請求"]
```

Laravel 內部使用許多 service provider 啟動 mailer、queue、cache 等核心服務。這些多為「延遲」provider，並非在每次請求時載入，而是實際需要所提供的服務時才載入。

所有使用者定義的 service provider 皆在 `bootstrap/providers.php` 檔案登記。

<Info>
  想深入了解 Laravel 如何處理請求，請參閱 [Request Lifecycle](https://laravel.com/docs/lifecycle) 文件。
</Info>

## 撰寫 Service Provider

所有 service provider 都繼承 `Illuminate\Support\ServiceProvider` 類別。多數 provider 會有 `register` 方法與 `boot` 方法。

要產生新的 provider，使用 `make:provider` Artisan 指令。Laravel 會自動將新 provider 登記到 `bootstrap/providers.php`。

```shell theme={null}
php artisan make:provider RiakServiceProvider
```

### register 方法

在 `register` 方法內，只做[service container](/zh-TW/service-container)的 binding。不要在 `register` 中登記 event listener、路由或其他功能。這可能誤用尚未載入的 service provider 所提供的服務。

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

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider
{
    /**
     * 登記應用服務
     */
    public function register(): void
    {
        $this->app->singleton(Connection::class, function (Application $app) {
            return new Connection(config('riak'));
        });
    }
}
```

在 service provider 的方法中，都可以透過 `$this->app` 屬性存取 service container。

#### bindings 屬性與 singletons 屬性

若要登記大量簡單的 binding，除了手動登記每個 binding 外，也可以使用 `bindings` 屬性與 `singletons` 屬性。當框架載入 service provider 時，會自動檢查這些屬性並登記 binding。

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

namespace App\Providers;

use App\Contracts\DowntimeNotifier;
use App\Contracts\ServerProvider;
use App\Services\DigitalOceanServerProvider;
use App\Services\PingdomDowntimeNotifier;
use App\Services\ServerToolsProvider;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * 所有要登記的 container binding
     *
     * @var array
     */
    public $bindings = [
        ServerProvider::class => DigitalOceanServerProvider::class,
    ];

    /**
     * 所有要登記的 container singleton
     *
     * @var array
     */
    public $singletons = [
        DowntimeNotifier::class => PingdomDowntimeNotifier::class,
        ServerProvider::class => ServerToolsProvider::class,
    ];
}
```

### boot 方法

在 service provider 中登記[view composer](https://laravel.com/docs/views#view-composers) 時，在 `boot` 方法中處理。**此方法會在其他所有 service provider 登記完成後才被呼叫**，因此可存取框架所登記的其他所有服務。

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

namespace App\Providers;

use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;

class ComposerServiceProvider extends ServiceProvider
{
    /**
     * 啟動應用服務
     */
    public function boot(): void
    {
        View::composer('view', function () {
            // ...
        });
    }
}
```

<Warning>
  請勿混淆 `register` 方法與 `boot` 方法的職責。`register` 只登記 binding，`boot` 用於服務初始化或其他設定。
</Warning>

#### boot 方法的依賴注入

`boot` 方法也能用 type-hint 注入依賴。[service container](/zh-TW/service-container) 會自動注入所需的依賴。

```php theme={null}
use Illuminate\Contracts\Routing\ResponseFactory;

/**
 * 啟動應用服務
 */
public function boot(ResponseFactory $response): void
{
    $response->macro('serialized', function (mixed $value) {
        // ...
    });
}
```

## Service Provider 的登記方式

所有 service provider 都在 `bootstrap/providers.php` 檔案登記。此檔案回傳一個由應用 service provider 類別名稱組成的陣列。

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

return [
    App\Providers\AppServiceProvider::class,
];
```

<Info>
  在 Laravel 13 中，service provider 在 `bootstrap/providers.php` 登記，而不再是 `config/app.php` 的 `providers` 陣列。使用 `make:provider` 指令會自動加入。
</Info>

執行 `make:provider` Artisan 指令時，Laravel 會自動把 provider 加到檔案中。若手動建立 provider 類別，需自行加入陣列。

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

return [
    App\Providers\AppServiceProvider::class,
    App\Providers\ComposerServiceProvider::class,
];
```

## 建立自訂 Service Provider

實際來建立自訂 service provider：

<Steps>
  <Step title="產生 provider">
    以 Artisan 指令產生 service provider。

    ```shell theme={null}
    php artisan make:provider PaymentServiceProvider
    ```
  </Step>

  <Step title="在 register 方法中 bind">
    在產生的 provider 的 `register` 方法中撰寫 binding。

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

    namespace App\Providers;

    use App\Contracts\PaymentGateway;
    use App\Services\StripePaymentGateway;
    use Illuminate\Contracts\Foundation\Application;
    use Illuminate\Support\ServiceProvider;

    class PaymentServiceProvider extends ServiceProvider
    {
        /**
         * 登記應用服務
         */
        public function register(): void
        {
            $this->app->singleton(PaymentGateway::class, function (Application $app) {
                return new StripePaymentGateway(
                    config('services.stripe.secret')
                );
            });
        }

        /**
         * 啟動應用服務
         */
        public function boot(): void
        {
            // 若有啟動時的處理，寫在這裡
        }
    }
    ```
  </Step>

  <Step title="登記到 bootstrap/providers.php">
    使用 `make:provider` 時會自動登記。手動時請自行加入。

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

    return [
        App\Providers\AppServiceProvider::class,
        App\Providers\PaymentServiceProvider::class,
    ];
    ```
  </Step>
</Steps>

## 延遲 Provider（Deferred Providers）

若 provider 只登記 service container 的 binding，可將該登記延遲到實際需要時。將這類延遲 provider 的載入延後，可避免每次請求都從檔案系統載入，提升應用效能。

```mermaid theme={null}
flowchart TD
    A["應用啟動"] --> B["載入並初始化一般 provider"]
    B --> C["延遲 provider 只記錄<br>所提供的服務清單"]
    C --> D["處理請求"]
    D --> E{{"是否請求延遲 provider<br>的服務？"}}
    E -->|"否"| F["provider 不會被載入<br>節省記憶體與處理"]
    E -->|"是"| G["即時載入 provider"]
    G --> H["執行 register()"]
    H --> I["由 container 提供服務"]
```

要建立延遲 provider，實作 `\Illuminate\Contracts\Support\DeferrableProvider` 介面並定義 `provides` 方法。`provides` 方法回傳 provider 所登記的 service container binding。

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

namespace App\Providers;

use App\Services\Riak\Connection;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
    /**
     * 登記應用服務
     */
    public function register(): void
    {
        $this->app->singleton(Connection::class, function (Application $app) {
                return new Connection(config('riak'));
            });
    }

    /**
     * 取得 provider 所提供的服務
     *
     * @return array<int, string>
     */
    public function provides(): array
    {
        return [Connection::class];
    }
}
```

<Tip>
  延遲 provider 適合只在特定功能而非整個應用使用的服務。可避免載入不必要的服務、最佳化效能。
</Tip>

## 後續步驟

<Card title="Service Container" icon="box" href="/zh-TW/service-container">
  確認 service container 機制與 binding 細節。
</Card>


## Related topics

- [Service Container](/zh-TW/service-container.md)
- [Laravel 11 以後的新應用程式結構 FAQ](/zh-TW/advanced/app-structure-faq.md)
- [延遲服務提供者](/zh-TW/advanced/deferred-provider.md)
- [建立 AI SDK 的自定義 Provider](/zh-TW/advanced/ai-sdk-custom-provider.md)
- [從 Laravel 10 升級到 11](/zh-TW/blog/upgrade-10-to-11.md)
