## [【thinkphp6源码分析三】 APP类之父, 容器Container类](https://www.cnblogs.com/dk1988/p/14513837.html)
上一篇关于APP类的构造函数 最后还有三句话
~~~
1 static::setInstance($this);
2 $this->instance('app', $this);
3 $this->instance('think\Container', $this);
~~~
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
~~~
1 /**
2 * 绑定一个类实例到容器
3 * @access public
4 * @param string $abstract 类名或者标识
5 * @param object $instance 类的实例
6 * @return $this
7 */
8 public function instance(string $abstract, $instance)
9 {
10 $abstract = $this->getAlias($abstract);
11
12 $this->instances[$abstract] = $instance;
13
14 return $this;
15 }
~~~
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
这里基本都涉及到APP的父类**Container**\[tp6\\vendor\\topthink\\framework\\src\\think\\Container.php\]了
第一句话 是设置了Container类的$instance属性 把这个属性设置为APP类的实例
这里可以理解为 Container是一个比较抽象的容器类,它可以作用于任何框架,而APP类则相当于这个抽象容器的实现,也就是适合TP框架的一个容器类
\[对应前面说过的注册树模式 这里是实例化一个适用于TP框架的 TP容器注册树\]
第二句和第三句 是设置容器里面的东西 将具体的类实例放到容器中仔细看 它们是设置了一个叫
instances数组(这里多了一个s)的内容
\[对应前面说过的注册树模式 这里是开始给这棵树挂苹果了 目前挂了两个苹果
一个叫app 一个叫think\\Container 俩苹果的内容是相同的 后面 框架一定会给这棵树挂更多的苹果 如db config cache等等\]
我们继续看Container类
Container类本身代码并不复杂 但是看得相当懵逼,因为太抽象了 ,光看这个代码根本不知道这些代码干嘛的?
但实际上 这个基本 可以说是所有框架的基石不为过
它的细节作用 其实从它的第一句话可以看出来
***class Container implements ContainerInterface, ArrayAccess, IteratorAggregate, Countable***
它分别实现了***ContainerInterface(容器) ;***ArrayAccess(数组式访问); ***IteratorAggregate(聚合迭代器);***Countable(可计数的)************
上面()里面的中文只是这些单词的翻译,实际上也的确就是这些翻译的字面意思
***第一个ContainerInterfac**e*先放一放 它自己都叫容器 那么容器接口的实现当然是它最主要的功能
***我们看第二个ArrayAccess***
这东西叫数组式访问 那么是把啥当数组式访问呢 也就对象了
我们在用TP框架的时候 应该没少写或者见过这样的东西
$user=new UserModel()
$user\['username'\] = "卢小霏";
按照我们正统语法 这样写是错误的 $user 是个对象 要使用它的属性 应该用$user->username呀?
但由于数组和对象这俩有比较多的共同点,PHP就提供了******ArrayAccess******接口 只要我们实现它 上面例子 UserModel就实现了ArrayAccess 就能把对象当做数组来使用了
ArrayAccess类我们可以看一下 实际很简短
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
~~~
interface ArrayAccess {
public function offsetExists($offset);
public function offsetGet($offset);
public function offsetSet($offset, $value);
public function offsetUnset($offset);
}
~~~
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
就这么四个方法 分别是判断是否存在,获取,设置,删除,(也数组的增删改查)
那么我们想一下一个对象,肯定可有对一个对象的属性进行增删改查的行为吧
在这里Container这个类里面 这四个方法 对应的实现是
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
~~~
public function offsetExists($key)
{
return $this->exists($key);
}
public function offsetGet($key)
{
return $this->make($key);
}
public function offsetSet($key, $value)
{
$this->bind($key, $value);
}
public function offsetUnset($key)
{
$this->delete($key);
}
~~~
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
其中 $this->bind我们前面说过 它有两个作用 关于第二个作用 就是把一个类实例塞到容器中, 也就是一开始那个instances数组里面插入一条 以便后面使用
make暂不解释,delete和exists比较简单,分别从instances数组里面删除一个元素;判断instances数组里面是否存在等
这里既然Contaner类实现了ArrayAccesss 且给四个方法实现了具备自己特色的功能 那么在实际场景中 我们该如何去使用呢
我们可以在TP原来的index控制器 \[tp6\\app\\controller\\Index.php\]文件下 写点测试代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)View Code
由于APP这个类继承了Container Container这个类又实现了ArrayAccess
那么我们就可以像玩数组一样去 玩$app类
$app\[\] = xxx ====》这样就等同于去执行$app类里面的bind方法 最终效果是$instances 里面 会多出user 和user2 两个对象实例
然后既然是数组 unset这个函数也可以使用
unset($app\['user2'\]) ===>这个就等同于去执行$app类里面的delete方法 最终就是把$instances里面的对应元素进行删除
这样使用的话 是不是就发现很方便呢
*********第三个IteratorAggregate(聚合迭代器)*********这个目前框架上的应用不是很多 配合yield关键字 可以达到一种省内存的目的,一般可以用于Excel大数据导出之类
如果以后遇到例子再细讲
*********************第四个Countable(可计数的)*********************对象里面的统计功能,在Container类里面 它统计了instaces里面的元素个数,也就是有多少“东西”已经在容器中
最后 我们再回到容器类的核心部分 它首先实现了***ContainerInterface***这个接口 这个接口的作用很简单 一个get 一个has
get 的实现 实际我们第一章就遇到过 它们通过make函数 来创建容器里面的实例(存在就直接读取 不重复创建) has则是判断容器里面某个实例是否存在
\[关于make函数我们先大体上了解它的作用记性
极其简单的可以先认为 它等同于New关键字 即实例化一个类从而获得一个对象;
它有些其它的功能相当复杂 主要是涉及到依赖注入和反射相关知识 这个后面的章节会仔细讲到的\]
make函数里面有 $this->instances\[$abstract\] = $object; 这一句话
这玩意那实际就和bind函数一样了
这里就是make先创建出一个实例 然后塞到容器里面
关于bind和make 我们可以在index控制 随意写点测试代码
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
~~~
1 public function index()
2 {
3 $app =new App();
4
5 $userModel =new User();
6 //先注释之前 先捋一下类 和实例的关系 app\model\User是类 new app\model\User()是实例
7 //给app\model\User 加一个别名 myUserClass 这个别名会在app-> $bind属性里面
8 $app->bind("myUserClass","app\model\User");
9
10 //将User类的实例 $userModel 放到容器中 并且给个别名 myUser 这个myUser会在app -> $instances里面
11 $app->bind("myUser",$userModel);
12
13 //利用make函数 去实例化User类 这里完全等同于 new app\model\User()
14 $userModel1 =$app->make("app\model\User");
15 //由于 app\model\User 类有个别名 myUserClass 所以这里可以直接这样去make
16 $userModel2 =$app->make("myUserClass");
17 //由于上面的类实例 $userModel 也有别名了 这里也可以直接使用myUser 相当于去$instances数组里面 直接找到它来使用
18 $userModel3 =$app->make("myUser");
19
20 dump($app);
21 dump($userModel1);
22 dump($userModel2);
23 dump($userModel3);die;
24 //return $str ;
25
26 }
~~~
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
可以观察下打印结果$app类里面 $bind 属性和 $instances 属性的变化
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
~~~
^ think\App {#50 ▼
#appDebug: false
#beginTime: null
#beginMem: null
#namespace: "app"
#rootPath: "D:\php\wamp64_3.17\www\tp6\"
#thinkPath: "D:\php\wamp64_3.17\www\tp6\vendor\topthink\framework\src\"
#appPath: "D:\php\wamp64_3.17\www\tp6\app\"
#runtimePath: "D:\php\wamp64_3.17\www\tp6\runtime\"
#routePath: ""
#configExt: ".php"
#initializers: array:3 [▶]
#services: []
#initialized: false
#bind: array:27 [▼
"app" => "think\App"
"cache" => "think\Cache"
"config" => "think\Config"
"console" => "think\Console"
"cookie" => "think\Cookie"
"db" => "think\Db"
"env" => "think\Env"
"event" => "think\Event"
"http" => "think\Http"
"lang" => "think\Lang"
"log" => "think\Log"
"middleware" => "think\Middleware"
"request" => "think\Request"
"response" => "think\Response"
"route" => "think\Route"
"session" => "think\Session"
"validate" => "think\Validate"
"view" => "think\View"
"filesystem" => "think\Filesystem"
"think\DbManager" => "think\Db"
"think\LogManager" => "think\Log"
"think\CacheManager" => "think\Cache"
"Psr\Log\LoggerInterface" => "think\Log"
"think\Request" => "app\Request"
"think\exception\Handle" => "app\ExceptionHandle"
"myRequest" => "app\Request"
"myUserClass" => "app\model\User"
]
#instances: array:4 [▼
"think\App" => think\App {#50}
"think\Container" => think\App {#50}
"myUser" => app\model\User {#51}
"app\model\User" => app\model\User {#53}
]
#invokeCallback: []
}
^ app\model\User {#53}
^ app\model\User {#53}
^ app\model\User {#51}
~~~
[![复制代码](https://common.cnblogs.com/images/copycode.gif)](javascript:void(0); "复制代码")
- thinkphp6执行流程(一)
- php中use关键字用法详解
- Thinkphp6使用腾讯云发送短信步骤
- 路由配置
- Thinkphp6,static静态资源访问路径问题
- ThinkPHP6.0+ 使用Redis 原始用法
- smarty在thinkphp6.0中的最佳实践
- Thinkphp6.0 搜索器使用方法
- 从已有安装包(vendor)恢复 composer.json
- tp6with的用法,表间关联查询
- thinkphp6.x多对多如何添加中间表限制条件
- thinkphp6 安装JWT
- 缓存类型
- 请求信息和HTTP头信息
- 模型事件用法
- 助手函数汇总
- tp6集成Alipay 手机和电脑端支付的方法
- thinkphp6使用jwt
- 6.0session cookie cache
- tp6笔记
- TP6(thinkphp6)队列与延时队列
- thinkphp6 command(自定义指令)
- command(自定义指令)
- 本地文件上传
- 缓存
- 响应
- 公共函数配置
- 七牛云+文件上传
- thinkphp6:访问多个redis数据源(thinkphp6.0.5 / php 7.4.9)
- 富文本编辑器wangEditor3
- IP黑名单
- 增删改查 +文件上传
- workerman 定时器操作控制器的方法
- 上传文件到阿里云oss
- 短信或者邮箱验证码防刷代码
- thinkphp6:访问redis6(thinkphp 6.0.9/php 8.0.14)
- 实现关联多个id以逗号分开查询数据
- thinkphp6实现邮箱注册功能的细节和代码(点击链接激活方式)
- 用mpdf生成pdf文件(php 8.1.1 / thinkphp v6.0.10LTS )
- 生成带logo的二维码(php 8.1.1 / thinkphp v6.0.10LTS )
- mysql数据库使用事务(php 8.1.1 / thinkphp v6.0.10LTS)
- 一,创建过滤IP的中间件
- 源码解析请求流程
- 验证码生成
- 权限管理
- 自定义异常类
- 事件监听event-listene
- 安装与使用think-addons
- 事件与多应用
- Workerman 基本使用
- 查询用户列表按拼音字母排序
- 扩展包合集
- 查询用户数据,但是可以通过输入用户昵称来搜索用户同时还要统计用户的文章和粉丝数
- 根据图片的minetype类型获取文件真实拓展名思路
- 到处excel
- 用imagemagick库生成缩略图
- 生成zip压缩包并下载
- API 多版本控制
- 用redis+lua做限流(php 8.1.1 / thinkphp v6.0.10LTS )
- 【thinkphp6源码分析三】 APP类之父, 容器Container类
- thinkphp6表单重复提交解决办法
- 小程序授权
- 最简单的thinkphp6导出Excel
- 根据访问设备不同访问不同模块
- 服务系统
- 前置/后置中间件
- 给接口api做签名验证(php 8.1.1 / thinkphp v6.0.10LTS )
- 6实现邮箱注册功能的细节和代码(点击链接激活方式)
- 使用前后端分离的验证码(thinkphp 6.0.9/php 8.0.14/vue 3.2.26)
- 前后端分离:用jwt+middleware做用户登录验证(php 8.1.1 / thinkphp v6.0.10LTS )
- vue前后端分离多图上传
- thinkphp 分组、页面跳转与ajax
- thinkphp6 常用方法文档
- 手册里没有的一些用法
- Swagger 3 API 注释
- PHP 秒级定时任务
- thinkphp6集成gatewayWorker(workerman)实现实时监听
- thinkphp6按月新增数据表
- 使用redis 实现消息队列
- api接口 统一结果返回处理类
- 使用swoole+thinkphp6.0+redis 结合开发的登录模块
- 给接口api做签名验证
- ThinkPHP6.0 + UniApp 实现小程序的 微信登录
- ThinkPHP6.0 + Vue + ElementUI + axios 的环境安装到实现 CURD 操作!
- 异常$e
- 参数请求验证自定义和异常错误自定义