这里以登陆接口为例
请求路由: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的格式让框架认识这个路由