当前位置: 首页 > 后端技术 > PHP

ThinkPHP6核心分析-加载中间件及多应用分析

时间:2023-03-29 15:11:33 PHP

1.加载中间件之前写的一篇文章分析了应用的初始化,即在Http类的run()方法中调用的runWithRequest()方法展开分析第一行代码$this->initialize()。我们再看一下runWithRequest()方法的前几行:protectedfunctionrunWithRequest(Request$request){$this->initialize();//加载全局中间件$this->loadMiddleware();...应用程序初始化之后,接下来处理中间件。中间件类初始化loadMiddleware方法:protectedfunctionloadMiddleware():void{if(is_file($this->app->getBasePath().'middleware.php')){$this->app->middleware->import(include$this->app->getBasePath().'middleware.php');}}依旧是百看不厌的套路,使用$this->app->middleware实例化中间件,获取其实例。导入中间件通过$this->app->middleware获取到Middleware类的实例后,程序再调用import方法传入从“app”目录下的“middleware.php”文件读取的数据。文件原内容如下(原来全部注释掉):return[//全局请求缓存//\think\middleware\CheckRequestCache::class,//多语言加载\think\middleware\LoadLangPack::class,//会话初始化//\think\middleware\SessionInit::class,//页面跟踪调试\think\middleware\TraceDebug::class,];这里为了研究中间件是如何加载的,先去掉两个注释,即添加两个中间件。接下来看导入方法:publicfunctionimport(array$middlewares=[],string$type='global'):void{foreach($middlewaresas$middleware){$this->add($middleware,$type);}}这个方法传入一个中间件数组和一个中间件类型,默认是全局的,关键是里面的add方法。跳转添加方法:publicfunctionadd($middleware,string$type='route'):void{if(is_null($middleware)){return;}$middleware=$this->buildMiddleware($middleware,$type);if($middleware){$this->queue[$type][]=$middleware;//删除重复的$this->queue[$type]=array_unique($this->queue[$type],SORT_REGULAR);}}其实真正干活的是buildMiddleware方法,直接上:protectedfunctionbuildMiddleware($middleware,string$type):array{//是否是数组if(is_array($middleware)){//列表middleware及其参数//这里我们可以给中间件传递参数,形式为[middleware,parameters]list($middleware,$param)=$middleware;}//是否是闭包//表示中间件可以是闭包if($middlewareinstanceof\Closure){//返回闭包和参数return[$middleware,$param??无效的];}//排除以上类型,不是字符串,throwErrorif(!is_string($middleware)){thrownewInvalidArgumentException('Themiddlewareisinvalid');}//中间件别名检查$alias=$this->app->config->get('middleware.alias',[]);如果(isset($alias[$middleware])){$middleware=$alias[$middleware];}//如果中间件包含中间件(描述中间件项可以嵌套)//再次进行“导入”递归分析if(is_array($middleware)){$this->import($middleware,$type);返回[];}//返回分析结果return[[$middleware,'handle'],$param??null];}详细分析见上述代码注释最后返回的结果。在add方法中,执行$this->queue\[$type][]=$middleware;添加到队列中。最后的分析结果大概是这样的(app/middleware.php去掉了一些中间件的注释):至此,全局中间件已经加载完毕。2.多应用分析加载中间件后,接下来就是多应用分析(ThinkPHP6开始支持多应用模式)。if($this->multi){$this->parseMultiApp();}注意Http类的构造函数:publicfunction__construct(App$app){$this->app=$app;//多应用分析,通过判断“app”目录下是否有“controller”目录。可以看出,程序通过判断“app”目录下是否有“controller”目录来判断是否为多应用模式。然后看main方法parseMultiApp:protectedfunctionparseMultiApp():void{//虽然“Http”构造函数已经自动判断是否开启多应用//如果没有controller目录,$this->multi为真,然后会来这个方法//接下来看配置文件是否配置if($this->app->config->get('app.auto_multi_app',false)){//自动多应用识别$this->bindDomain=false;//获取域绑定$bind=$this->app->config->get('app.domain_bind',[]);//如果有域绑定if(!empty($bind)){//获取当前子域$subDomain=$this->app->request->subDomain();$domain=$this->app->request->host(true);//完成域名绑定if(isset($bind[$domain])){$appName=$bind[$domain];$this->bindDomain=true;//子域绑定}elseif(isset($bind[$subDomain])){$appName=$bind[$subDomain];$this->bindDomain=true;//二级通用域名绑定}elseif(isset($bind['*'])){$appName=$bind['*'];$this->bindDomain=true;}}//如果没有绑定域名if(!$this->bindDomain){//获取别名映射$map=$this->app->config->get('app.app_map',[]);//获取禁止的URL访问目录$deny=$this->app->config->get('app.deny_app_list',[]);//获取当前请求URL的pathinfo信息(包括URL后缀)//例如index/index/index$path=$this->app->request->pathinfo();//例如,从index/index/index获取索引$name=current(explode('/',$path));//解析别名映射if(isset($map[$name])){//如果这个别名映射到一个闭包//不知道用什么if($map[$name]instanceofClosure){$result=call_user_func_array($map[$name],[$this]);$appName=$result?:$name;//直接获取应用名称}else{$appName=$map[$name];}//$name不为空且$name作为$map数组中的KEY,或者$name为禁止URL定向的目录}elseif($name&&(false!==array_search($name,$map)||in_array($name,$deny))){thrownewHttpException(404,'appnotexists:'.$name);}elseif($name&&isset($map['*'])){$appName=$map['*'];}else{$appName=$name;}if($name){$this->app->request->setRoot('/'.$name);$this->app->request->setPathinfo(strpos($path,'/')?ltrim(strstr($path,'/'),'/'):'');}}}else{$appName=$this->name?:$this->getScriptName();}$this->loadApp($appName?:$this->app->config->get('app.default_app','index'));}可以看到第一段的“pathinfo”信息会被解析成应用名,比如index/index/index/中的index在该方法的最后,同样调用了loadApp方法,执行的操作与前面应用程序的初始化类似,只是加载的文件都在应用程序的目录下。和之前的版本相比,ThinkPHP6好像把原来的模块改成了多应用,因为在多应用的情况下,应用名和之前的模块名是从pathinfo的第一段解析出来的,新文档看不到模块的内容。更多学习内容,可访问【与各大厂商对比】优质PHP架构师教程目录。只要能读懂,就能保证你的薪水更上一层楼(持续更新中)。以上内容希望对大家有所帮助。很多PHPer总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道从哪里开始改进,我整理了一些这方面的资料,包括但不限于:分布式架构,高可扩展性、高性能、高并发、服务器性能调优、TP6、laravel、YII2、Redis、Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等知识点免费分享给你如果你需要高级干货。如果您需要,可以点击这里,还有更多的学习资料等着您领取。进阶PHP月薪30k>>>架构师成长路线【免费获取视频和面试资料】