容器和依賴注入
容器與依賴注入(container)
依賴注入, 是透過容器來實現的, 可以實現對引用的類別的自動引入, 以及該類別的依賴對象的自動加載
依賴注入是指對類別的依賴通過構造器完成自動注入:
<?php namespace app\index\controller; use app\index\model\User; class Index { protected $user; // 通过依赖注入方式,实现User实例的自动注入到当前对象中 public function __construct(User $user) { $this->user = $user; } public function hello() { return 'Hello,' . $this->user->name . '!'; } }
依賴注入的對象參數支持多個,且和順序無關。
綁定
依賴注入的類別統一由容器管理,大多數情況下是在自動綁定且實例化的。不過你可以隨時進行手動綁定類別到容器中(通常是在服務類別的register方法中進行綁定),支援多種綁定方式。
綁定類別標識
可以對已有的類別庫綁定一個標識(唯一),以便於快速呼叫。
// 绑定类库标识 $this->app->bind('cache', 'think\Cache');
或使用助手函數
// 绑定类库标识 bind('cache', 'think\Cache');
綁定的類別標識可以自己定義(只要不衝突)。
綁定閉包
可以綁定一個閉包到容器中
bind('sayHello', function ($name) { return 'hello,' . $name; });
綁定實例
也可以直接綁定一個類別的實例
$cache = new think\Cache; // 绑定类实例 bind('cache', $cache);
綁定到介面實作
對於依賴注入使用介面類別的情況,我們需要告訴系統使用哪個特定的介面實作類別來進行注入,這個使用可以把某個類別綁定到介面
// 绑定think\LoggerInterface接口实现到think\Log bind('think\LoggerInterface','think\Log');
使用介面作為依賴注入的類型
<?php namespace app\index\controller; use think\LoggerInterface; class Index { public function hello(LoggerInterface $log) { $log->record('hello,world!'); } }
批次綁定
在實際應用開發過程,不需要手動綁定,我們只需要在應用程式目錄下面定義provider.php檔案(傳回一個陣列),系統會自動批次綁定類別庫到容器中。
return [ 'route' => \think\Route::class, 'session' => \think\Session::class, 'url' => \think\Url::class, ];
綁定標識呼叫的時候區分大小寫,系統已經內建綁定了核心常用類別庫,無需重複綁定
系統內建綁定到容器中的類別庫包括
系統類別庫 | 容器綁定識別碼 |
---|---|
think\App | app |
think\Cache | cache |
think\Config | config |
think\Cookie | cookie |
#think\Console | console |
think\Console | console |
#think\Db | db |
think\Debug | debug |
think \Env | env |
think\Event | #event |
think\Http | http |
think\Lang | lang |
#think\Log | log |
think\Middleware | middleware |
#think\Request | ##request|
response | |
filesystem | |
route | |
session | |
# validate |
解析
使用app助手函數進行容器中的類別解析調用,對於已經綁定的類別標識,會自動快速實例化
$cache = app('cache');
帶參數實例化呼叫
$cache = app('cache',['file']);
對於沒有綁定的類,也可以直接解析
$arrayItem = app('org\utils\ArrayItem');
呼叫和綁定的標識必須保持一致(包括大小寫)
容器中已經呼叫過的類別會自動使用單例,除非你使用下面的方式強制重新實例化。
// 每次调用都会重新实例化 $cache = app('cache', [], true);
物件化呼叫
使用app助理函數取得容器中的物件實例(支援依賴注入)。
$app = app(); // 判断对象实例是否存在 isset($app->cache); // 注册容器对象实例 $app->cache = think\Cache::class; // 获取容器中的对象实例 $cache = $app->cache;
也就是說,你可以在任何地方使用app()方法呼叫容器中的任何類,但大多數情況下面,我們更建議使用依賴注入。
// 调用配置类 app()->config->get('app_name'); // 调用session类 app()->session->get('user_name');
自動注入
容器主要用於依賴注入,依賴注入會先檢查容器中是否註冊過該物件實例,如果沒有就會自動實例化,然後自動注入,例如:
我們可以給路由綁定模型物件實例
Route::get('user/:id','index/Index/hello') ->model('\app\index\model\User');
然後在操作方法中自動注入User模型
<?php namespace app\index\controller; use app\index\model\User; class Index { public function hello(User $user) { return 'Hello,'.$user->name; } }
自訂實例化
容器中的物件實例化支援自訂,可以在你需要依賴注入的物件中增加__make方法定義,例如:
如果你希望User模型類別在依賴注入的時候使用自訂實例化的方式,可以用下面的方法。
<?php namespace app\index\model; use think\Model; use think\db\Query; class User extends Model { public static function __make(Query $query) { return (new self())->setQuery($query); } }
容器物件回呼機制
容器中的物件實例化之後,支援回呼機制,利用該機制可以實現諸如註解功能等相關功能。
你可以透過resolving方法註冊一個全域回呼
Container::getInstance()->resolving(function($instance,$container) { // ... });
回呼方法支援兩個參數,第一個參數是容器物件實例,第二個參數是容器實例本身。
或單獨註冊一個某個容器物件的回呼
Container::getInstance()->resolving(\think\Cache::class,function($instance,$container) { // ... });