[TOC]
* * * * *
## 1 Container實現
### 0 實現文件
~~~
/lirary/think/Container.php
~~~
### 1 核心方法
#### bind() 注冊內容到容器
~~~
public function bind($abstract, $concrete = null)
{
if (is_array($abstract)) {
$this->bind = array_merge($this->bind, $abstract);
} elseif ($concrete instanceof \Closure) {
$this->bind[$abstract] = $concrete;
} elseif (is_object($concrete)) {
$this->instances[$abstract] = $concrete;
} else {
$this->bind[$abstract] = $concrete;
}
return $this;
}
~~~
> 主要分為四種綁定
> * 批量綁定
> * 閉包綁定
> * 對象綁定
> * 類標識/接口綁定
> 其中對象綁定添加綁定內容到Container的instances。
> 其他三種綁定添加綁定內容到Container的bind中
* * * * *
#### make() 獲取綁定內容
~~~
public function make($abstract, $vars = [], $newInstance = false)
{
if (true === $vars) {
// 總是創建新的實例化對象
$newInstance = true;
$vars = [];
}
if (isset($this->instances[$abstract]) && !$newInstance) {
$object = $this->instances[$abstract];
} else {
if (isset($this->bind[$abstract])) {
$concrete = $this->bind[$abstract];
if ($concrete instanceof \Closure) {
$object = $this->invokeFunction($concrete, $vars);
} else {
$object = $this->make($concrete, $vars, $newInstance);
}
} else {
$object = $this->invokeClass($abstract, $vars);
}
if (!$newInstance) {
$this->instances[$abstract] = $object;
}
}
return $object;
}
~~~
> * 首先判斷$vars值是否為true。用來判斷是強制創建新的對象實例
> * 然后 讀取Container中的instances中是否已有實例內容
> * 如果Container的instance沒有實例內容,則讀取Container的bind中綁定的實現
> 1. 如果bind中的是閉包Closure則執行閉包,獲取執行結果
> 2. 不是閉包(類,接口)則繼續重新調用make,這時則會執行 $object = $this->invokeClass($abstract, $vars);
> * 將執行的結果保存到Container的instances中作為緩存。
> * 然后執行結果的object。
> make()主要是用來讀取instances中緩存的實例內容。
> 如果沒有則從bind中綁定的內容進行創建。
> 然后緩存到instances中,緩存后下次則讀取instances中緩存的內容
> 如果強制創建新的,則從bind重新創建實例,緩存到instances中
* * * * *
### 2 上層方法
#### get() 獲取綁定內容
~~~
public static function get($abstract, $vars = [], $newInstance = false)
{
return static::getInstance()->make($abstract, $vars, $newInstance);
}
~~~
> 直接調用make()方法,與make方法過程一致。
* * * * *
#### set() 注冊內容到容器
~~~
public static function set($abstract, $concrete = null)
{
return static::getInstance()->bind($abstract, $concrete);
}
~~~
> 直接調用bind()方法,與bind方法過程一致。
* * * * *
#### instance() 綁定實例
~~~
public function instance($abstract, $instance)
{
if (isset($this->bind[$abstract])) {
$abstract = $this->bind[$abstract];
}
$this->instances[$abstract] = $instance;
return $this;
}
~~~
> 注冊實例到Container的instances中
* * * * *
#### bound() 檢測是否綁定
~~~
public function bound($abstract)
{
return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
}
~~~
> 檢測是否已綁定過程。
* * * * *
### 3 底層實現
#### getInstance()
~~~
public static function getInstance()
{
if (is_null(static::$instance)) {
static::$instance = new static;
}
return static::$instance;
}
~~~
> 獲取全局唯一容器對象。
> 經典的單例模式。
* * * * *
#### invokeFunction() 反射執行函數或閉包
~~~~
public function invokeFunction($function, $vars = [])
{
$reflect = new \ReflectionFunction($function);
$args = $this->bindParams($reflect, $vars);
return $reflect->invokeArgs($args);
}
~~~~
> ReflectionFunction反射執行函數或閉包。
* * * * *
#### invokeMethod() 反射執行類方法或對象方法
~~~
public function invokeMethod($method, $vars = [])
{
if (is_array($method)) {
$class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
$reflect = new \ReflectionMethod($class, $method[1]);
} else {
// 靜態方法
$reflect = new \ReflectionMethod($method);
}
$args = $this->bindParams($reflect, $vars);
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
}
~~~
> ReflectionMethod 反射執行類方法或靜態方法
* * * * *
#### invoke() 反射執行(上面兩個的封裝)
~~~
public function invoke($callable, $vars = [])
{
if ($callable instanceof \Closure) {
$result = $this->invokeFunction($callable, $vars);
} else {
$result = $this->invokeMethod($callable, $vars);
}
return $result;
}
~~~
> 調用invokeFunction()或者invokeMethod()反射執行callable
* * * * *
#### invokeClass() 創建類實例
~~~
public function invokeClass($class, $vars = [])
{
$reflect = new \ReflectionClass($class);
$constructor = $reflect->getConstructor();
if ($constructor) {
$args = $this->bindParams($constructor, $vars);
} else {
$args = [];
}
return $reflect->newInstanceArgs($args);
}
~~~
> ReflectionClass 反射創建類的對象實例
* * * * *
#### bindParams() 合并參數
~~~
protected function bindParams($reflect, $vars = [])
{
$args = [];
if ($reflect->getNumberOfParameters() > 0) {
// 判斷數組類型 數字數組時按順序綁定參數
reset($vars);
$type = key($vars) === 0 ? 1 : 0;
$params = $reflect->getParameters();
foreach ($params as $param) {
$name = $param->getName();
$class = $param->getClass();
if ($class) {
$className = $class->getName();
$args[] = $this->make($className);
} elseif (1 == $type && !empty($vars)) {
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$args[] = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new \InvalidArgumentException('method param miss:' . $name);
}
}
}
return $args;
}
~~~
> 合并多個參數到一個數組中。
* * * * *
## 2 助手函數
#### app() 讀取實例
~~~
function app($name = 'think\App', $args = [], $newInstance = false)
{
return Container::get($name, $args, $newInstance);
}
~~~
> 調用Container的get方法獲取容器中的實例
#### bind() 注冊內容到容器
~~~
function bind($abstract, $concrete = null)
{
return Container::getInstance()->bind($abstract, $concrete);
}
~~~
> 調用Conatiner的bind()到注冊內容到容器