「本文主要介紹框架的執行流程
」
然後就會進入到檔案thinkphp/library/think/App.php
的run方法,在這個方法主要就是下圖框出來的地方,執行的initialize方法。
來到initialize這個方法,先看上半部。
microtime(true);
傳回的是unix的微秒數memory_get_usage
回傳的是分配給PHP的記憶體量,單位為位元組static::setInstance($this);
這裡是將app這個實例設定為容器實例#$this->instance('app' , $this);
這個在之前容器章節就提到了,就是為了把app這個類別綁定到容器裡邊去,也就是註冊樹模式。 這裡有一個小小的問題點可以給大家提出來,在初始化應用的這個方法裡邊存在這樣一行程式碼。
有沒有小夥伴對這個$this->env
和下邊的$this->config
這兩個呼叫有疑惑。
如果你有疑惑那就跟著喀喀一起來看,沒疑惑的就可以繼續往下看了。
App這個類別是繼承的容器類,那麼這個env和config不論是在app還是container類別中都是沒有這兩個屬性的。
那麼怎麼就可以直接呼叫呢!而且程式碼追蹤都會追蹤到env類別和container類別中。
要知道這個源頭就需要我們去在大致的看一遍container類別的程式碼。
#經過一番苦讀之後,可以看到下圖的幾行程式碼。這幾行程式碼全部使用的是魔術方法。
當存取env類別不存在的時候就會去執行make方法。
make這個方法在容器那一章進行的細的不能再細的解讀了。
這個make方法最後會回傳一個類別的實例,也會儲存到容器裡邊。
這裡只放一個make方法的程式碼,如果有不會的可以去看之前的文章。
最後就是載入一系列的數據,載入詳情請看前言的心智圖。
在閱讀原始碼的過程中,有一個很難把控的問題就是一個方法在不同的地方進行了調用,但是咱們確一時半會根本不知道都在哪裡調用了。
這裡用init方法來做一個示範。
init方法是初始化應用程式或模組的方法,但這裡的module參數確實一個空值。
先做一個斷點查看一下相關的資料資訊。
列印的結果就是空,這就是一些新學習的伙伴會犯的一個錯誤,因為這個方法不可能只呼叫一次的。
如果初始化模組都是空那麼這個方法就沒有存在的必要了。
那麼正確的斷點方式應該是這個樣子的。
此時就會有一個問題,這個init方法明顯是被呼叫了兩個次的,那麼另一個呼叫的地方是在哪裡呢!
如果在不知道新的技巧之前,就會進行一系列的斷點列印,看在哪裡進行了執行,例如在這個init的上層去列印。
也就是在initialize那個方法裡邊去打印做斷點,但是這樣很是麻煩的,而且很有可能浪費了大量的時間還是找不到正確的地方。
小技巧之debug_backtrace()
這個方法會產生一條回溯追踪,會顯示出一個方法所有的呼叫位置。
使用方式就是如下圖,只要把debug_backtrace這個方法印出來即可。
根據所得到的資料訊息,就可以非常快的進行定位。
第一次就是在app類別的215行。
第二次是在thinkphp/library/think/route/dispatch/Module.php
類的60行
可以在這裡做一個列印,看看這個module是否為index
所以說有了這個方法就可以非常快速地定位呼叫位置。
#上文給大家提供了一個小技巧debug_backtrace
實戰示範如何檢視一個方法都在哪裡執行的。
而案例也是使用的init這個方法來示範的,因為接下來就是要對init這個方法進行深入的了解。
在init方法裡邊主要做的事情在上邊的腦圖已經描述的很清楚了。
bindTo
方法進行綁定註冊的。 env
環境變數配置裡邊<span style="display: block; background: url(https://files.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; display: -webkit-box; font-family: Operator Mono, Consolas, Monaco, Menlo, monospace; font-size: 12px; -webkit-overflow-scrolling: touch; padding-top: 15px; background: #282c34; border-radius: 5px;"> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 初始化应用或模块<br> * <span class="hljs-doctag" style="color: #c678dd; line-height: 26px;">@access</span> public<br> * <span class="hljs-doctag" style="color: #c678dd; line-height: 26px;">@param</span> string $module 模块名<br> * <span class="hljs-doctag" style="color: #c678dd; line-height: 26px;">@return</span> void<br> */</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">function</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">init</span><span class="hljs-params" style="line-height: 26px;">($module = <span class="hljs-string" style="color: #98c379; line-height: 26px;">''</span>)</span><br> </span>{<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 定位模块目录</span><br> $module = $module ? $module . DIRECTORY_SEPARATOR : <span class="hljs-string" style="color: #98c379; line-height: 26px;">''</span>;<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 第一次:D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\<br> * 第二次:D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\index\<br> */</span><br> $path = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->appPath . $module;<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载初始化文件</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>)) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">elseif</span> (is_file(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->runtimePath . $module . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>)) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->runtimePath . $module . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'init.php'</span>;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">else</span> {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载行为扩展文件</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'tags.php'</span>)) {<br> $tags = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'tags.php'</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_array($tags)) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->hook->import($tags);<br> }<br> }<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载公共文件</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'common.php'</span>)) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include_once</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'common.php'</span>;<br> }<br><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (<span class="hljs-string" style="color: #98c379; line-height: 26px;">''</span> == $module) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载系统助手函数</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->thinkPath . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'helper.php'</span>;<br> }<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 加载中间件</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'middleware.php'</span>)) {<br> $middleware = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'middleware.php'</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_array($middleware)) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->middleware->import($middleware);<br> }<br> }<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 注册服务的容器对象实例</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_file($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'provider.php'</span>)) {<br> $provider = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">include</span> $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'provider.php'</span>;<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_array($provider)) {<br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->bindTo($provider);<br> }<br> }<br><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * $path : "D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\"<br> * "D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\application\index\"<br> */</span><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 自动读取配置文件</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (is_dir($path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'config'</span>)) {<br> $dir = $path . <span class="hljs-string" style="color: #98c379; line-height: 26px;">'config'</span> . DIRECTORY_SEPARATOR;<br> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">elseif</span> (is_dir(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->configPath . $module)) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// D:\phpstudy_pro\WWW\ThinkPHPSourceCodeAnalysis\config\</span><br> $dir = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->configPath . $module;<br> }<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// scandir:以升序的方式读取目录中的文件</span><br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 返回就是config目录中的所有文件</span><br> $files = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">isset</span>($dir) ? scandir($dir) : [];<br><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">foreach</span> ($files <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">as</span> $file) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * $this->configExt:配置文件的后缀<br> * pathinfo返回的是文件后缀,关于pathinfo共有三个可选的参数PATHINFO_DIRNAME、PATHINFO_BASENAME、PATHINFO_EXTENSION,分别为只返回文件名,文件目录名,文件扩展<br> */</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (<span class="hljs-string" style="color: #98c379; line-height: 26px;">'.'</span> . pathinfo($file, PATHINFO_EXTENSION) === <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->configExt) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**<br> * 俩个参数分别为<br> * 1.目录+config目录下的文件<br> * 2.config目录下文件名<br> */</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->config->load($dir . $file, pathinfo($file, PATHINFO_FILENAME));<br> }<br> }<br> }<br><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->setModulePath($path);<br><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> ($module) {<br> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 对容器中的对象实例进行配置更新</span><br> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">$this</span>->containerConfigUpdate($module);<br> }<br> }<br></code>
這裡附帶一份程式碼,可以對著程式碼看上邊的執行流程,對每一步都做了簡單的說明。
喀喀噠個人見解對原始碼進行最佳化
在設定模組的這步驟程式碼喀喀感覺不是很嚴謹,因為init方法會在兩個地方進行執行。
第一次的模組為空,這塊程式碼執行是沒有任何意義的。
下面在容器的物件實例進行配置更新時進行了一次判斷,判斷模組的這個參數是否為空,如果不為空才會執行。
那麼同樣的道理,咔咔感覺在設定模組路徑這塊也應該在這個判斷裡邊。
雖然說第二次執行會把第一次的結果覆蓋掉,但是咔咔感覺下圖這樣使用才會更好。
在上一節這裡就是最後的內容,那這個對實例進行更新配置,到底更新了什麼,怎麼更新沒有說明。
在這一小節就會做出說明,同樣可以配合著前言的心智圖看。
在這一節中咔咔感覺最重要的就是下圖的內容了。
我們可以隨意追蹤一到兩個方法查看一下那邊到底執行了什麼方法。
追蹤方法Db::init()
追蹤方法過來後可以看到就是對Db類別中的config屬性進行賦值,把database中的值賦值給Db類別中的config屬性。
追蹤方法$this->middleware->setConfig()
來到中間件這個類別裡邊,可以看到就是把本類的配置和傳遞過來的參數類別進行合併,同樣也是進行config屬性的賦值。
跟上邊案例的Db類別的init方法實現的效果是一致的。
這裡在提一嘴就是在對容器中的物件實例進行更新配置
這一幅圖中可以看到紫色部分是在本類別中沒有引用的。
那麼這是怎麼可以進行執行的呢!是因為App類別繼承了容器類,容器類別中有四個魔術方法,其中有一個__get方法,就是在取得不存在的屬性時會執行那個方法。
在魔術方法__get方法中執行了一個make方法,這個make方法說了好多次了,這個方法最終會回傳一個應用的實例,然後用這個實例呼叫對應實例類別的方法。
這一塊一定要理解好,閱讀原始碼就是這個樣子,我們需要對一切未知的進行的解決,只有這樣才能提高我們的程式設計能力和想法。
本節會對偵錯模式做出簡單的說明,並且會對框架程式碼冗餘情況進行簡單的提出。
沒有人寫的程式碼是沒有漏洞的,如果有那就是你還沒達到一定的造詣。
調試模式
在第一節只提到了initialize方法的上半部分,因為在這一節之前聊的都是關於應用初始化init的內容。
接下來會對這一塊的內容做簡單的說明。
接下來的內容估計不是很好理解,都是平常在工作中根本使用不到的。
#上邊這三個先暫時認識就行,後期如果有機會會專門出一篇文章做解釋的。
關於框架程式碼冗餘
這裡也僅僅代表咔咔咔個人的觀點。
可以先看看這部分的程式碼,這兩個處程式碼是不是很熟悉,沒錯就是在上文的init方法中容器物件實例配置更新看到過。
如圖
這塊也就是喀喀爾個人提出的見解,由於喀喀爾式針對5.1做的源碼解讀,不太了解新版版是否做出了改動。
#本節主要是針對框架執行流程中的初始化應用做了簡單的探討。
至於在app類別的run方法下面還有很多的執行過程在這一節中沒有做過多的解釋。
在閱讀原始碼的過程中給大家提了一個很好得小技巧,那就是如何去查看一個方法都在哪裡進行了執行。
這個方法為debug_backtrace
,這個方法需要大家多使用幾次就知道怎麼使用了,因為在列印出來的結果中也存在著很多無用的資訊。
這個方法在偵錯原始碼的過程中是非常有效的,一定要好好利用這個方法。
在就是對初始化應用init方法進行了特別詳細的介紹。
其中咔咔感覺這塊設計最好的就是在容器中的物件實例進行更新配置那一塊,先讀取所有的配置,然後在通過各個類別的方法進行配置的設定。
這種程式碼規劃和設計想法值得我們去學習。
最後聊到了調試模式和框架的程式碼冗餘問題,關於調試模式這裡咔咔給大家提個醒項目在線上的調試模式一定要關閉。
否則你的專案就類似裸奔的存在,沒有一點點的安全可言。
這塊有點不好理解的就是對於緩衝區,關於這塊的內容咔咔認為暫時沒有必要去鑽牛角尖,先認識認識然後在進行深入的研究。
緩衝區的這塊內容估計工作了三四年的也很少有人使用,所以先認識,知道怎麼一回事,咔咔後期學習了之後在給大家進行補充。
直到這裡關於框架的執行流程之初始化應用就結束了,這一節沒有過深需要學習的,主要是其中的程式碼設計模式和實作思路。
最後這張圖大家一定要跟著原始碼看一看哈!
#「#堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。
”
以上是ThinkPHP框架執行流程(附腦圖)的詳細內容。更多資訊請關注PHP中文網其他相關文章!