tp给我们提供啦一个容器的概念,用来储存我们生产的类实例。在下次需要使用类实例时可以快速获取。
那app()助手函数是如何工作的:
通过查看助手文件得到他其实调用的是:Container::get()方法。
function app($name = 'think\App', $args = [], $newInstance = false) { return Container::get($name, $args, $newInstance); }
我们在去看Container::get()方法。
public static function get($abstract, $vars = [], $newInstance = false) { //通过getInstance方法创建一个当前容器的实例(单例模式)再去调用make()方法 return static::getInstance()->make($abstract, $vars, $newInstance); }
我们来看make()方法:这个方法很重要,门面其实也是通过这个方法来创建对象。
public function make($abstract, $vars = [], $newInstance = false) { if (true === $vars) { // 总是创建新的实例化对象 $newInstance = true; $vars = []; } static $i = 0; $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; //通过容器获取类的对象 if (isset($this->instances[$abstract]) && !$newInstance) { // echo '容器中'.$i++.'<br>'; return $this->instances[$abstract]; } //通过反射创建类的对象 if (isset($this->bind[$abstract])) { $concrete = $this->bind[$abstract]; if ($concrete instanceof Closure) { $object = $this->invokeFunction($concrete, $vars); echo '反射'.$i++.PHP_EOL; } else { $this->name[$abstract] = $concrete; return $this->make($concrete, $vars, $newInstance); } } else { echo '反射'.$i++.PHP_EOL; $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; }
我为了证明app()在调用的时候是通过容器来获取对象的还是通过反射来获取对象的,在这个类中定义了一个:static $i = 0;和echo '反射'.$i++.PHP_EOL;来输出调用的方法和次数。方便观察。
当我的方法为空时:
输出为:
为什么会使出 17次的放射呢?
因为在框架初始化的时候会来获取一些默认的类对象(大概是这个意思)。所以这前17次输出都和我们没有关系。
现在我在方法里调用一次我自己定义的一个类文件。
再看输出
这是输出多啦一次 ‘反射’这说明当我们通过app()来获取一个我们未向容器注册过的类的时候,他会通过我们提供的命名空间,用反射来创建这个类的对象。
当我们调用两次app()来获取相同的类对象时:
public function lize(){ // bind('Redis',new \Redis); $redis = app('Redis'); $redis = app('Redis'); }
再看输出:
并没有多输出一次‘反射’,这说明我们第二次调用的时候并没有通过反射来获取类对象。而是做的容器。
总结:
当我们通过app()来获取一个类对象时,先会在容器里获取,获取不到去通过反射来获取。反射获取对象成功后,在吧这个对象放到容器里。下次获取的时候直接去容器里获取。(去不去容器里获取还可以通过,app()的第二个参数来决定,当为true时就不会走容器,不管容器里有没有。)