一、container容器类剖析

Countable巧用

Container容器类文件是在thinkphp\library\think目录下的,我认为它是框架的一个精髓,它能够很方便的管理框架的类,方便我们使用。
image.png
在Container中,它用到了很多类,还有反射机制,所以反射机制是需要我们去了解的一个东西。其中Countable是php内置的一个类,接下来我们进行对它的演示。
首先在extend目录下创建一个TestCountable类,然后它继承Countable,因为是继承的Countable,需要把其中的count方法定义出来,不然会报错,然后在count里面随便返回内容。接着在控制器index.php中创建方法去调用这个类,但是它的调用和其他的有点区别,常规调用是这样的,$obj=new TestCountable(); $a = $obj->count(); 而Countable是这样的$obj = new TestCountable(); $a = count($obj);。这就是它的区别。

获取容器内的实例分析

$this->instance 获取容器对象实例
$this->bind 容器绑定标识
image.png
$this->name 容器标识别名
[APP] => think\App
[log] => think\Log
类似于容器绑定标识的内容
$this->instances容器中的对象的实例
[think\App] => think\App Object
[think\Config] => think\Config Object等等。

那么我们如何去学习容器呢,我们还是结合框架代码来进行分析。
首先我们定位到入口文件index.php
image.png
这里它用了Container的get方法,并传入了一个app参数,我们来看一下get方法的逻辑。
image.png
get方法是获取容器中的对象实例。这里它传入了三个参数,三个参数各是什么意思,注释上面有,传入的app对应的就是think\App,然后它用了单例模式,getInstance就是一个单例,然后调用make方法。
image.png
首先会对$vars进行判断,如果是true,就把它置空,让$newInstace为true。接下来进行判断$this->name[$abstract],由于传入参数是app,$abstract => app。然后判断$abstract有没有实例,默认是没有的,就返回一个实例。然后接着就是容器绑定标识,$concrete对应的就是think\App。接着如果它是闭包的话,就会走到invokeFunction,如果不是,就设置容器标识别名,相当于$this->name["app"] = think\App 。然后再去调用make方法,这个时候,传的参数就不一样了,第一个参数就成为think\App,这个时候两个isset都是没有的,就不会走这两个逻辑。直接跳到invokeClass方法,这个时候会返回一个对象。然后把这个对象放入容器中的对象实例,最后返回这个对象。
invokeClass方法
image.png
现在我们讲一下invokeClass方法,这个方法传入了两个参数,第一是类名,第二是它的参数。然后实例化反射类ReflectionClass,然后判断这个类里面是否有__make方法,如果有的话就获取到这个类,接着判断这个类的属性是不是public或者static,如果是的,就进行实例化,然后返回。而__make方法在Config类里是存在的,所以这里实例化的是一个Config类。

容器类的使用场景

容器类可以这样使用,这种是使用的Facade门面的方法来使用的。
image.png
第二种可以这样使用,类似入口文件那样。这两种都能打印出app的config配置。
image.png
我们还有另外一种使用场景的,这种场景我们使用到了php的助手函数,在helper.php中的app方法中。
image.png
我们可以这样用,直接app("config);,这样可以直接拿到配置。

二、Facade门面模式

门面为容器中的类提供了一个静态调用接口,相比于传统的静态方法调用,带来了更好的可测试性和扩展性。接下来还是通过代码来了解。
在thinkphp\library\think目录下有一个Facade文件,它相当于门面的一个父类,子类在think\facade目录下,里面有许多的类,它们都需要继承Facade父类。这些子类中都只有一个方法,getFacadeClass方法,里面逻辑就是返回绑定的标识。它的作用是在Facade里面体现的。
image.png
这里获取到这个标识,然后判断有没有这个标识,如果有就返回这个实例,如果没有,就绑定到标识中去。

我们接下来通过演示来分析一下Facade代码。
我们在index控制器里面创建一个facade方法,然后打印Config::get("app");,它是怎么走的呢,它会找到think\facade\Config.php这个文件,但是在这个文件里面是没有get方法的,而且在它的父类Facade里面也没有get方法,那么它是怎么执行的呢。由于Facade里面没有get方法,那么它会走到__callStatic方法,然后去创建一个get方法。接下来我们来详细讲解一下__callStatic方法。我们创建一个Test类,在Test类里面添加__callStatic方法,然后打印一下传入的参数,在应用层index.php去调用一下这个类,代码如下:
image.png
image.png
我们调用的abcd方法在Test里面是不存在的,我们看一下打印的参数是什么。
image.png
我们继续看Facade类的__callStatic方法,
image.png
在这里会执行createFacade方法,这个方法我们在上面解释过了,所以最终会把get放入$this->[name]标识里面去。最终返回的是一个实例。

结合上面的描述,facade方法就是根据容器里面是否有静态方法get,如果有调用,如果没有就进行实例化,进行调用。

Facade图例

image.png

Facade实战

Facade有两个使用场景,第一个:
我们在app目录下创建common和facade文件夹,然后分别在两个目录下创建Test文件,common下的Test里面创建test方法,并输出内容。facade目录下需要继承Facade门面类,然后调用getFacadeClass方法,然后返回common下Test的路径,然后在应用层去调用appfacadeTest::test();
第二种:
facade目录下的Test文件不再写方法,然后在控制器里面这样使用。Facade::bind('app\facade\Test','app\common\Test'); app\facade\Test::test();


begin
1 声望3 粉丝

我有一剑,可搬山,断江,倒海,降妖,镇魔,敕神,摘星,摧城,开天!