tp6源码分析之顶级类--App

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:这些代码转过来装过去的,人都快晕了.不过总算有那么一点点收获. 目前只是在流程经过的地方做了注释.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值