zoukankan      html  css  js  c++  java
  • thinkphp6执行流程(一)

    项目目录相对为tp6
    请求被统一转发到入口文件(入口文件地址为 tp6/index.php)
    1.入口文件引入了composer自动载入文件类库
    <php?
    namespace think;
    
    require __DIR__ . '/../vendor/autoload.php';
    

      

    (文件地址为 tp6/vendor/autoload.php')
    2.实例化 thinkApp 对象 赋值给$app
    <?php
    
    $app = new App();
    

      

    (App类文件地址为 tp6/vendor/topthink/framework/src/think/App.php')
    执行App类中的__construct构造方法
    <?php
    public function __construct(string $rootPath = '')
    {
        // 框架类库目录
        $this->thinkPath   = 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;
        // 加载服务provide容器
        if (is_file($this->appPath . 'provider.php')) {
            // 执行 Containerind()方法 绑定类、闭包、实例、接口
            $this->bind(include $this->appPath . 'provider.php');
        }
        // 设置一个容器实例
        static::setInstance($this);
        // 绑定类的实例到容器
        $this->instance('app', $this);
        $this->instance('thinkContainer', $this);
    }
    

      

    其中在属性$bind中已经有了一些框架类的别名与实现的映射数组
    此时$app因为是继承了Container类,所以$app实际也是一个容器
    3.通过$app类调用http类(web管理类)
    $http = $app->http;
    

      

    3.0 引用http类的过程如下:
    (http类文件地址为 tp6/vendor/topthink/framework/src/think/Http.php')
    3.1 首先App中不存在http方法,但是在容器类中存在魔术方法__get,会触发该魔术方法
    <?php
    public function __get($name)
    {
        return $this->get($name);
    }
    

      

    get方法从app应用容器中获取http对象实例
    <php?
    /**
    * 获取容器中的对象实例
    * @access public
    * @param string $abstract 类名或者标识
    * @return object
    */
    public function get($abstract)
    {
        if ($this->has($abstract)) {
            return $this->make($abstract);
        }
    
        throw new ClassNotFoundException('class not exists: ' . $abstract, $abstract);
    }
    

      

    3.2 通过app应用容器中的make方法进行类的实例,以及执行instances方法将实例化的http类绑定到容器数组对象中
    <?php
    /**
        * 创建类的实例 已经存在则直接获取
        * @access public
        * @param string $abstract    类名或者标识
        * @param array  $vars        变量
        * @param bool   $newInstance 是否每次创建新的实例
        * @return mixed
        */
    public function make(string $abstract, array $vars = [], bool $newInstance = false)
    {
        $abstract = $this->getAlias($abstract);
    
        if (isset($this->instances[$abstract]) && !$newInstance) {
            return $this->instances[$abstract];
        }
    
        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;
    }
    

      

    返回http对象实例到调用处 第3步开始的地方
    4 http对象执行run方法,应用程序的执行开始
    <?php
    /**
    * 执行应用程序
    * @access public
    * @param Request|null $request
    * @return Response
    */
    public function run(Request $request = null): Response
    {
        //自动创建request对象
        $request = $request ?? $this->app->make('request', [], true);
        // 绑定request类的实例化对象到容器中
        $this->app->instance('request', $request);
    
        try {
            // 执行应用程序返回response类
            $response = $this->runWithRequest($request);
        } catch (Throwable $e) {
            $this->reportException($e);
    
            $response = $this->renderException($request, $e);
        }
        return $response;
    }
    

      

    4.1 run 方法中首先创建Request类,执行instances方法将实例化的Request类绑定到容器数组对象中,标识为request
    然后进行路由调度,执行 app容器中runWithRequest方法
    /**
        * 执行应用程序
        * @param Request $request
        * @return mixed
        */
    protected function runWithRequest(Request $request)
    {
        // 初始化app应用程序
        $this->initialize();
    
        // 加载全局中间件
        $this->loadMiddleware();
    
        // 监听HttpRun
        $this->app->event->trigger(HttpRun::class);
        // 中间件调度
        return $this->app->middleware->pipeline()
            ->send($request)
            ->then(function ($request) {
                return $this->dispatchToRoute($request);
            });
    }
    

      

    4.2然后在在http类中runWithRequest方法中执行了dispatchToRoute 讲请求分发到路由
     (dispatchToRoute 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/Route.php')
    // 分发请求到路由
    <?php
    protected function dispatchToRoute($request)
    {
        // 是否启用路由, 默认启用路由
        $withRoute = $this->app->config->get('app.with_route', true) ? function () {
            $this->loadRoutes();
        } : null;
        // 执行路由调度
        return $this->app->route->dispatch($request, $withRoute);
    }
    

      

    4.3 在http类中dispatchToRoute 通过app容器获取了route实例,并调用了route实例中的dispatch方法
    <?php
    /**
    * 路由调度
    * @param Request $request
    * @param Closure|bool $withRoute
    * @return Response
    */
    public function dispatch(Request $request, $withRoute = true)
    {
        $this->request = $request;
        $this->host    = $this->request->host(true);
        $this->init();

        if ($withRoute) {
            // 加载路由设置
            if ($withRoute instanceof Closure) {  
                $withRoute();
            }
            // 检查路由
            $dispatch = $this->check();
        } else {
            // 如果没有路由,则使用默认url解析
            $dispatch = $this->url($this->path());
        }
        // $dispatch 为think outedispatchController 的实例化 中的初始化,提取控制名称以及操作名称
        // 1, 通过最终think outeRule::dispatch来确路由调度的最终执行动作
        
        $dispatch->init($this->app);
        return $this->app->middleware->pipeline('route')
            ->send($request)
            ->then(function () use ($dispatch) {
                // 执行动作方法
                return $dispatch->run();
            });
    }
    4.4 在route类中的dispatch中执行了run方法, 
    (run 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/route/Dispatch.php')
    <?php
    /** * 执行路由调度 * @access public * @return mixed */ public function run(): Response { if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) { $rules = $this->rule->getRouter()->getRule($this->rule->getRule()); $allow = []; foreach ($rules as $item) { $allow[] = strtoupper($item->getMethod()); } return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]); } // 此处调用的$this类,由调用者确定, 可能为url, callback, 或者controller // $data 为返回的response 类 $data = $this->exec(); return $this->autoResponse($data); }

      

    4.5 run方法调用了exec 
    (此处调用的exec的类方法所在的文件,由调用者确定, 可能为url, callback, 或者controller, exec 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/route/dispatch/Callback.php|Controller.php')
    在exec方法中最终返回了data数据
    4.6 然后调用了autoResponse方法,并传递4.5返回的data数据
    (autoResponse 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/route/Dispatch.php')
    <?php
    protected function autoResponse($data): Response
    {
        if ($data instanceof Response) {
            $response = $data;
        } elseif (!is_null($data)) {
            // 默认自动识别响应输出类型
            $type     = $this->request->isJson() ? 'json' : 'html';
            $response = Response::create($data, $type);
        } else {
            $data = ob_get_clean();
    
            $content  = false === $data ? '' : $data;
            $status   = '' === $content && $this->request->isJson() ? 204 : 200;
            // 创建response类返回,使用html
            $response = Response::create($content, 'html', $status);
        }
        return $response;
    }
    

      

    4.7 autoResponse 方法中执行了 Response::create方法
    最终方法返回了response对象;
    (Response::create 类方法的文件地址为 tp6/vendor/topthink/framework/src/think/Response.php')
    <?php
    /**
        * 创建Response对象
        * @access public
        * @param  mixed  $data 输出数据
        * @param  string $type 输出类型
        * @param  int    $code 状态码
        * @return Response
        */
    public static function create($data = '', string $type = 'html', int $code = 200): Response
    {
        $class = false !== strpos($type, '\') ? $type : '\think\response\' . ucfirst(strtolower($type));
    
        return Container::getInstance()->invokeClass($class, [$data, $code]);
    }
    

      

    4.8 最终返回了 response Html类的对象实例
    5 执行run方法
    $response = $http->run();
    

      

    6 response 执行send输出数据的操作;
    (Html 类文件所在地址为 tp6/vendor/topthink/framework/src/think/response/Html.php )
    6.1 执行send方法
    (send方法 类文件所在地址为 tp6/vendor/topthink/framework/src/think/Response.php )
    $response->send();
    <?php
    /**
        * 发送数据到客户端
        * @access public
        * @return void
        * @throws InvalidArgumentException
        */
    public function send(): void
    {
        // 处理输出数据
        $data = $this->getContent();
        if (!headers_sent() && !empty($this->header)) {
            // 发送状态码
            http_response_code($this->code);
            // 发送头部信息
            foreach ($this->header as $name => $val) {
                header($name . (!is_null($val) ? ':' . $val : ''));
            }
        }
        if ($this->cookie) {
            $this->cookie->save();
        }
    
        $this->sendData($data);
    
        if (function_exists('fastcgi_finish_request')) {
            // 提高页面响应
            fastcgi_finish_request();
        }
    }
    

      

    6.1 在send方法中最终执行了 sendData 方法
    (sendData方法 类文件所在地址为 tp6/vendor/topthink/framework/src/think/Response.php )
    <?php
    /**
    * 输出数据
    * @access protected
    * @param string $data 要处理的数据
    * @return void
    */
    protected function sendData(string $data): void
    {
        echo $data;
    }
    

      

    7 执行 http对象中的end方法
    <?php
    $http->end($response);
    

      

    (http类文件地址为 tp6/vendor/topthink/framework/src/think/Http.php')
    <?php
    /**
    * HttpEnd
    * @param Response $response
    * @return void
    */
    public function end(Response $response): void
    {
    $this->app->event->trigger(HttpEnd::class, $response);
    
    //执行中间件
    $this->app->middleware->end($response);
    
    // 写入日志
    $this->app->log->save();
    }
    

      

    8整个程序结束
     
    应用类App继承了Container容器类, 所有类的实例通过容器类进行统一管理,容器类为单例模式全局唯一;
  • 相关阅读:
    UML中对关系的描述 (二)
    MDA(模型驱动架构)
    简单工厂模式
    Windows Communication Foundation(WCF) 一步一步从入门到精通
    UML 状态图
    C# 设计模式与重构
    DSM领域定义建模和MDA模型驱动架构分析
    UML 部署图
    UML 活动图
    UML 及其作用
  • 原文地址:https://www.cnblogs.com/jasonyou/p/13590973.html
Copyright © 2011-2022 走看看