在自訂 Service Provider 的 register() 方法中呼叫 Auth::extend() 註冊自訂驅動時,拋出 Call to undefined method Illuminate\Support\Facades\Auth::extend() 錯誤。
Laravel 核心 AuthServiceProvider 設定 protected $defer = true,並實作 provides() 回傳 ['auth']。這意味著:
AuthServiceProvider::register()Auth Facade (如 Auth::check()) 時,才會觸發延遲載入Illuminate\Foundation\ProviderRepository)public function load(Application $app, array $providers)
{
// 1. 先註冊所有 eager providers
foreach ($manifest['eager'] as $provider) {
$app->register($this->createProvider($app, $provider));
}
// 2. 再設定 deferred services 清單
$app->setDeferredServices($manifest['deferred']);
}
當你的自訂 LoauthServiceProvider 是 eager 載入時,其 register() 執行於步驟 1,此時 auth 服務尚未註冊,自然無法呼叫 Auth::extend()。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Auth\AuthServiceProvider;
class LoauthServiceProvider extends ServiceProvider
{
public function register()
{
// 強制註冊 Auth 服務
$authProvider = new AuthServiceProvider($this->app);
$authProvider->register();
// 現在可以安全呼叫 Auth::extend()
$this->app['auth']->extend('loauth', function ($app) {
return new \App\Auth\LoauthGuard(
$app['auth']->createUserProvider(),
$app['session.store']
);
});
}
}
<?php
class LoauthServiceProvider extends ServiceProvider
{
protected $defer = true;
public function provides()
{
return ['auth.loauth']; // 自訂服務名稱
}
public function register()
{
$this->app['auth']->extend('loauth', function ($app) {
return new \App\Auth\LoauthGuard(...);
});
}
}
並在 config/app.php 的 providers 陣列中,確保 LoauthServiceProvider 排在 AuthServiceProvider **之後** (雖然延遲載入順序由框架依賴解析決定,但這能確保邏輯清晰)。
boot() 而非 register() 中註冊public function boot()
{
// boot 階段所有服務已就緒
$this->app['auth']->extend('loauth', function ($app) {
return new \App\Auth\LoauthGuard(...);
});
}
注意: boot() 會在每次請求執行,若驅動註冊邏輯重複執行無副作用則可用;但最佳實踐仍是於 register() 完成綁定。
| 場景 | 建議做法 |
|---|---|
| 在 eager Provider 中使用 deferred 服務 | 手動 new DeferredProvider($app)->register() |
| 自訂驅動/擴展核心服務 | 方法一:手動觸發核心 Provider 註冊 |
| 自訂服務本身無需立即可用 | 設定 $defer = true + provides() |
| 需在請求早期可用的服務 | 保持 eager (預設),勿設定 defer |
$app->bind('key', fn() => new Class) - 每次解析新實例$app->singleton('key', fn() => new Class) - 共享同一實例$app->instance('key', $obj) - 直接綁定既有物件$defer = true + provides() - 按需實例化,提升啟動效能Auth::method() 實為 $app['auth']->method()理解容器載入順序與延遲機制,是開發 Laravel 套件、擴展核心服務、排除「服務未找到」錯誤的關鍵。