1、首先Container继承3个接口,分别是ArrayAccess, IteratorAggregate, Countable,不了解的童鞋可以去看看手册
2、Container使用了单例和注册树模式,通过静态方法getInstance()获取类对象
将容器中的对象实例保存在类的成员变量$instances[]中
另外还有成员变量$bind[]和$name ,分别是容器绑定标识和容器标识别名,这里建议大家将这些变量打印出来看下数据格式,思路会比较清晰
3、在通过容器获取类的实例时,可以通过助手函数app()或Container::get()传入类名或类别名获取,但其实助手函数app()也是通过调用Container::get()实现的
而get()方法会调用make()方法,make()方法才是容器类的核心,注意调用make方法时是通过单例调用的
4、make()方法
- make()方法,本类的核心,三个参数
第二个参数默认为空数组,若传true则说明总是创建新的实例化对象,否则代表需创建实例类的参数数组(这个设计很怪)。 - 我们以$abstract=‘app’为例分析代码
这里若$this->name数组中已存在’app’,则直接返回其值,‘app’就是’\think\App’的类标识,在name数组中,$this->name[‘app’]=‘think\App’。但在容器第一次获取app类的实例时,这里name为空,所以这里的$abstract = ‘app’ - 同理,instances保存类的实例,若app已实例化过,则直接从instances中返回该实例,但在第一次执行时,instances中不存在’app’,故程序继续向下执行
- $this->bind[‘app’] = ‘think\App’ 所以isset()为ture,第一次走到if语句中,$concrete=‘think\App’,但$concrete不是闭包,即$concrete instanceof Closure 为false,这里走进else语句,设置$this->name[‘app’]=‘think\App’,然后调用自身,注意这时$concrete=‘think\App’
- make方法第二次调用,同样$this->name和$this->instances中不存在‘think\App’,在外层if判断中这时走到else模块中,调用invokeClass()方法实例化类,并将实例保存在$this->instances中,下次在请求时,会从中直接返回该实例
5、invokeClass()方法分析
- 还是以app类为例,这里$class=‘think\App’,首先通过调用反射类ReflectionClass,判断$class类中是否存在‘__make’方法,若存在则调用
ReflectionMethod反射类,ReflectionMethod传入两个参数,类名和方法名,返回ReflectionMethod对象实例,注意若方法在类中不存在PHP会报致命错误
- 通过isPublic和isStatic判断‘__make’方法是否是公开的和静态的,若是则走进if语句中,调用bindParams()绑定参数,返回参数数组,然后通过
invokeArgs()方法调用类中的’__make’方法,返回类的实例,注意这里invokeArgs()第一个参数null,当方法是静态方法时,第一个参数可以为空,否则必须为类的实例
- APP中没有‘__make’方法所以程序继续向下执行,通过反射类调用getConstructor()方法获取类的构造函数,注意这里返回的是ReflectionMethod对象,然后调用bindParams()方法绑定参数,最后调用ReflectionClass反射类的newInstanceArgs()方法,该方法作用是从给出的参数创建一个新的类实例,将该实例返回