# 容器和依賴注入
## 容器和依賴注入
`5.1`版本正式引入了容器的概念,用來更方便的管理類依賴及運行依賴注入。
> 5\.0版本已經支持依賴注入的,依賴注入和容器沒有必然關系
容器類的工作由`think\Container`類完成,但大多數情況我們只需要通過`app`助手函數即可完成大部分操作。
依賴注入其實本質上是指對類的依賴通過構造器完成自動注入,例如在控制器架構方法和操作方法中一旦對參數進行對象類型約束則會自動觸發依賴注入,由于訪問控制器的參數都來自于URL請求,普通變量就是通過參數綁定自動獲取,對象變量則是通過依賴注入生成。
```
<?php
namespace app\index\controller;
use app\index\model\User;
class Index
{
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function hello()
{
return 'Hello,' . $this->user->name . '!';
}
}
```
> 依賴注入的對象參數支持多個,并且和順序無關。
支持使用依賴注入的場景包括(但不限于):
- 控制器架構方法;
- 控制器操作方法;
- 數據庫和模型事件方法;
- 路由的閉包定義;
- 行為類的方法;
> 在ThinkPHP的設計中,`think\App`類雖然自身不是容器,但卻是一個容器管理類,可以完成容器的所有操作。
> `V5.1.14+`版本開始,應用類自身就是一個容器實例。
## 綁定
依賴注入的類統一由容器進行管理,你可以隨時綁定類到容器中,支持多種綁定方式。
### 綁定類標識
可以對已有的類庫綁定一個標識(唯一),便于快速調用。
```
// 綁定類庫標識
bind('cache','think\Cache');
// 快速調用(自動實例化)
$cache = app('cache');
```
> 調用和綁定的標識必須保持一致(包括大小寫)
容器中已經調用過的類會自動使用單例,除非你使用下面的方式強制重新實例化。
```
// 每次調用都會重新實例化
$cache = app('cache',true);
```
你可以綁定一個類到容器中(第一個參數直接傳入類名):
```
bind('app\common\Test');
```
但實際上這個操作是多余的,因為只要調用過一次后就會自動綁定
```
app('app\common\Test');
```
> 綁定的類標識可以自己定義(只要不沖突)。
### 綁定閉包
可以把一個閉包方法綁定到容器中
```
bind('sayHello', function ($name) {
return 'hello,' . $name;
});
echo app('sayHello',['thinkphp']);
```
### 綁定類的實例
也可以直接綁定一個類的實例
```
$cache = new think\Cache;
// 綁定類實例
bind('cache',$cache);
// 快速調用類的實例
$cache = app('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!');
}
}
```
### 批量綁定
如果傳入一個數組的話,就表示進行批量綁定,例如:
```
bind([
'route' => \think\Route::class,
'session' => \think\Session::class,
'url' => \think\Url::class,
]);
```
可以在應用或者模塊目錄下面定義`provider.php`文件(返回一個數組),系統會自動批量綁定類庫到容器中。
```
return [
'route' => \think\Route::class,
'session' => \think\Session::class,
'url' => \think\Url::class,
];
```
> 綁定標識調用的時候區分大小寫,系統已經內置綁定了核心常用類庫,無需重復綁定
系統內置綁定到容器中的類庫包括
系統類庫容器綁定標識think\\Buildbuildthink\\Cachecachethink\\Configconfigthink\\Cookiecookiethink\\Debugdebugthink\\Envenvthink\\Hookhookthink\\Langlangthink\\Loglogthink\\Requestrequestthink\\Responseresponsethink\\Routeroutethink\\Sessionsessionthink\\Urlurlthink\\Validatevalidatethink\\Viewview## 解析
### 助手函數方式
使用`app`助手函數進行容器中的類解析調用,對于已經綁定的類標識,會自動快速實例化
```
app('cache');
```
上面的app助手函數相當于調用了
```
Container::get('cache');
```
帶參數實例化調用
```
app('cache',['file']);
```
對于沒有綁定的類,也可以直接解析
```
app('org\utils\ArrayItem');
```
### 對象化調用
使用`app`助手函數獲取容器中的對象實例(支持依賴注入)。
```
$app = app();
// 判斷對象實例是否存在
isset($app->cache);
// 注冊容器對象實例
$app->cache = think\Cache::class;
// 獲取容器中的對象實例
$cache = $app->cache;
```
不帶任何參數調用`app`助手函數其實是實例化`think\App`類,可以方便的操作容器、綁定和調用對象實例。
```
// 綁定類到容器
app()->test = new Test;
// 實例調用
$test = app()->test;
```
也就是說,你可以在任何地方使用`app()`方法調用容器中的任何類。
```
// 調用配置類
app()->config->get('app_name');
// 調用session類
app()->session->get('user_name');
```
### 自動注入
容器的更多使用主要用于依賴注入,和5.0自動注入的方式有所區別,類的綁定操作不再使用`Request`對象而是直接注冊到容器中,并且支持模型事件和數據庫事件的依賴注入,依賴注入會首先檢查容器中是否注冊過該對象實例,如果有的話就會自動注入,例如:
我們可以給路由綁定模型對象實例
```
Route::get('user/:id','index/Index/hello')
->model('\app\index\model\User');
```
然后在操作方法中自動注入User模型
```
<?php
namespace app\index\controller;
use app\index\model\User;
use think\Controller;
class Index extends Controller
{
public function hello(User $user)
{
return 'Hello,'.$user->name;
}
}
```
## 自定義實例化
`V5.1.13+`版本開始,容器中的對象實例化支持自定義,可以在你的容器中需要依賴注入的對象中增加`__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');
}
}
```
- 序言
- 基礎
- 安裝
- 開發規范
- 目錄結構
- 配置
- 架構
- 架構總覽
- 入口文件
- URL訪問
- 模塊設計
- 命名空間
- 容器和依賴注入
- Facade
- 鉤子和行為
- 中間件
- 路由
- 路由定義
- 變量規則
- 路由地址
- 閉包支持
- 路由參數
- 路由緩存
- 跨域請求
- 注解路由
- 路由分組
- MISS路由
- 資源路由
- 快捷路由
- 路由別名
- 路由綁定
- 域名路由
- URL生成
- 控制器
- 控制器定義
- 前置操作
- 跳轉和重定向
- 空操作和空控制器
- 分層控制器
- 資源控制器
- 請求
- 請求對象
- 請求信息
- 輸入變量
- 請求類型
- HTTP頭信息
- 偽靜態
- 參數綁定
- 請求緩存
- 響應
- 響應輸出
- 響應參數
- 重定向
- 數據庫
- 連接數據庫
- 查詢構造器
- 查詢數據
- 添加數據
- 更新數據
- 刪除數據
- 查詢表達式
- 鏈式操作
- 聚合查詢
- 時間查詢
- 高級查詢
- 視圖查詢
- JSON字段
- 子查詢
- 原生查詢
- 查詢事件
- 事務操作
- 監聽SQL
- 存儲過程
- 數據集
- 分布式數據庫
- 模型
- 定義
- 新增
- 更新
- 刪除
- 查詢
- JSON字段
- 獲取器
- 修改器
- 自動時間戳
- 只讀字段
- 軟刪除
- 類型轉換
- 數據完成
- 查詢范圍
- 模型輸出
- 模型事件
- 模型關聯
- 一對一關聯
- 一對多關聯
- 遠程一對多
- 多對多關聯
- 多態關聯
- 關聯預載入
- 關聯統計
- 關聯輸出
- 視圖
- 視圖渲染
- 視圖賦值
- 視圖過濾
- 模板引擎
- 模板
- 變量輸出
- 使用函數
- 運算符
- 原樣輸出
- 模板注釋
- 模板布局
- 模板繼承
- 包含文件
- 輸出替換
- 標簽庫
- 內置標簽
- 循環標簽
- 比較標簽
- 條件判斷
- 資源文件加載
- 標簽嵌套
- 原生PHP
- 定義標簽
- 標簽擴展
- 錯誤和日志
- 異常處理
- 日志處理
- 調試
- 調試模式
- Trace調試
- 性能調試
- SQL調試
- 變量調試
- 遠程調試
- 驗證
- 驗證器
- 驗證規則
- 錯誤信息
- 驗證場景
- 路由驗證
- 內置規則
- 獨立驗證
- 靜態調用
- 表單令牌
- 雜項
- 緩存
- Session
- Cookie
- 多語言
- 分頁
- 上傳
- 命令行
- 啟動內置服務器
- 自動生成目錄結構
- 創建類庫文件
- 生成類庫映射文件
- 清除緩存文件
- 生成配置緩存文件
- 生成數據表字段緩存
- 生成路由映射緩存
- 自定義指令
- 擴展庫
- 驗證碼
- 圖像處理
- Time
- 數據庫遷移工具
- Workerman
- MongoDb
- 單元測試
- 安全和性能
- 安全建議
- 優化建議
- 附錄
- 助手函數
- 升級指導
- 更新日志