1 Star 0 Fork 0

零壹工作室 / tp6start

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
01.02容器和服务.md 5.67 KB
一键复制 编辑 原始数据 按行查看 历史
守信哥 提交于 2020-02-02 14:18 . 文件内容添加格式

容器

容器类提供了框架类(这里通常指动态类)的依赖管理和依赖注入,容器类功能本身是由think\Container类完成,但实际在应用的运行过程中,都是委托think\App类来进行容器类的管理。所以,大部分情况下,你都无需直接操作容器类think\Container本身。

新版的容器支持PSR-11规范,容器类的功能特性主要包括:

  • 绑定类、对象实例、接口到容器
  • 创建类的实例(存在则直接获取)
  • 容器对象绑定别名
  • 支持容器对象(实例化)回调
  • 获取容器对象实例
  • 删除容器中的对象实例
  • 依赖注入实现
  • 调用容器对象实例的方法(或者闭包)
  • 提供容器对象的ArrayAccess支持

获取容器对象

获取容器中对象的最简单方法就是通过依赖注入,如果你要手动获取的话,事实上在容器中获取对象实例仅仅需要统一使用一个app助手函数就可以了,你不需要每次手动new一个对象实例,要获取的对象实例要么已经存在于容器中,要么会在你第一次调用的时候自动实例化。

// 获取缓存对象实例
$cache = app('cache');

cache是容器内置给think\Cache类绑定的一个容器标识,对于没有绑定的类,应该使用完整的类名

$test = app('app\common\Test');

获取当前应用对象实例,只需要调用一个没有任何参数的app函数即可

$app = app();

如果你需要对某个容器对象的实例化进行自定义,可以定义一个静态的__make方法,在该方法的参数中可以支持依赖注入。

namespace app;

class Cookie
{
    /**
     * 构造方法
     * @access public
     */
    public function __construct(Request $request, array $config )
    {
        $this->request = $request;
        $this->config  = $config;
    }

    public static function __make(Request $request, Config $config)
    {
        return new static($request, $config->get('cookie'));
    }
}

如果你直接实例化app\Cookie类,必须传入request参数以及config参数,但定义了__make后,就可以通过依赖注入或者app方法自动实例化Cookie类。

依赖注入

在应用开发的过程中,有很多的场景支持直接指定方法参数的类型为某个对象,在调用该方法的时候就会自动进行实例化,也就是通常所说的依赖注入。

支持使用依赖注入的场景通常包括(但不限于):

  • 控制器架构方法;
  • 控制器操作方法;
  • 路由的闭包定义;
  • 事件类的执行方法;
  • 中间件的执行方法;

你可以像下面这样,在控制器的架构函数和操作方法中进行灵活的依赖注入。

<?php
namespace app\controller;

use think\Cache;
use think\Request;

class Index
{
    protected $request;

    public function __construct(Request $request)
    {
        $this->request = $request;
    }

    public function test(Cache $cache)
    {
        $cache->set('name', 'test');
        return 'This is ' . $this->request->action() . '!';
    }
}

事实上你可以在方法中依赖注入多个对象参数,并且和顺序无关。

如果你需要对自己的类库支持依赖注入,只需要在调用的时候使用invoke方法即可,例如

class Foo 
{
    public function __construct(Bar $bar)
    {
    }
}

如果使用容器来实例化的话,可以自动进行依赖注入。

// 实例化Foo对象,并支持依赖注入
$foo = invoke('Foo');
如果要对某个方法支持依赖注入,可以使用

class Foo 
{
    public function bar(Bar $bar)
    {
        // ...
    }
}
// 调用Foo对象的bar方法,支持依赖注入
$result = invoke(['Foo', 'bar']);

服务提供者

容器中的对象都是可以提前注入的,所以很容易替换成另外一个实现。你可以通过服务提供者来指定或者替换容器对象,例如,我们在app\provider.php文件中定义如下:

use app\Request;

return [
    'think\Request'  => Request::class,
];

作用其实是把容器中的think\Request对象替换为app\Request对象,但实际上,你仍然只需要和往常一样操作容器中的think\Request类即可。

namespace app;
use think\Request;

class Index 
{
    public function index(Request $request)
    {
    }
}

这里的$request对象其实已经是一个app\Request对象实例,但我们依赖注入的其实是think\Request类,没错,但这并不是什么魔法,不过是一种叫做容器绑定的功能而已。

学会了这招,你就可以对容器中的对象进行偷天换日。

门面

为了方便单元测试和长连接使用,核心框架没有任何的静态类,系统给核心常用类库都定义了门面(Facade),其作用可以简单的理解为给类的动态方法调用提供了静态代理,门面对象操作的每个对象都是容器中的对象实例。

你在官方手册中或者本书中如果看到核心类库使用了静态方法,都是使用了门面操作的,在使用之前务必引入相关的门面类,而不是实际的动态类。

很典型的例子是,如果你看到下面的代码,就必须明白这里的Db类其实是think\facade\Db而非think\Db,类似的问题后面不会重复说明。

Db::name('user')->find();

但所有的依赖注入使用的类必须是实际的动态类或者接口,不能使用门面类。关于依赖注入的细节后面再讲,虽然门面对象和依赖注入都是从容器中获取对象,不过如果从性能上说,依赖注入略有优势。

不要给模型定义门面,是不符合规范的,况且模型的查询操作本身都是静态方法调用的。

1
https://gitee.com/wgsuxin/tp6start.git
git@gitee.com:wgsuxin/tp6start.git
wgsuxin
tp6start
tp6start
master

搜索帮助