thinkphp5 路由分发原理

 这里以登陆接口为例

请求路由:http://localhost/login

route.php配置

//登陆
Route::post('login','atsystem/login/save');

 1.先由入口index.php进来

由public/index.php  ->  thinkphp/start.php

看下start.php

<?php
namespace think;

// ThinkPHP 引导文件
// 1. 加载基础文件
require __DIR__ . '/base.php';

// 2. 执行应用
App::run()->send();

APP::run()->send(),打开文件thinkphp\library\think\App.php先看下run()函数


    /**
     * 执行应用程序
     * @access public
     * @param  Request $request 请求对象
     * @return Response
     * @throws Exception
     */
    public static function run(Request $request = null)
    {
        $request = is_null($request) ? Request::instance() : $request;

        try {
            $config = self::initCommon();

            // 模块/控制器绑定
            if (defined('BIND_MODULE')) {
                BIND_MODULE && Route::bind(BIND_MODULE);
            } elseif ($config['auto_bind_module']) {
                // 入口自动绑定
                $name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
                if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
                    Route::bind($name);
                }
            }

            $request->filter($config['default_filter']);

            // 默认语言
            Lang::range($config['default_lang']);
            // 开启多语言机制 检测当前语言
            $config['lang_switch_on'] && Lang::detect();
            $request->langset(Lang::range());

            // 加载系统语言包
            Lang::load([
                THINK_PATH . 'lang' . DS . $request->langset() . EXT,
                APP_PATH . 'lang' . DS . $request->langset() . EXT,
            ]);

            // 监听 app_dispatch
            Hook::listen('app_dispatch', self::$dispatch);
            // 获取应用调度信息
            $dispatch = self::$dispatch;

            // 未设置调度信息则进行 URL 路由检测
            if (empty($dispatch)) {
                $dispatch = self::routeCheck($request, $config);
            }

            // 记录当前调度信息
            $request->dispatch($dispatch);

            // 记录路由和请求信息
            if (self::$debug) {
                Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info');
                Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info');
                Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info');
            }

            // 监听 app_begin
            Hook::listen('app_begin', $dispatch);

            // 请求缓存检查
            $request->cache(
                $config['request_cache'],
                $config['request_cache_expire'],
                $config['request_cache_except']
            );

            $data = self::exec($dispatch, $config);
        } catch (HttpResponseException $exception) {
            $data = $exception->getResponse();
        }

        // 清空类的实例化
        Loader::clearInstance();

        // 输出数据到客户端
        if ($data instanceof Response) {
            $response = $data;
        } elseif (!is_null($data)) {
            // 默认自动识别响应输出类型
            $type = $request->isAjax() ?
            Config::get('default_ajax_return') :
            Config::get('default_return_type');

            $response = Response::create($data, $type);
        } else {
            $response = Response::create();
        }

        // 监听 app_end
        Hook::listen('app_end', $response);

        return $response;
    }

 其中有一行是 

$dispatch = self::routeCheck($request, $config);这是路由检测,我们去看下这个方法
  /**
     * URL路由检测(根据PATH_INFO)
     * @access public
     * @param  \think\Request $request 请求实例
     * @param  array          $config  配置信息
     * @return array
     * @throws \think\Exception
     */
    public static function routeCheck($request, array $config)
    {
        $path   = $request->path();
        $depr   = $config['pathinfo_depr'];
        $result = false;

        // 路由检测
        $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on'];
        if ($check) {
            // 开启路由
            if (is_file(RUNTIME_PATH . 'route.php')) {
                // 读取路由缓存
                $rules = include RUNTIME_PATH . 'route.php';
                is_array($rules) && Route::rules($rules);
            } else {
                $files = $config['route_config_file'];
                foreach ($files as $file) {
                    if (is_file(CONF_PATH . $file . CONF_EXT)) {
                        // 导入路由配置
                        $rules = include CONF_PATH . $file . CONF_EXT;
                        is_array($rules) && Route::import($rules);
                    }
                }
            }

            // 路由检测(根据路由定义返回不同的URL调度)
            $result = Route::check($request, $path, $depr, $config['url_domain_deploy']);
            $must   = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must'];

            if ($must && false === $result) {
                // 路由无效
                throw new RouteNotFoundException();
            }
        }

        // 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
        if (false === $result) {
            $result = Route::parseUrl($path, $depr, $config['controller_auto_search']);
        }
        return $result;
    }

看见没有,这里去引入了我们的路由配置文件route.php

我们打印下这里返回得$result 返回下面这样得格式

array(3) {
  ["type"] => string(6) "module"
  ["module"] => array(3) {
    [0] => string(8) "atsystem"
    [1] => string(5) "login"
    [2] => string(4) "save"
  }
  ["convert"] => bool(false)
}

然后我们在回到run()方法,其中有一行

$data = self::exec($dispatch, $config);

我们再去看看本类的exec()方法

    /**
     * 执行调用分发
     * @access protected
     * @param array $dispatch 调用信息
     * @param array $config   配置信息
     * @return Response|mixed
     * @throws \InvalidArgumentException
     */
    protected static function exec($dispatch, $config)
    {
        switch ($dispatch['type']) {
            case 'redirect': // 重定向跳转
                $data = Response::create($dispatch['url'], 'redirect')
                    ->code($dispatch['status']);
                break;
            case 'module': // 模块/控制器/操作
                $data = self::module(
                    $dispatch['module'],
                    $config,
                    isset($dispatch['convert']) ? $dispatch['convert'] : null
                );
                break;
            case 'controller': // 执行控制器操作
                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                $data = Loader::action(
                    $dispatch['controller'],
                    $vars,
                    $config['url_controller_layer'],
                    $config['controller_suffix']
                );
                break;
            case 'method': // 回调方法
                $vars = array_merge(Request::instance()->param(), $dispatch['var']);
                $data = self::invokeMethod($dispatch['method'], $vars);
                break;
            case 'function': // 闭包
                $data = self::invokeFunction($dispatch['function']);
                break;
            case 'response': // Response 实例
                $data = $dispatch['response'];
                break;
            default:
                throw new \InvalidArgumentException('dispatch type not support');
        }

        return $data;
    }

根据我们以上$dispatch返回得数据我们选择

  case 'module': // 模块/控制器/操作
                $data = self::module(
                    $dispatch['module'],
                    $config,
                    isset($dispatch['convert']) ? $dispatch['convert'] : null
                );
                break;

这里调用了本类的module()这个方法,我们去看下

    /**
     * 执行模块
     * @access public
     * @param array $result  模块/控制器/操作
     * @param array $config  配置参数
     * @param bool  $convert 是否自动转换控制器和操作名
     * @return mixed
     * @throws HttpException
     */
    public static function module($result, $config, $convert = null)
    {
        if (is_string($result)) {
            $result = explode('/', $result);
        }

        $request = Request::instance();

        if ($config['app_multi_module']) {
            // 多模块部署
            $module    = strip_tags(strtolower($result[0] ?: $config['default_module']));
            $bind      = Route::getBind('module');
            $available = false;

            if ($bind) {
                // 绑定模块
                list($bindModule) = explode('/', $bind);

                if (empty($result[0])) {
                    $module    = $bindModule;
                    $available = true;
                } elseif ($module == $bindModule) {
                    $available = true;
                }
            } elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
                $available = true;
            }

            // 模块初始化
            if ($module && $available) {
                // 初始化模块
                $request->module($module);
                $config = self::init($module);

                // 模块请求缓存检查
                $request->cache(
                    $config['request_cache'],
                    $config['request_cache_expire'],
                    $config['request_cache_except']
                );
            } else {
                throw new HttpException(404, 'module not exists:' . $module);
            }
        } else {
            // 单一模块部署
            $module = '';
            $request->module($module);
        }

        // 设置默认过滤机制
        $request->filter($config['default_filter']);

        // 当前模块路径
        App::$modulePath = APP_PATH . ($module ? $module . DS : '');

        // 是否自动转换控制器和操作名
        $convert = is_bool($convert) ? $convert : $config['url_convert'];

        // 获取控制器名
        $controller = strip_tags($result[1] ?: $config['default_controller']);
        $controller = $convert ? strtolower($controller) : $controller;

        // 获取操作名
        $actionName = strip_tags($result[2] ?: $config['default_action']);
        if (!empty($config['action_convert'])) {
            $actionName = Loader::parseName($actionName, 1);
        } else {
            $actionName = $convert ? strtolower($actionName) : $actionName;
        }

        // 设置当前请求的控制器、操作
        $request->controller(Loader::parseName($controller, 1))->action($actionName);

        // 监听module_init
        Hook::listen('module_init', $request);

        try {
            $instance = Loader::controller(
                $controller,
                $config['url_controller_layer'],
                $config['controller_suffix'],
                $config['empty_controller']
            );
        } catch (ClassNotFoundException $e) {
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
        }

        // 获取当前操作名
        $action = $actionName . $config['action_suffix'];

        $vars = [];
        if (is_callable([$instance, $action])) {
            // 执行操作方法
            $call = [$instance, $action];
            // 严格获取当前操作方法名
            $reflect    = new \ReflectionMethod($instance, $action);
            $methodName = $reflect->getName();
            $suffix     = $config['action_suffix'];
            $actionName = $suffix ? substr($methodName, 0, -strlen($suffix)) : $methodName;
            $request->action($actionName);

        } elseif (is_callable([$instance, '_empty'])) {
            // 空操作
            $call = [$instance, '_empty'];
            $vars = [$actionName];
        } else {
            // 操作不存在
            throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()');
        }

        Hook::listen('action_begin', $call);

        return self::invokeMethod($call, $vars);
    }

看下最后一行的$call ,我们打印下看看数据

array(2) {
  [0] => object(app\atsystem\controller\Login)#5 (5) {
    ["view":protected] => object(think\View)#6 (3) {
      ["engine"] => object(think\view\driver\Think)#7 (2) {
        ["template":"think\view\driver\Think":private] => object(think\Template)#8 (5) {
          ["data":protected] => array(0) {
          }
          ["config":protected] => array(30) {
            ["view_path"] => string(60) "E:\xampp\htdocs\atAdmin\public/../application/atsystem/view/"
            ["view_base"] => string(0) ""
            ["view_suffix"] => string(4) "html"
            ["view_depr"] => string(1) "/"
            ["cache_suffix"] => string(3) "php"
            ["tpl_deny_func_list"] => string(9) "echo,exit"
            ["tpl_deny_php"] => bool(false)
            ["tpl_begin"] => string(2) "\{"
            ["tpl_end"] => string(2) "\}"
            ["strip_space"] => bool(false)
            ["tpl_cache"] => bool(true)
            ["compile_type"] => string(4) "file"
            ["cache_prefix"] => string(0) ""
            ["cache_time"] => int(0)
            ["layout_on"] => bool(false)
            ["layout_name"] => string(6) "layout"
            ["layout_item"] => string(13) "{__CONTENT__}"
            ["taglib_begin"] => string(2) "\{"
            ["taglib_end"] => string(2) "\}"
            ["taglib_load"] => bool(true)
            ["taglib_build_in"] => string(2) "cx"
            ["taglib_pre_load"] => string(0) ""
            ["display_cache"] => bool(false)
            ["cache_id"] => string(0) ""
            ["tpl_replace_string"] => array(0) {
            }
            ["tpl_var_identify"] => string(5) "array"
            ["cache_path"] => string(37) "E:\xampp\htdocs\atAdmin/runtime/temp/"
            ["auto_rule"] => int(1)
            ["taglib_begin_origin"] => string(1) "{"
            ["taglib_end_origin"] => string(1) "}"
          }
          ["literal":"think\Template":private] => array(0) {
          }
          ["includeFile":"think\Template":private] => array(0) {
          }
          ["storage":protected] => object(think\template\driver\File)#9 (1) {
            ["cacheFile":protected] => NULL
          }
        }
        ["config":protected] => array(10) {
          ["view_base"] => string(0) ""
          ["view_path"] => string(60) "E:\xampp\htdocs\atAdmin\public/../application/atsystem/view/"
          ["view_suffix"] => string(4) "html"
          ["view_depr"] => string(1) "/"
          ["tpl_cache"] => bool(true)
          ["auto_rule"] => int(1)
          ["tpl_begin"] => string(1) "{"
          ["tpl_end"] => string(1) "}"
          ["taglib_begin"] => string(1) "{"
          ["taglib_end"] => string(1) "}"
        }
      }
      ["data":protected] => array(0) {
      }
      ["replace":protected] => array(5) {
        ["__ROOT__"] => string(0) ""
        ["__URL__"] => string(15) "/atsystem/login"
        ["__STATIC__"] => string(7) "/static"
        ["__CSS__"] => string(11) "/static/css"
        ["__JS__"] => string(10) "/static/js"
      }
    }
    ["request":protected] => object(think\Request)#2 (33) {
      ["method":protected] => string(4) "POST"
      ["domain":protected] => NULL
      ["url":protected] => string(6) "/login"
      ["baseUrl":protected] => NULL
      ["baseFile":protected] => string(10) "/index.php"
      ["root":protected] => string(0) ""
      ["pathinfo":protected] => string(5) "login"
      ["path":protected] => string(5) "login"
      ["routeInfo":protected] => array(4) {
        ["rule"] => array(1) {
          [0] => string(5) "login"
        }
        ["route"] => string(19) "atsystem/login/save"
        ["option"] => array(0) {
        }
        ["var"] => array(0) {
        }
      }
      ["env":protected] => NULL
      ["dispatch":protected] => array(3) {
        ["type"] => string(6) "module"
        ["module"] => array(3) {
          [0] => string(8) "atsystem"
          [1] => string(5) "login"
          [2] => string(4) "save"
        }
        ["convert"] => bool(false)
      }
      ["module":protected] => string(8) "atsystem"
      ["controller":protected] => string(5) "Login"
      ["action":protected] => string(4) "save"
      ["langset":protected] => string(5) "zh-cn"
      ["param":protected] => array(2) {
        ["username"] => string(11) "wanghuilong"
        ["password"] => string(7) "a123456"
      }
      ["get":protected] => array(0) {
      }
      ["post":protected] => array(2) {
        ["username"] => string(11) "wanghuilong"
        ["password"] => string(7) "a123456"
      }
      ["request":protected] => array(0) {
      }
      ["route":protected] => array(0) {
      }
      ["put":protected] => NULL
      ["session":protected] => array(0) {
      }
      ["file":protected] => array(0) {
      }
      ["cookie":protected] => array(0) {
      }
      ["server":protected] => array(0) {
      }
      ["header":protected] => array(9) {
        ["host"] => string(16) "192.168.1.158:83"
        ["connection"] => string(10) "keep-alive"
        ["content-length"] => string(2) "37"
        ["accept"] => string(16) "application/json"
        ["origin"] => string(51) "chrome-extension://ikceelgnkcigfiacnjdkdejkdicmlibb"
        ["user-agent"] => string(109) "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"
        ["content-type"] => string(48) "application/x-www-form-urlencoded; charset=UTF-8"
        ["accept-encoding"] => string(13) "gzip, deflate"
        ["accept-language"] => string(14) "zh-CN,zh;q=0.9"
      }
      ["mimeType":protected] => array(12) {
        ["xml"] => string(42) "application/xml,text/xml,application/x-xml"
        ["json"] => string(62) "application/json,text/x-json,application/jsonrequest,text/json"
        ["js"] => string(63) "text/javascript,application/javascript,application/x-javascript"
        ["css"] => string(8) "text/css"
        ["rss"] => string(19) "application/rss+xml"
        ["yaml"] => string(28) "application/x-yaml,text/yaml"
        ["atom"] => string(20) "application/atom+xml"
        ["pdf"] => string(15) "application/pdf"
        ["text"] => string(10) "text/plain"
        ["image"] => string(71) "image/png,image/jpg,image/jpeg,image/pjpeg,image/gif,image/webp,image/*"
        ["csv"] => string(8) "text/csv"
        ["html"] => string(35) "text/html,application/xhtml+xml,*/*"
      }
      ["content":protected] => NULL
      ["filter":protected] => string(0) ""
      ["bind":protected] => array(0) {
      }
      ["input":protected] => string(37) "username=wanghuilong&password=a123456"
      ["cache":protected] => NULL
      ["isCheckCache":protected] => NULL
    }
    ["failException":protected] => bool(false)
    ["batchValidate":protected] => bool(false)
    ["beforeActionList":protected] => array(0) {
    }
  }
  [1] => string(4) "save"
}

这个数组总共两个元素,第一个元素是对象,他将我们传过来模块、控制器组合成了命名空间的格:app\atsystem\controller\Login,第二个元素是我们最后调用的方法save

所以我们重点看下第一个元素是怎么来的

看下module方法中的$call = [$instance, $action];那$instance是怎么来的呢

 try {
            $instance = Loader::controller(
                $controller,
                $config['url_controller_layer'],
                $config['controller_suffix'],
                $config['empty_controller']
            );
        } catch (ClassNotFoundException $e) {
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
        }

我们再去看下thinkphp\library\think\Loader.php里面的controller这个静态类

  /**
     * 实例化(分层)控制器 格式:[模块名/]控制器名
     * @access public
     * @param  string $name         资源地址
     * @param  string $layer        控制层名称
     * @param  bool   $appendSuffix 是否添加类名后缀
     * @param  string $empty        空控制器名称
     * @return object
     * @throws ClassNotFoundException
     */
    public static function controller($name, $layer = 'controller', $appendSuffix = false, $empty = '')
    {
        list($module, $class) = self::getModuleAndClass($name, $layer, $appendSuffix);
  
        if (class_exists($class)) {
            return App::invokeClass($class);
        }

        if ($empty) {
            $emptyClass = self::parseClass($module, $layer, $empty, $appendSuffix);

            if (class_exists($emptyClass)) {
                return new $emptyClass(Request::instance());
            }
        }

        throw new ClassNotFoundException('class not exists:' . $class, $class);
    }

我们打印下第一行看看结果 halt(self::getModuleAndClass($name, $layer, $appendSuffix));

array(2) {
  [0] => string(8) "atsystem"
  [1] => string(29) "app\atsystem\controller\Login"
}

就是这个函数去解析的

大致就是这个意思吧,再看下去头快晕了,这里主要就是看下哪里以调用的路由配置文件route.php,然后在哪里解析成namespace的格式让框架认识这个路由

猜你喜欢

转载自blog.csdn.net/weixin_41858542/article/details/86075879