「本文會對控制器最後的執行流程和使用的兩個高階屬性進行簡單的學習,一個是fastcgi_finish_request方法巧用,另一個是trait特性,超類別的概念多少都有過了解,接下來一起來解析一下。
」
當執行完控制器中的方法回應資料給App類別的run方法,直到這裡就已經執行了。
是不是有點懵這裡的資料最後會回傳哪裡呢!
之前寫過的框架執行流程、路由、控制器實例化都是從這裡開始進入的。
所以當run方法執行完成之後,就會把對應的結果給回到這裡。
這部分的程式碼Container::get('app')
應該都知道了是傳回App類別的實例。
然後透過App類別去執行run方法,才會有之前講過的一切。
下圖是喀喀爾從半中腰做的一個心智圖,前面的沒有,後邊的所有知識點都會寫在這個心智圖裡。
執行完run方法就會去執行Container::get('app')- >run()->send()
send這個方法,有多少人會認為在App類別裡邊執行send方法。
其實不是的,回想一下之前執行控制器方法然後回傳的回應結果是什麼?
如果你不是很粗略的看都會記得是Response的一個物件實例。
所以說send方法會去response類別裡邊去執行。
#先不看其它的,先看這行程式碼$this-> ;app['hook']
,現在知道是執行的那裡嗎?
這種形式就是透過存取陣列形式去存取物件的屬性,也就是先前解析的ArrayAccess這個類別。當存取的屬性不存在時會去執行offsetGet,然後執行魔術方法__get,最終透過make方法傳回實例,這一切的操作都是在容器中。
對這行程式碼具體是監聽的什麼就不去做解析了。
接著需要看處理輸出資料的這行程式碼$data = $this->getContent();
這個方法做的事情就是將傳過來的資料賦值給本類的content屬性。
其實在取得輸出資料這個方法中,請看喀喀著喀嚓圈出來的第一個地方感覺是很沒有必要。
可以看到根本對資料就沒有任何的處理,只是簡單的回傳了,所以說框架有好的地方也有不好的地方,只有你去閱讀了才會知道,否則你會對你經常使用的工具一無所知。
在接著就是Trace偵錯注入,就是透過設定檔配置的,透過呼叫debug類別來實現的,這裡就不詳解了。
然後就是快取判斷,快取會在後文中單獨拎出來講,所以也是過。
在接下來就對回應頭的設定了,偵測 HTTP 頭是否已經發送,這塊的東西就很重要了,也是平時接觸不多的知識點了。
最後一步,來了來了,它來了,它帶著echo來了,執行了一個方法$this->sendData($data);
給人一種媳婦熬成娘的感覺,終於來到的終點站,一個echo輸出了咔咔幾十天的心酸啊!
為了到達這個echo咔咔是經歷九九八十一難啊!戰鬥還未停止,同志仍需努力!
那麼到這裡關於框架執行然後到應用初始化,在到路由偵測、控制器的實例化、然後傳回response實例,在透過入口檔案執行send方法。
最後將資料輸出到終端,也就是一個echo的事情。
雖然這裡的戰鬥結束了,但是在下面還有一個非常重要的知識點,咔咔將重新提一節來進行說明。
在上一節中透過Container::get('app')-> ;run()->send();
在response類別中執行了send方法,輸出了資料。
但是在輸出資料之後也執行了一個方法fastcgi_finish_request();
,給的註解是提高頁面回應,接下來好好來扒一扒其中的奧秘。
在PHP官網看到這樣一段話
The script will still occupy a FPM process after fastcgi_finish_request(). So using it excessively for long running tasks may occupy all your toPM th pm.max_children. This will lead to gateway errors on the webserver.
在fastcgi_finish_request()之後,腳本仍將佔用FPM進程。因此,對於長時間運行的任務過度使用它可能會佔用您的所有FPM線程,直到pm.max_children。這將導致Web伺服器上的網關錯誤。
所以說在沒有徹底的了解這個方法之前不要輕易的在自己的專案中使用這個方法。
接下來咔咔將使用一個案例來演示這個方法的使用,僅僅只是演示使用,如果需要使用到專案中請仔細閱讀文檔應該注意的問題。
案例示範
公司有一個業務需要發送通知給用戶,但是由於發送時間太久,非常費時間,有可能需要好幾十秒的時間,更嚴重的會直接導致瀏覽器連線逾時。
在一個問題就是使用者體驗的問題,使用者等待時間過程,體驗當然不好。
為了解決以上兩個問題,今天談論的fastcgi_finish_request
就派上了用場。
理解
#對這個函數的理解其實就是發送回應給瀏覽器,使用者等待時間大大縮短,但是PHP進程還是在運作的。
這樣就達到了來個目的,就類似於我們常說的非同步執行。
直覺的來說就是發送郵件有可能需要10秒,但是用戶是沒有感知的,用戶點擊發送郵件之後直接就返回發送成功,瀏覽器響應結束,用戶做其它事情,後台進程繼續執行發送郵件的任務。
案例
#<span style="display: block; background: url("https://files.mdnice.com/point.png") 10px 10px / 40px no-repeat rgb(40, 44, 52); height: 30px; width: 444.812px; margin-bottom: -7px; border-radius: 5px;"></span><code class="hljs" style="overflow-x: auto; padding: 15px 16px 16px; color: rgb(171, 178, 191); display: -webkit-box; font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace; font-size: 12px; background: rgb(40, 44, 52); border-radius: 5px;"><span class="hljs-meta" style="color: rgb(97, 174, 238); line-height: 26px;"><?php</span><br/><span class="hljs-comment" style="color: rgb(92, 99, 112); font-style: italic; line-height: 26px;">/**<br/> * 设置超时时间,变成不限制<br/> *<br/> */</span><br/>set_time_limit(<span class="hljs-number" style="color: rgb(209, 154, 102); line-height: 26px;">0</span>);<br/><br/><span class="hljs-comment" style="color: rgb(92, 99, 112); font-style: italic; line-height: 26px;">/**<br/> * 本函数模拟非常耗时的任务,执行完毕需要5秒的时间<br/> */</span><br/><span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: rgb(198, 120, 221); line-height: 26px;">function</span> <span class="hljs-title" style="color: rgb(97, 174, 238); line-height: 26px;">writeFile</span><span class="hljs-params" style="line-height: 26px;">()</span><br/></span>{<br/> $path = <span class="hljs-string" style="color: rgb(152, 195, 121); line-height: 26px;">'D:/phpstudy_pro/WWW/kaka.txt'</span>;<br/> file_put_contents($path,<span class="hljs-string" style="color: rgb(152, 195, 121); line-height: 26px;">'程序运行开始'</span> . PHP_EOL,FILE_APPEND);<br/> <span class="hljs-keyword" style="color: rgb(198, 120, 221); line-height: 26px;">for</span>($i =<span class="hljs-number" style="color: rgb(209, 154, 102); line-height: 26px;">0</span>;$i < <span class="hljs-number" style="color: rgb(209, 154, 102); line-height: 26px;">5</span>;$i++) {<br/> file_put_contents($path,time() . PHP_EOL,FILE_APPEND);<br/> sleep(<span class="hljs-number" style="color: rgb(209, 154, 102); line-height: 26px;">1</span>);<br/> }<br/><br/> file_put_contents($path,<span class="hljs-string" style="color: rgb(152, 195, 121); line-height: 26px;">'程序运行结束'</span> . PHP_EOL,FILE_APPEND);<br/><br/>}<br/><br/><span class="hljs-comment" style="color: rgb(92, 99, 112); font-style: italic; line-height: 26px;">/**<br/> * 输出文字标记,任务开始<br/> */</span><br/><span class="hljs-keyword" style="color: rgb(198, 120, 221); line-height: 26px;">echo</span>(<span class="hljs-string" style="color: rgb(152, 195, 121); line-height: 26px;">'任务开始'</span>);<br/><br/><span class="hljs-comment" style="color: rgb(92, 99, 112); font-style: italic; line-height: 26px;">/**<br/> * 后台执行非常耗时的任务<br/> */</span><br/>register_shutdown_function(writeFile);<br/><br/><span class="hljs-comment" style="color: rgb(92, 99, 112); font-style: italic; line-height: 26px;">/**<br/> * 立即发送请求<br/> */</span><br/>fastcgi_finish_request();<br/><br/><br/><br/></code>
以上測試全部使用linux系統進行測試哈,否則你看不到直覺的效果。
經過上面的演示,回應非常快,瀏覽器回應結束後,後台程式依然進行執行每秒執行一個時間戳。
以上就是對fastcgi_finish_request
方法的簡單介紹,如果你也感興趣可以進行簡單的嘗試一下,有助於更好的去理解其中的小秘密。
#應該在兩個年前咔咔就對這個特性進行過一次解析,trait
就是常說的超類。
這個特性是在PHP5.4才加入的,這個特性不是常用的介面更不是類別。
這個特性是為了解決PHP的一大弱點只能單繼承的缺點,但是也不能叫多繼承,嚴謹一點的就是類似多繼承的功能而已。
接下來要為大家示範一個案例。
建立test檔一,並且傳回對應類別名稱。
建立test1文件,並且傳回對應類別名稱
建立控制器檔案用來輸出資訊。
然後在控制器中引入對應的超類別文件,這裡需要注意的是圈住的第一個框,這個框就是直接引入超類別test檔。
然後可以直接進行訪問,看看會回傳什麼。
透過上圖存取結果結果可以看得到傳回的是Test超類別檔案的方法,但是此控制器同樣也基礎了Controller控制器,也就是在文章開頭就說的超類別就是實作了一種多繼承的功能而已。
但是這裡會存在一個問題,請看下圖報錯資訊。
上圖的報錯訊息是因為在控制器中使用了兩個超類別導致的,也就是下圖的使用方式。
那麼如何解決這種報錯訊息呢!接下來跟這喀喀的節奏一起來。
解決報錯訊息
在解決之前問題之前得先清楚這個問題是由於什麼造成的。
出現這個錯誤的原因是引用的兩個trait裡面有同名的hello函數,出現了衝突。
但是在日常開發中這種情況都是可以避免的,因為手動改方法名還是很方便的,但是這裡咔咔教大家如何解決這種問題。
一是用其中一個trait裡的hello方法覆蓋另外一個trait的同名方法,因為兩個方法內容是一致的,所以我在這裡直接選擇insteadof覆蓋;
二是給他們用as取別名,這樣就不會有衝突了。 as關鍵字還有另一個用途,那就是修改方法的存取控制。
#經過上圖的改動之後,再一次的進行訪問,看一下返回結果。
那麼這個時候就會有夥伴有疑問了,就是案例列印結果一直是Test類別的方法,Test1類別的方法一直沒有進行列印。
那是如何進行訪問的呢!來接著看一下。
#從上圖可以看到將存取方法改為別名控制訪問,接著來看一下訪問結果。
#從上圖可以看到回傳結果就是超類別Test1類別的回傳結果。
那麼關於as這個的使用就需要大家在去搜尋一下使用方式,有時候注意一下細節就可以學到很多知識點。
直到這裡關於控制器的原始碼解析就到這了,喀喀爾透過原始碼給大家分析控制器的如如何進行實例化的。
也再一次的進行了對ArrayAccess和魔術方法的呼叫關係,一定要有自己的思考去想問題。
在就是對存取控制器後是如何進行回應資料的,等等。
也在原始碼中學到了關於fastcgi_finish_request方法巧用,但在使用這個函數一定要注意關於喀喀爾提到的兩個注意點。
最後就是對超類別的一個簡單案例描述。
「堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。
”
以上是ThinkPHP框架所使用的特性fastcgi_finish_request和trait的詳細內容。更多資訊請關注PHP中文網其他相關文章!