[TOC]
## 第一節: 容器(Container)的功能
> * 容器(Container)的功能,一句話概括就是 **管理對象實例,緩存已創建的實例**。
> 1. 源碼分析中可知容器(Conatiner)主要實現為bind()和make()。
> 2. bind()注冊內容到容器(Container)的bind或者instances中
> 3. make()獲取實例內容,并緩存到容器(Container)的instances中。
> * 核心功能就是**緩存已創建的實例**。
* * * * *
## 第二節:think5中容器(Container)的使用
### 1 容器(Container)在系統初始化中的注冊
~~~
// base.php
// 注冊核心類到容器
Container::getInstance()->bind([
'app' => App::class,
'build' => Build::class,
'cache' => Cache::class,
'config' => Config::class,
'cookie' => Cookie::class,
'debug' => Debug::class,
'env' => Env::class,
'hook' => Hook::class,
'lang' => Lang::class,
'log' => Log::class,
'request' => Request::class,
'response' => Response::class,
'route' => Route::class,
'session' => Session::class,
'url' => Url::class,
'validate' => Validate::class,
'view' => View::class,
// 接口依賴注入
'think\LoggerInterface' => Log::class,
]);
~~~
> 在base.php中注冊核心類到容器(Container)的bind。
> 注冊完成后,在如下調用時
~~~
Container::get('app');
~~~
> 將返回think\app的實例對象。
> 這里的實例對象將緩存到Container的instances中。
> 下次使用上面的調用時 返回緩存的實例對象。
### 2 容器(Container)在核心類App中的封裝
>[info] 在library\think\App.php中。
> 系統的核心應用類App實現為一個具有容器功能的類。
~~~
// library\think\App.php
public function __construct($appPath = '')
{
$this->container = Container::getInstance();
。。。。。。
}
~~~
> 創建容器對象保存到App的container屬性中。
* * * * *
~~~
// library\think\App.php
public function container()
{
return $this->container;
}
~~~
> 獲取容器實例
* * * * *
~~~
public function __set($name, $value)
{
$this->container->bind($name, $value);
}
public function __get($name)
{
return $this->container->make($name);
}
public function __isset($name)
{
return $this->container->bound($name);
}
public function __unset($name)
{
$this->container->__unset($name);
}
~~~
> 容器的調用封裝
實現容器的快捷操作。
在設置和讀取app的屬性的時候,如果其屬性不存在,則會操作app中的容器。
* * * * *
### 3 容器(Container)在核心類App中的使用
>[info] 類App中封裝了容器的功能
> 在設置和獲取app的屬性時,如果查找的屬性不存在,則會對容器進行操作
~~~
// library\think\App.phhp
public function initialize()
{
// 設置路徑環境變量
$this->env->set([
'think_path' => $this->thinkPath,
'root_path' => $this->rootPath,
'app_path' => $this->appPath,
'config_path' => $this->configPath,
'route_path' => $this->routePath,
'runtime_path' => $this->runtimePath,
'extend_path' => $this->rootPath . 'extend/',
'vendor_path' => $this->rootPath . 'vendor/',
]);
// 加載環境變量配置文件
if (is_file($this->rootPath . '.env')) {
$this->env->load($this->rootPath . '.env');
}
$this->namespace = $this->env->get('app_namespace', $this->namespace);
$this->env->set('app_namespace', $this->namespace);
// 注冊應用命名空間
Loader::addNamespace($this->namespace, $this->appPath);
$this->configExt = $this->env->get('config_ext', '.php');
// 初始化應用
$this->init();
// 開啟類名后綴
$this->suffix = $this->config('app.class_suffix');
// 應用調試模式
$this->debug = $this->env->get('app_debug', $this->config('app.app_debug'));
$this->env->set('app_debug', $this->debug);
if (!$this->debug) {
ini_set('display_errors', 'Off');
} elseif (PHP_SAPI != 'cli') {
//重新申請一塊比較大的buffer
if (ob_get_level() > 0) {
$output = ob_get_clean();
}
ob_start();
if (!empty($output)) {
echo $output;
}
}
// 注冊根命名空間
if (!empty($this->config('app.root_namespace'))) {
Loader::addNamespace($this->config('app.root_namespace'));
}
// 注冊類庫別名
Loader::addClassAlias($this->config->pull('alias'));
// 加載系統助手函數
include $this->thinkPath . 'helper.php';
// 設置系統時區
date_default_timezone_set($this->config('app.default_timezone'));
// 監聽app_init
$this->hook->listen('app_init');
}
~~~
>[danger] 類App中并未聲明env,config,hook等屬性
> 使用$this->env->xx() $this->config->xx(),$this->hook->xx()就調用了容器功能
> $this->env->xx()獲取調App類的__get()方法,進而調用Container->make()方法,
> Container->make()方法查找到Container的bind注冊的env為Env::class。創建Env對象。
> 調用Env->xx()方法完成$this->evn->xx()的調用
> 其中env與Env::class的關系在上面的所說的base.php進行綁定注冊。
> $this->config->xx(),$this->hook->listent()類似
* * * * *
~~~
// library\think\App.php
public function run()
{
// 初始化應用
$this->initialize();
try {
if ($this->bind) {
// 模塊/控制器綁定
$this->route->bind($this->bind);
} elseif ($this->config('app.auto_bind_module')) {
// 入口自動綁定
$name = pathinfo($this->request->baseFile(), PATHINFO_FILENAME);
if ($name && 'index' != $name && is_dir($this->appPath . $name)) {
$this->route->bind($name);
}
}
$this->request->filter($this->config('app.default_filter'));
// 讀取默認語言
$this->lang->range($this->config('app.default_lang'));
if ($this->config('app.lang_switch_on')) {
// 開啟多語言機制 檢測當前語言
$this->lang->detect();
}
$this->request->langset($this->lang->range());
// 加載系統語言包
$this->lang->load([
$this->thinkPath . 'lang/' . $this->request->langset() . '.php',
$this->appPath . 'lang/' . $this->request->langset() . '.php',
]);
// 監聽app_dispatch
$this->hook->listen('app_dispatch');
// 獲取應用調度信息
$dispatch = $this->dispatch;
if (empty($dispatch)) {
// 進行URL路由檢測
$dispatch = $this->routeCheck();
}
// 記錄當前調度信息
$this->request->dispatch($dispatch);
// 記錄路由和請求信息
if ($this->debug) {
$this->log('[ ROUTE ] ' . var_export($this->request->routeinfo(), true));
$this->log('[ HEADER ] ' . var_export($this->request->header(), true));
$this->log('[ PARAM ] ' . var_export($this->request->param(), true));
}
// 監聽app_begin
$this->hook->listen('app_begin');
// 請求緩存檢查
$this->request->cache(
$this->config('app.request_cache'),
$this->config('app.request_cache_expire'),
$this->config('app.request_cache_except')
);
// 執行調度
$data = $dispatch->run();
} catch (HttpResponseException $exception) {
$data = $exception->getResponse();
}
// 輸出數據到客戶端
if ($data instanceof Response) {
$response = $data;
} elseif (!is_null($data)) {
// 默認自動識別響應輸出類型
$isAjax = $this->request->isAjax();
$type = $isAjax ? $this->config('app.default_ajax_return') : $this->config('app.default_return_type');
$response = Response::create($data, $type);
} else {
$response = Response::create();
}
// 監聽app_end
$this->hook->listen('app_end', $response);
return $response;
}
~~~
> 再舉例說明,如上面代碼中的
> $this->route->xx(),$this->request->xx(),$this->lang->xx()
> 與$this->env->xx()的原理相似
### 4 容器(Container)在應用中的配置注冊
> 為了方便使用容器功能
> think5.1支持在application的模塊中通過配置注冊自定義的類到容器
~~~
// library/think/App.php
public function init($module = '')
{
。。。。。。
// 注冊服務和容器對象實例
if (is_file($path . 'provider.php')) {
$this->container->bind(include $path . 'provider.php');
}
}
}
~~~
> 在模塊中配置文件provider.php中相關配置
> 將會注冊到系統容器中。
> 具體的使用方法見下面的 **容器基本使用章節**
* * * * *
### 5 容器(Container)在控制器基類(Controller)的使用
>應用業務的實現主要在繼承Controller基類的業務控制器中實現
>控制器基類(Controller)的部分屬性使用容器實現
~~~
// library/think/Controller.php
public function __construct()
{
$this->request = Container::get('request');
$this->app = Container::get('app');
$this->view = Container::get('view')->init(
$this->app['config']->pull('template'),
$this->app['config']->get('view_replace_str')
);
// 控制器初始化
$this->initialize();
// 前置操作方法
if ($this->beforeActionList) {
foreach ($this->beforeActionList as $method => $options) {
is_numeric($method) ?
$this->beforeAction($options) :
$this->beforeAction($method, $options);
}
}
}
~~~
> Controller的request,app,view等屬性
> 就是通過容器獲取的全局唯一Request,App,View對象實例
> 如果在業務控制器的方法中想獲取請求信息,系統運行信息,
> 就可以直接使用request和app屬性。
~~~
// application\index\controller\Index.php
<?php
namespace app\index\controller;
use think\Controller;
class Index extends Controller
{
public function index()
{
return '<style type="text/css">*{ padding: 0; margin: 0; } .think_default_text{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:)</h1><p> ThinkPHP V5<br/><span style="font-size:30px">十年磨一劍 - 為API開發設計的高性能框架</span></p><span style="font-size:22px;">[ V5.0 版本由 <a href="http://www.qiniu.com" target="qiniu">七牛云</a> 獨家贊助發布 ]</span></div><script type="text/javascript" src="http://tajs.qq.com/stats?sId=9347272" charset="UTF-8"></script><script type="text/javascript" src="http://ad.topthink.com/Public/static/client.js"></script><thinkad id="ad_bd568ce7058a1091"></thinkad>';
}
public function test(){
$request = $this->request;
echo '請求參數:';
echo "當前模塊名稱是" . $request->module();
echo "當前控制器名稱是" . $request->controller();
echo "當前操作名稱是" . $request->action();
}
}
~~~
> 如上$request,直接讀取request屬性,
> 通過Request對象獲取當前請求信息
> 同理可以通過app屬性獲取全局App對象,獲取系統的運行信息
## 第三節: 容器(Container)的總結
1. 容器(Container)主要用來管理對象
2. 容器(Container)的核心功能是緩存已創建對象實例
3. think5.1中的核心應用類App封裝了容器的功能
4. think5.1中可以通過操作核心應用類App的屬性方法獲取相應的對象實例
5. think5.1中控制器基類Controller的app,request,view屬性也是使用容器進行管理的
6. 容器(Container)主要用來管理對象。緩存已創建的對象實例。