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

ThinkPHP6核心解析应用程序初始化

时间:2023-03-29 22:19:20 PHP

runWithRequest()方法在Http类的run()方法中,得到think\\Request类的实例后,程序接着执行$response=$this->runWithRequest(request);其中,runWithRequest()方法的前几行代码如下:protectedfunctionrunWithRequest(Request$request){$this->initialize();//加载全局中间件$this->loadMiddleware();...这个方法的第一行Execute$this->initialize();对应用程序进行初始化,接下来详细分析这个初始化操作。Http类的initialize()方法:protectedfunctioninitialize(){//如果还没有初始化,就会初始化if(!$this->app->initialized()){$this->app->初始化();}}实际上是调用了App类的initialize()方法。该方法代码:publicfunctioninitialized(){//设置要初始化的应用程序状态$this->initialized=true;//记录开始时间$this->beginTime=microtime(true);//记录初始内存使用情况$this->beginMem=memory_get_usage();//==(A)==加载环境变量//$this->env和前面的(newApp())->http和$this->config例程一样if(is_file($this->rootPath.'.env')){$this->env->load($this->rootPath.'.env');}//设置配置文件后缀$this->configExt=$this->env->get('config_ext','.php');//==(B)==设置调试模式$this->debugModeInit();//==(C)==加载应用文件和配置等操作$this->load();//加载框架默认语言包$langSet=$this->lang->defaultLangSet();//framework目录下对应的语言包//例如:D:\dev\tp6\vendor\topthink\framework\src\lang\zh-cn.php$this->lang->load($this->thinkPath.'lang'.DIRECTORY_SEPARATOR.$langSet.'.php');//加载应用默认语言包//这将扫描“app/lang”中对应语言包文件夹下的所有“.php”文件//例如app/lang/zh-cn/*下的所有文件//然后加载并解析$this->loadLangPack($langSet);//监听AppInit$this->event->trigger('AppInit');//设置时区date_default_timezone_set($this->config->get('app.default_timezone','Asia/Shanghai'));//==(D)==初始化//初始化错误和异常处理,注册系统服务并初始化系统服务foreach($this->initializersas$initializer){$this->make($initializer)->init($这个);}return$this;}应用程序的初始化做了很多操作,主要操作有:加载环境变量,加载配置文件,加载语言包,监听AppInit,初始化数组中包含的类Initialization(A)相应的加载环境变量语句:$this->env->load($this->rootPath.'.env');其中$this->env与前面的(newApp())->http原理相同(见第一篇),可以得到\think\Env类的实例。获取到Env类实例后,调用load()方法,传入的参数是.env文件的地址。load()方法实现如下:publicfunctionload(string$file):void{$env=parse_ini_file($file,true)?:[];$this->set($env);}该方法读取.env文件的值后,调用set()方法将配置保存在Env类的$data成员变量中。set()方法代码:publicfunctionset($env,$value=null):void{if(is_array($env)){//所有KEY都转为大写$env=array_change_key_case($env,CASE_UPPER);foreach($envas$key=>$val){//如果有二级配置,则转换为KEY1_KEY2=>$vif(is_array($val)){foreach($valas$k=>$v){$this->data[$key.'_'。strtoupper($k)]=$v;}}else{$this->data[$key]=$val;}}//ENV的值不是数组case}else{$name=strtoupper(str_replace('.','_',$env));$this->data[$name]=$value;}}从.env中读取的值大概是这样的:$this->set($env)之后,结果大致是这样的:(B)Debug模式设置$this->debugModeInit()运行原理详见评论。protectedfunctiondebugModeInit():void{//应用调试模式if(!$this->appDebug){$this->appDebug=$this->env->get('app_debug')?真假;//关闭错误显示ini_set('display_errors','Off');}//如果不是命令行模式if(!$this->runningInConsole()){//重新申请更大的缓冲区//php缓冲区控制//参考:https://www.php.net/manual/en/ref.outcontrol.php//https://www.cnblogs.com/saw2012/archive/2013/01/30/2882451.html//新版PHP默认打开缓冲区ob_start(),ob_get_level()==1if(ob_get_level()>0){//相当于ob_get_contents()和ob_clean()//获取缓冲区内容并删除缓冲区内容$output=ob_get_clean();}//打开一个新的缓冲区控件ob_start();if(!empty($output)){//由于开启了新的buffer控件,//这里不会直接输出$output//而是等到执行完ob_flush()和flash()后,内容是输出到浏览器echo$output;}}}需要注意的是这里好像有bug,应该先执行$this->appDebug=$this->env->得到('应用\_调试')?真假;获取是否是debug模式配置,然后判断:if(!$this->appDebug)(C)加载应用文件和配置等操作接下来执行$this->load();,“load”方法的具体实现如下:protectedfunctionload():void{$appPath=$this->getAppPath();//加载“app”目录下的“common.php”文件if(is_file($appPath.'common.php')){include_once$appPath.'common.php';}//加载“helper.php”文件//正如你所看到的,这里的加载顺序:首先是“common.php”,然后是“helper.php”//并且“helper.php”中的函数被包裹在"if(!function_exists('xxx'))"//所以你可以在"common.php"文件中覆盖系统定义的辅助函数include_once$this->thinkPath。'helper.php';$configPath=$this->getConfigPath();$文件=[];//glob的作用是扫描给定路径下的文件,非常有用//这里扫描“config”目录下的所有“.php”文件if(is_dir($configPath)){$files=glob($configPath.'*'.$this->configExt);}foreach($filesas$file){//$this->config还是老套路获取“Config”类的实例//第二个“load”第一个参数是一级配置名。这里传入一个文件名,所有的文件名都作为一级配置//例如“app.php”配置文件,一级配置为“app”//的范围下“Config”类操作://用“load”加载文件后,通过“parse”方法解析文件内容//最后用“set”方法设置所有一些配置合并“config”成员变量$this->config->load($file,pathinfo($file,PATHINFO_FILENAME));}//加载“app”目录下的“event.php”文件if(is_file($appPath.'event.php')){$this->loadEvent(include$appPath.'event.php');}//注册自定义服务if(is_file($appPath.'service.php')){$services=include$appPath.'服务.php';foreach($servicesas$service){$this->register($service);}}}值得一提的是,程序先加载“common.php”,再加载“helper.php”,“helper.php”中的函数被包裹在“if(!function_exists('xxx'))",所以我们可以在必要时在"common.php"文件中覆盖它除了加载这两个文件外,系统定义的辅助函数会扫描"config"目录下的所有配置文件,并将它们加载到$config成员中Config类的变量,并加载“app”目录中的“event”。.php”文件,并加载和注册自定义服务。(D)初始化错误和异常处理,注册系统服务和初始化系统服务接下来看最后一段初始化函数:foreach($this->initializersas$initializer){$this->make($initializer)->init($this);}这几行代码做了很多操作:分别实例化其中包含的类,然后调用它的“init”方法。initializers数组的值如下:protected$initializers=[Error::class,//错误处理类RegisterService::class,//注册系统服务类BootService::class,//启动系统服务];跳过系统错误处理类,先看注册系统服务类。值得注意的是这个类有一个成员变量:protected$services=[PaginatorService::class,ValidateService::class,ModelService::class,];包含三个系统核心服务。在它的init方法中,将这些服务注册到系统服务中,并与之前的自定义服务合并。其主要实现代码:foreach($servicesas$service){if(class_exists($service)){//注册到系统服务$app->register($service);}}最后一个实例化是启动系统服务类。该类的init方法只调用了App类的boot方法。该方法的作用是初始化各个系统服务,也是调用各个服务的boot方法。启动系统服务类的实现如下:classBootService{publicfunctioninit(App$app){$app->boot();}}App类的boot方法:publicfunctionboot():void{array_walk($this->services,function($service){$this->bootService($service);});}关键是bootService方法:publicfunctionbootService($service){if(method_exists($service,'boot')){return$this->invoke([$service,'boot']);}}该方法调用每个服务的boot方法初始化注册服务。从上面的代码可以看出,系统注册的服务来源有3种:系统自带的,比如PaginatorService、ValidateService、ModelService;在app目录下的“service.php”文件中自定义的;service.php”文件定义。初始化后,“App”类的实例如下所示:更多学习内容,可以访问码农到架构师的培训路径