TP5.1 源码窥探之容器面纱
在了解了单例模式,注册树模式和反射机制以后,下面来了解下容器类
容器类
项目入口文件index.php
Container::get('app')->run()->send();
系统自动加载Container
容器类,然后调用get
方法
thinkphp/library/think/Container.php
public static function get($abstract, $vars = [], $newInstance = false)
{
return static::getInstance()->make($abstract, $vars, $newInstance);
}
可以看到容器类首先实例化,然后调用make
方法,携带的参数是app
,[]
,false
,然后进入make
方法
thinkphp/library/think/Container.php
public function make($abstract, $vars = [], $newInstance = false)
{
if (true === $vars) {
// 总是创建新的实例化对象
$newInstance = true;
$vars = [];
}
$abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
if (isset($this->instances[$abstract]) && !$newInstance) {
return $this->instances[$abstract];
}
if (isset($this->bind[$abstract])) { //1 true 5 false
$concrete = $this->bind[$abstract]; //2 think\App
if ($concrete instanceof Closure) {
$object = $this->invokeFunction($concrete, $vars);
} else {
$this->name[$abstract] = $concrete;//3 ['app'=>'think\App']
return $this->make($concrete, $vars, $newInstance);//4 think\App,[],false
}
} else {
$object = $this->invokeClass($abstract, $vars);//6 think\App,[]
}
if (!$newInstance) {
$this->instances[$abstract] = $object;//7 ['think\App' => 'App实例对象']
}
return $object;
}
可以看到上边标注的步骤流程,最终将think\App
实例放到注册池里面,反射类之前有分享过,就不再赘述了。
实践
写了一个不正规的容器类,因为设计的太简单了,不能满足复杂场景的应用,比如依赖注入,参数绑定,闭包等情况。
note/container/Container.php
namespace note\container;
use ReflectionClass;
use ReflectionMethod;
/**
* Class Container
* @package note\container
*/
class Container
{
private static $instance = null;
// 命名空间 => 实例
private $instances = [];
private function __construct()
{
//todo construct
}
/**
* @return Container|null
*/
public static function getInstance()
{
if( ! self::$instance instanceof self){
self::$instance = new self;
}
return self::$instance;
}
/**
* @param $key
* @param array $vars
* @return mixed|object
* @throws \ReflectionException
*/
public function get($key,$vars=[])
{
if(isset($this->instances[$key])){
return $this->instances[$key];
}
$reflectionClass = new ReflectionClass($key);
$constructor = $reflectionClass->getConstructor();
$args = $constructor ? $this->getParameters($constructor,$vars) : [];
$object = $reflectionClass->newInstanceArgs($args);
$this->instances[$key] = $object;
return $object;
}
/**
* @param ReflectionMethod $reflect
* @param array $vars
* @return array
* @throws \ReflectionException
*/
public function getParameters(ReflectionMethod $reflect,$vars=[])
{
$args = [];
if( $reflect->getNumberOfParameters() == 0 )
return [];
$parameters = $reflect->getParameters();
foreach( $parameters as $parameter){
$name = $parameter->getName();
//检测此参数是否有默认值
if(isset($vars[$name])){
$args[] = $vars[$name];
}else if($parameter->isDefaultValueAvailable()){
$args[] = $parameter->getDefaultValue();
}else{
throw new \Exception('parameter ' . $name . ' is required');
}
}
return $args;
}
/**
* @param $key 待域名空间的类名或者数组
* @param string $val 类实例
*/
public function set($key,$val='')
{
if(is_array($key)){
$this->instances = array_merge($this->instances,$key);
}else{
$this->instances[$key] = $val;
}
return $this;
}
/**
* @param $key
* @return bool
*/
public function exists($key)
{
if(isset($this->instances[$key]))
return true;
return false;
}
/**
* @param $key
* @return $this
*/
public function delete($key)
{
unset($this->instances[$key]);
return $this;
}
}
测试
note/Person.php
namespace note;
class Person
{
public $name;
public $sex;
public function __construct($name,$sex)
{
$this->name = $name;
$this->sex = $sex;
}
public function getInfo()
{
return $this->name.'想要买辆劳斯莱斯库里南';
}
}
测试文件
$container = note\container\Container::getInstance();
$personNamespace = note\Person::class;
$person1 = $container->get($personNamespace,['name'=>'张三','sex'=>25]);
$info = $person1->getInfo();
//将会输出:张三想要买辆劳斯莱斯库里南
echo $info;
本作品采用《CC 协议》,转载必须注明作者和本文链接