1.(new App())->http
调用无法调用的属性(不存在或者是非public),会触发__get()魔术方法.因为App继承了Container类,该方法在Container中
#1.触发
public function __get($name)
{
return $this->get($name);
}
#2.
public function get($abstract)
{
if ($this->has($abstract)) { //是否在App->bind或Container->instances中
return $this->make($abstract); //创建实例化对象
}
throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
}
2.通过make()方法实例化类
public function make(string $abstract, array $vars = [], bool $newInstance = false)
{
$abstract = $this->getAlias($abstract); //通过别名,获取别名绑定的类
//省略部分代码
$object = $this->invokeClass($abstract, $vars); //重点
if (!$newInstance) {
$this->instances[$abstract] = $object;
}
return $object;
}
3.
public function invokeClass(string $class, array $vars = [])
{
try {
$reflect = new ReflectionClass($class); //映射类
} catch (ReflectionException $e) {
throw new ClassNotFoundException('class not exists: ' . $class, $class, $e);
}
$constructor = $reflect->getConstructor(); //反射了类的构造函数
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
$object = $reflect->newInstanceArgs($args); //通过参数实例化对象
return $object;
}
4.绑定参数
public function bindParams(ReflectionFunctionAbstract $reflect, array $vars = []): array
{
if ($reflect->getNumberOfParameters() == 0) {
return [];
}
reset($vars); //将数组的内部指针重置到数组中的第一个元素
$type = key($vars) === 0 ? 1 : 0; //关联数组还是下标数组
$params = $reflect->getParameters(); //获取类方法的参数,这里传的构造函数的参数
$args = [];
foreach ($params as $param) {
$name = $param->getName();
$lowerName = Str::snake($name); //驼峰转下划线
//获取参数的类型 function someFunction(int $param) 为int
$reflectionType = $param->getType();
//$reflectionType->isBuiltin()检查它是否是内置类型
if ($reflectionType && $reflectionType->isBuiltin() === false) {
$args[] = $this->getObjectParam($reflectionType->getName(), $vars);
} elseif (1 == $type && !empty($vars)) {
$args[] = array_shift($vars);
} elseif (0 == $type && array_key_exists($name, $vars)) {
$args[] = $vars[$name];
} elseif (0 == $type && array_key_exists($lowerName, $vars)) {
$args[] = $vars[$lowerName];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new InvalidArgumentException('method param miss:' . $name);
}
}
return $args;
}
5.获取对象的参数
protected function getObjectParam(string $className, array &$vars)
{
$array = $vars;
$value = array_shift($array);
if ($value instanceof $className) {
$result = $value;
array_shift($vars);
} else {
$result = $this->make($className); //实例化对象
}
return $result;
}