实战案例
光说不干,事事落空;又说又干,马到成功。直接开干
新建文件kaka/container/countableTest.php,并且添加以下内容
接着在文件application/index/controller/Container.php中学会使用Countable。
这里注意一下用法,是直接使用count();
Countable中的count()跟平时使用count()方法有什么区别
顺便看一下PHP源码中的解释
可以看到第一个参数可以是数组也可是是countable
咔咔的理解是Countable只是重写了SPL中的count方法,为了就是方便定制自己需要的统计规则而已。
int count ( mixed $array_or_countable [, int $mode = COUNT_NORMAL ] )
count你不知道的用法
既然说到了这里,咔咔给大家在普及一个count不是很常用的一个用法。
在平时开发的过程中,这样的用法是最普遍的,也是大家最经常见到的一个使用案例。
但是如果这时给你一个多维数组,例如下图这样,让你统计这个多维数组,你该怎么统计呢!
这个时候估计大多数小伙伴的想法就是循环然后定义一个计数器累计。
其实count()函数在这一块就已经解决了这个需求。
下方打印结果就是"4----6"
直接使用count()函数一个数组得到的就是第一层数组的长度。
但是count()函数还有第二个参数,设置为1就是递归地计数数组中元素的数目(计算多维数组中的所有元素)
所以你这时在去看文档就会发现,count()函数本身就有俩个参数
第一个参数是必须饿,选择是数组
第二个参数默认是0就是不对多维数组中的所有元素进行计数
当第二个参数为1时就是递归的计算多维数组中的所有元素。
七、Container容器类剖析
上文中实现了一个自己创建的容器,接下来看看源码中的容器,经过了上文容器中出现的技术点都已经囊括完了。
在接下里阅读容器源码就不会很吃力,如果之前的文章没看,一定要大概过一遍哈!
大家无数次打开的一个文件public/index.php。
曾有多少次打开这个文件想对源码进行一探究竟,但是看着看着就放弃了。
经过之前的注册树模式之后,你肯定就会明白这行代码会返回什么Container::get('app')
这行代码返回就是app的实例,可以进行简单的断点一下。
可以看到返回就是app类里边的众多属性。
所以说注册树模式不会的在继续返回去看之前写的,要不越看越迷糊。
那么框架中的容器是怎么定义的呢!它到底是怎么实现的呢!
也就是只需要去关注这个get()方法做的事情就可以了。
代码就会追踪到文件thinkphp/library/think/Container.php中的get()方法
这里的getInstance()方法不陌生了吧!这就是上文说过的单例模式。
可以进行代码追踪getInstance()这个方法,你就会在同文件中看到这个单例模式的方法,返回Container实例。
Container实例调用make方法
代码static::getInstance()返回了Container的实例后,就会去调用本类的make方法,接下来就是对make方法进行详解了。
在开始阅读make方法里边的源码之前,我们需要先对几个属性进行简单的梳理一下。
这四个属性一定要有点印象,并且一定要区别instance和instances。
这俩个属性一个是单例模式返回当前类的实例,一个是容器中的所有的实例。
第一次执行结果
/** * 创建类的实例 * @access public * @param string $abstract 类名或者标识 * @param array|true $vars 变量 * @param bool $newInstance 是否每次创建新的实例 * @return object */ public function make($abstract, $vars = [], $newInstance = false) { // 判断$vars这个变量是否为true if (true === $vars) { // 总是创建新的实例化对象 $newInstance = true; $vars = []; } // app 这里就是在容器别名里获取传递过来的app 如果没有则就是app $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; // 从容器实例中获取 如果存在则直接返回对应的实例 也就是使用注册树模式 if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } // think\App 从容器标识中获取 if (isset($this->bind[$abstract])) { // 将think\App 复制给$concrete变量 $concrete = $this->bind[$abstract]; // 用于代表匿名函数的类 判断是不是闭包 if ($concrete instanceof Closure) { $object = $this->invokeFunction($concrete, $vars); } else { // $this->name['app'] = think\App $this->name[$abstract] = $concrete; // 在执行一次本类的make方法,也就是本方法 return $this->make($concrete, $vars, $newInstance); } } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; }
这是第二次执行流程
public function make($abstract, $vars = [], $newInstance = false) { // 判断$vars这个变量是否为true if (true === $vars) { // 总是创建新的实例化对象 $newInstance = true; $vars = []; } // app 这里就是在容器别名里获取传递过来的app 如果没有则就是app // 第二次执行时 $abstract = think\App $abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract; // 从容器实例中获取 如果存在则直接返回对应的实例 也就是使用注册树模式 if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } // think\App 从容器标识中获取 // 第二次执行$this->bind['think\App']不存在走else if (isset($this->bind[$abstract])) { // 将think\App 复制给$concrete变量 $concrete = $this->bind[$abstract]; // 用于代表匿名函数的类 判断是不是闭包 if ($concrete instanceof Closure) { $object = $this->invokeFunction($concrete, $vars); } else { // $this->name['app'] = think\App $this->name[$abstract] = $concrete; // 在执行一次本类的make方法,也就是本方法 // think\App return $this->make($concrete, $vars, $newInstance); } } else { // think\App $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { // 把创建的容器存起来 //$this->instances['think\App'] = $object; $this->instances[$abstract] = $object; } return $object; }
public function invokeClass($class, $vars = []) { try { /** * ReflectionClass Object ( [name] => think\App ) */ // 这里就是之前文章提到的反射 $reflect = new ReflectionClass($class); if ($reflect->hasMethod('__make')) { $method = new ReflectionMethod($class, '__make'); if ($method->isPublic() && $method->isStatic()) { $args = $this->bindParams($method, $vars); return $method->invokeArgs(null, $args); } } // 通过反射获取think\App的构造函数 $constructor = $reflect->getConstructor(); $args = $constructor ? $this->bindParams($constructor, $vars) : []; // 从给出的参数创建一个新的类实例 return $reflect->newInstanceArgs($args); } catch (ReflectionException $e) { throw new ClassNotFoundException('class not exists: ' . $class, $class); } }