[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)主要用来管理对象。缓存已创建的对象实例。