2. 实例化系统应用基础类think\App
tp6\public\index.php
$http = (new App())->http;
3. 获取应用目录等相关路径信息
4.加载全局的服务提供provider.php
文件
5.设置容器实例及应用对象实例,确保当前容器对象唯一
tp6\vendor\topthink\framework\think\App.php
/**
* 架构方法
* @access public
* @param string $rootPath 应用根目录
*/
public function __construct(string $rootPath = '')
{
// 获取应用目录等相关路径信息
$this->thinkPath = realpath(dirname(__DIR__)) . DIRECTORY_SEPARATOR;
$this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath();
$this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR;
$this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR;
// 加载全局的服务提供provider.php文件
if (is_file($this->appPath . 'provider.php')) {
$this->bind(include $this->appPath . 'provider.php');
}
// 设置容器实例及应用对象实例
static::setInstance($this);
//绑定一个类实例到容器
$this->instance('app', $this);
$this->instance('think\Container', $this);
}
6. 从容器中获取HTTP
应用类think\Http
从2中可以看到实例化app后直接调用了http,但是app类中和父类Container没有这个属性.
在 Container类中找到这串代码
//读取不可访问(protected 或 private)或不存在的属性的值时,__get() 会被调用
public function __get($name)
{
return $this->get($name);
}
/**
* 获取容器中的对象实例
* @template T
* @param string|class-string<T> $abstract 类名或者标识
* @return T|object
*/
public function get($abstract)
{
//先判断在 容器绑定标识 中是否存在
if ($this->has($abstract)) {
return $this->make($abstract);
}
throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
}
/**
* 判断容器中是否存在类及标识
* @access public
* @param string $name 类名或者标识
* @return bool
*/
public function has($name): bool
{
return $this->bound($name);
}
/**
* 判断容器中是否存在类及标识
* @access public
* @param string $abstract 类名或者标识
* @return bool
*/
public function bound(string $abstract): bool
{
return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
}
说实话,有点不太理解为什么不直接在has方法中完成而选择转到bound方法中具体实现.
/**
* 创建类的实例 已经存在则直接获取
* @template T
* @param string|class-string<T> $abstract 类名或者标识
* @param array $vars 变量
* @param bool $newInstance 是否每次创建新的实例
* @return T|object
*/
public function make(string $abstract, array $vars = [], bool $newInstance = false)
{ //根据别名获取真实类名 会返回"think\Http"
$abstract = $this->getAlias($abstract);
if (isset($this->instances[$abstract]) && !$newInstance) {
//直接返回容器中的实例
return $this->instances[$abstract];
}
//Closure 代表 匿名函数 的类
if (isset($this->bind[$abstract]) && $this->bind[$abstract] instanceof Closure) {
$object = $this->invokeFunction($this->bind[$abstract], $vars);
} else {
//调用反射执行类的实例化 支持依赖注入
$object = $this->invokeClass($abstract, $vars);
}
if (!$newInstance) {
//将实力放入容器
$this->instances[$abstract] = $object;
}
return $object;
}
/**
* 根据别名获取真实类名
* @param string $abstract
* @return string
*/
public function getAlias(string $abstract): string
{ //bind app中的 容器绑定标识
if (isset($this->bind[$abstract])) {
$bind = $this->bind[$abstract];
if (is_string($bind)) {
return $this->getAlias($bind);
}
}
return $abstract;
}
/**
* 调用反射执行类的实例化 支持依赖注入
* @access public
* @param string $class 类名
* @param array $vars 参数
* @return mixed
*/
public function invokeClass(string $class, array $vars = [])
{
try {
//创建一个该类的反射类
$reflect = new ReflectionClass($class);
} catch (ReflectionException $e) {
//创建出错时报错
throw new ClassNotFoundException('class not exists: ' . $class, $class, $e);
}
if ($reflect->hasMethod('__make')) {
$method = $reflect->getMethod('__make');
//公开且静态
if ($method->isPublic() && $method->isStatic()) {
$args = $this->bindParams($method, $vars);
//调用反射方法并将其参数作为数组传递
$object = $method->invokeArgs(null, $args);
$this->invokeAfter($class, $object);
return $object;
}
}
//获取类的构造函数
$constructor = $reflect->getConstructor();
//绑定参数 会返回一个数组 数组中有app实例
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
//根据给定的参数创建新的类实例
$object = $reflect->newInstanceArgs($args);
$this->invokeAfter($class, $object);
return $object;
}
/**
* 绑定参数
* @access protected
* @param ReflectionFunctionAbstract $reflect 反射类
* @param array $vars 参数
* @return array
*/
protected 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);
$reflectionType = $param->getType();
if ($reflectionType && $reflectionType->isBuiltin() === false) {
//会获得一个对象实例 think\app
$args[] = $this->getObjectParam($reflectionType->getName(), $vars);
dump($this->getObjectParam($reflectionType->getName(), $vars));exit;
} 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;
}
/**
* 获取对象类型的参数值
* @access protected
* @param string $className 类名
* @param array $vars 参数
* @return mixed
*/
protected function getObjectParam(string $className, array &$vars)
{
$array = $vars;
$value = array_shift($array);
if ($value instanceof $className) {
$result = $value;
array_shift($vars);
} else {
//返回一个app实例 会在make方法直接返回App实例
$result = $this->make($className);
}
return $result;
}
ps:这些代码转过来装过去的,人都快晕了.不过总算有那么一点点收获. 目前只是在流程经过的地方做了注释.