只考虑实例化一个类的话,一个简单的思路是:
class B
{}
class A
{
public function __construction(B $b)
}
set('a', 'A');//将类A注册为a
get('a');
在get时会这么解决实例化A的问题:
1)首先利用php的反射类ReflectionClass,构造类A的反射对象。
2)然后利用反射对象的getConstructor方法解析构造方法的参数
3)再然后是获取构造函数参数的值,有默认值得给默认值,如果是依赖其他类的实现,则调用get来实现(这相当于一个递归)
4)最后是使用反射对象的newInstanceArgs(构造函数的参数数组)方法,传入构造函数需要的参数并实例化一个对象
值得注意的是,在解析构造方法依赖时,对于不具有默认值的参数,我们会生成一个Instance对象代替,之后会替换为相应的参数对象。
当然除了类实例化的情况,还有其他情况需要解决,不过相对来说就比较简单了,我们直接上代码。
1.注册方法
public function register($class, $define, array $params = [])
{
$define = $this->formatDefine($class, $define);
$this->defineMap[$class] = $define;
$this->paramsMap[$class] = $params;
unset($this->singletonMap[$class]);
return $this;
}
2.注册单例方法
public function registerSingleton($class, $define, array $params = [])
{
$define = $this->formatDefine($class, $define);
$this->defineMap[$class] = $define;
$this->paramsMap[$class] = $params;
$this->singletonMap[$class] = null;
return $this;
}
3.格式化定义数组的方法
public function formatDefine($class, $define)
{
$result = [];
if (empty($define)) {
$result['class'] = $class;
} else if (is_string($define)) {
$result['class'] = $define;
} else if (is_callable($define, true)) {
$result = $define;
} else if (is_object($define)) {
$result = $define;
} else if (is_array($define)) {
if (!array_key_exists('class', $define) && (strpos($class, '\\') !== false)) {
$define['class'] = $class;
} else {
throw new Exception('Please provide class value!');
}
$result = $define;
} else {
throw new Exception('Define is illegal!');
}
return $result;
}
4.获取对象方法
public function resolve($class, $params = [], $config = [])
{
$result = null;
if (isset($this->singletonMap[$class])) {
$result = $this->singletonMap[$class];
} else if (!isset($this->defineMap[$class])) {
$result = $this->generateObject($class, $params, $config);
} else if (is_callable($this->defineMap[$class], true)) {
$defineParams = isset($this->paramsMap[$class]) ? $this->paramsMap[$class] : [];
$currentParams = array_merge($defineParams, $params);
$define = $this->defineMap[$class];
$result = call_user_func($define, $this, $currentParams, $config);
} else if (is_object($this->defineMap[$class])) {
$result = $this->defineMap[$class];
$this->singletonMap[$class] = $this->defineMap[$class];
} else if (is_array($this->defineMap[$class])) {
$define = $this->defineMap[$class];
$concrete = $define['class'];
unset($define['class']);
$config = array_merge($define, $config);
$defineParams = isset($this->paramsMap[$class]) ? $this->paramsMap[$class] : [];
$currentParams = array_merge($defineParams, $params);
if ($concrete === $class) {
$result = $this->generateObject($class, $currentParams, $config);
} else {
$result = $this->resolve($concrete, $currentParams, $config);
}
} else {
throw new Exception('Error');
}
if (array_key_exists($class, $this->singletonMap) && is_object($result)) {
$this->singletonMap[$class] = $result;
}
return $result;
}
5.实例化类的方法
protected function generateObject($class, array $params = [], array $config = [])
{
list ($reflection, $dependencies) = $this->parseConstructorDependency($class);
foreach ($params as $index => $param) {
$dependencies[$index] = $param;
}
$dependencies = $this->resolveConstructorDependency($dependencies, $reflection);
if (empty($config)) {
return $reflection->newInstanceArgs($dependencies);
}
if (!empty($dependencies) && is_a($class, 'tank\base\BaseObject', true)) {
$dependencies[count($dependencies) - 1] = $config;
return $reflection->newInstanceArgs($dependencies);
} else {
$object = $reflection->newInstanceArgs($dependencies);
foreach ($config as $name => $value) {
$object->$name = $value;
}
return $object;
}
}
6.解析构造函数依赖的方法
protected function parseConstructorDependency($class)
{
if (isset($this->reflectionMap[$class])) {
return [$this->reflectionMap[$class], $this->dependencyMap[$class]];
}
$dependencies = [];
$reflection = new \ReflectionClass($class);
$constructor = $reflection->getConstructor();
if ($constructor !== null) {
foreach ($constructor->getParameters() as $param) {
if ($param->isDefaultValueAvailable()) {
$dependencies[] = $param->getDefaultValue();
} else {
$dependClass = $param->getClass();
$isClass = !is_null($dependClass) ? true : false;
$dependencies[] = Instance::generate($isClass ? $dependClass->getName() : null);
}
}
}
$this->reflectionMap[$class] = $reflection;
$this->dependencyMap[$class] = $dependencies;
return [$reflection, $dependencies];
}
7.解决构造函数依赖的方法
protected function resolveConstructorDependency($dependencies, $reflection = null)
{
foreach ($dependencies as $index => $dependency) {
if ($dependency instanceof Instance) {
if (!is_null($dependency->id)) {
$dependencies[$index] = $this->resolve($dependency->id);
} elseif (!is_null($reflection)) {
$name = $reflection->getConstructor()->getParameters()[$index]->getName();
$class = $reflection->getName();
throw new Exception($class." missing required parameter ".$name);
}
}
}
return $dependencies;
}
最后,我们再构建BaseTank类时,留下了一个generateObject方法未实现,现在,我们的DI容器实现完毕,是时候实现这个方法了。
public static function generateObject($defineConfig, array $params = [])
{
if (is_string($defineConfig)) {
return static::$container->resolve($defineConfig, $params);
} elseif (is_array($defineConfig) && isset($defineConfig['class'])) {
$class = $defineConfig['class'];
unset($defineConfig['class']);
return static::$container->resolve($class, $params, $defineConfig);
} elseif (is_callable($defineConfig, true)) {
return call_user_func($defineConfig, $params);
} else {
throw new Exception('Generate Object Error!');
}
}
ok,今儿个先到这,欲知后事如何,且听下回分解……
github源码:https://github.com/2lovecode/tank