什么是 Manager
Illuminate\Support\Manager 是从 Laravel 4.0 时代就存在的抽象类。它为缓存、Session、Mail 等能切换多个「驱动」的系统提供了简洁的构建基础。
「驱动」是指具有相同接口但内部实现不同的后端。例如 Session 有 file、cookie、database、redis 等驱动,可通过配置文件的 driver 键切换。
框架内的使用情况
以 Manager 命名的类并非 都继承自 Illuminate\Support\Manager。
类 是否继承 Manager Illuminate\Session\SessionManager✅ 继承 Illuminate\Cache\CacheManager❌ 未继承(自实现) Illuminate\Queue\QueueManager❌ 未继承(自实现) Laravel\Socialite\SocialiteManager✅ 继承
即使未继承,用 extend() 扩展的基本思路也一致。因此理解 Manager 模式有助于把握整个框架的运作。
基本机制
driver() — 获取驱动
调用 driver() 后,驱动按下面的流程解析:
查看实际源码可以看到,它是用 Str::studly() 将驱动名拼出方法名。
// 摘自 Illuminate\Support\Manager::createDriver()
protected function createDriver ( $driver )
{
if ( isset ( $this -> customCreators [ $driver ])) {
return $this -> callCustomCreator ( $driver );
}
$method = 'create' . Str :: studly ( $driver ) . 'Driver' ;
if ( method_exists ( $this , $method )) {
return $this -> $method ();
}
throw new InvalidArgumentException ( "Driver [ $driver ] not supported." );
}
即 file 驱动调用 createFileDriver(),my-custom 驱动则调用 createMyCustomDriver()。
extend() — 注册自定义驱动
将驱动名与闭包传给 extend(),可以注册自定义驱动。闭包参数是容器实例。
use Illuminate\Support\Facades\ Session ;
Session :: extend ( 'redis-cluster' , function ( $app ) {
return new RedisClusterSessionHandler (
$app -> make ( 'redis' ),
$app [ 'config' ][ 'session' ],
);
});
extend() 会把闭包绑定到 $this,因此在闭包中可以访问 Manager 的属性与方法。
// 摘自 Illuminate\Support\Manager::extend()
public function extend ( $driver , Closure $callback )
{
try {
$callback = $callback -> bindTo ( $this , static :: class ) ?? throw new RuntimeException ;
} catch ( Throwable ) {
$callback = $callback -> bindTo ( null , static :: class );
}
$this -> customCreators [ $driver ] = $callback ;
return $this ;
}
__call — 委托到默认驱动
Manager 类实现了 __call,Manager 本身不存在的方法调用会自动委托给默认驱动。
public function __call ( $method , $parameters )
{
return $this -> driver () -> $method ( ... $parameters );
}
因此可以像 SessionManager::get('key') 一样直接使用 Manager,实际处理则交给驱动。
SessionManager 实现示例
看看 Illuminate\Session\SessionManager,可以具体了解 Manager 的用法。
namespace Illuminate\Session ;
use Illuminate\Support\ Manager ;
class SessionManager extends Manager
{
// 必需:返回默认驱动
public function getDefaultDriver ()
{
return $this -> config -> get ( 'session.driver' );
}
// file 驱动
protected function createFileDriver ()
{
return $this -> createNativeDriver ();
}
// database 驱动
protected function createDatabaseDriver ()
{
$table = $this -> config -> get ( 'session.table' );
$lifetime = $this -> config -> get ( 'session.lifetime' );
return $this -> buildSession ( new DatabaseSessionHandler (
$this -> getDatabaseConnection (), $table , $lifetime , $this -> container
));
}
// redis 驱动
protected function createRedisDriver ()
{
$handler = $this -> createCacheHandler ( 'redis' );
// ...
return $this -> buildSession ( $handler );
}
}
在自定义包中使用 Manager
基本实现
以通知服务为例,构建一个继承 Manager 的类。
创建继承 Manager 的类
namespace App\Notifications ;
use Illuminate\Support\ Manager ;
class NotificationManager extends Manager
{
public function getDefaultDriver () : string
{
return $this -> config -> get ( 'notifications.driver' , 'slack' );
}
protected function createSlackDriver () : SlackNotifier
{
return new SlackNotifier (
$this -> config -> get ( 'notifications.slack' ),
);
}
protected function createEmailDriver () : EmailNotifier
{
return new EmailNotifier (
$this -> config -> get ( 'notifications.email' ),
);
}
protected function createLogDriver () : LogNotifier
{
return new LogNotifier (
$this -> container -> make ( 'log' ),
);
}
}
在 Service Provider 中注册
namespace App\Providers ;
use App\Notifications\ NotificationManager ;
use Illuminate\Support\ ServiceProvider ;
class NotificationServiceProvider extends ServiceProvider
{
public function register () : void
{
$this -> app -> singleton ( NotificationManager :: class , function ( $app ) {
return new NotificationManager ( $app );
});
}
}
添加自定义驱动
// 例如在 AppServiceProvider::boot() 中
$manager = app ( NotificationManager :: class );
$manager -> extend ( 'teams' , function ( $app ) {
return new TeamsNotifier (
$app [ 'config' ][ 'notifications.teams' ],
);
});
使用
$manager = app ( NotificationManager :: class );
// 使用默认驱动
$manager -> send ( '消息' );
// 指定具体驱动
$manager -> driver ( 'email' ) -> send ( '消息' );
// 一次解析后的驱动会被缓存
$manager -> driver ( 'slack' ); // 返回相同实例
MultipleInstanceManager
Illuminate\Support\MultipleInstanceManager 是 Laravel 10 中新增的类。Manager 管理的是「驱动种类」,而 MultipleInstanceManager 可以管理多个具名的「实例」。
与 Manager 的区别
ManagerMultipleInstanceManager管理单元 驱动种类(如 file、redis) 具名实例(如 mailer1、mailer2) 配置方式 一个默认驱动 每个实例都有独立配置 典型用途 Session、Cache 等 Mail、Log 等需要多连接的场景 获取方法 driver()instance()
必需方法
继承 MultipleInstanceManager 时需要实现 3 个方法。
// 摘自源码
abstract public function getDefaultInstance ();
abstract public function setDefaultInstance ( $name );
abstract public function getInstanceConfig ( $name );
getInstanceConfig() 返回对应名字的配置数组。配置中必须包含 driver(或 $driverKey 指定的键)。
解析流程
自定义包的实现示例
以同时对接多个 SMS 网关的包为例。
namespace App\Sms ;
use Illuminate\Support\ MultipleInstanceManager ;
class SmsManager extends MultipleInstanceManager
{
// 默认实例名
public function getDefaultInstance () : string
{
return $this -> config -> get ( 'sms.default' , 'primary' );
}
public function setDefaultInstance ( $name ) : void
{
$this -> config -> set ( 'sms.default' , $name );
}
// 返回实例名对应的配置
public function getInstanceConfig ( $name ) : array
{
return $this -> config -> get ( "sms.gateways.{ $name }" );
}
// twilio 驱动(会传入配置数组)
protected function createTwilioDriver ( array $config ) : TwilioSmsGateway
{
return new TwilioSmsGateway (
$config [ 'account_sid' ],
$config [ 'auth_token' ],
);
}
// vonage 驱动
protected function createVonageDriver ( array $config ) : VonageSmsGateway
{
return new VonageSmsGateway (
$config [ 'api_key' ],
$config [ 'api_secret' ],
);
}
}
对应的配置文件:
// config/sms.php
return [
'default' => 'primary' ,
'gateways' => [
'primary' => [
'driver' => 'twilio' ,
'account_sid' => env ( 'TWILIO_ACCOUNT_SID' ),
'auth_token' => env ( 'TWILIO_AUTH_TOKEN' ),
],
'backup' => [
'driver' => 'vonage' ,
'api_key' => env ( 'VONAGE_API_KEY' ),
'api_secret' => env ( 'VONAGE_API_SECRET' ),
],
'marketing' => [
'driver' => 'twilio' ,
'account_sid' => env ( 'TWILIO_MARKETING_SID' ),
'auth_token' => env ( 'TWILIO_MARKETING_TOKEN' ),
],
],
];
使用时通过 instance() 指定名字。
$sms = app ( SmsManager :: class );
// 使用默认实例(primary = twilio)
$sms -> send ( '+8613800000000' , '验证码:123456' );
// 按名称使用指定实例
$sms -> instance ( 'backup' ) -> send ( '+8613800000000' , '验证码:123456' );
// 使用营销实例
$sms -> instance ( 'marketing' ) -> send ( '+8613800000000' , '活动通知' );
MultipleInstanceManager 与 Manager 一样支持通过 extend() 添加自定义驱动。它同样实现了 __call,实例本身不存在的方法会委托给默认实例。
驱动缓存管理
Manager 的缓存操作
// 获取全部驱动
$drivers = $manager -> getDrivers ();
// 清除全部驱动缓存
$manager -> forgetDrivers ();
MultipleInstanceManager 的缓存操作
// 移除特定实例
$manager -> forgetInstance ( 'backup' );
// 移除默认实例
$manager -> forgetInstance ();
// 一次移除多个实例
$manager -> forgetInstance ([ 'primary' , 'backup' ]);
// 同时清除底层缓存
$manager -> purge ( 'backup' );
Manager 管理驱动的「种类」。实现 createXxxDriver() 方法,用 extend() 扩展
MultipleInstanceManager 按名字管理驱动的「实例」,适合需要多连接配置的包
名字里带 Manager 的类并不一定都继承自 Illuminate\Support\Manager。CacheManager 和 QueueManager 都是自实现
Macroable trait 学习向既有类添加自定义方法的 Macroable trait。