雖然說 PHP 學(xué)起來相對簡單互广,但是要精通也不是一件簡單的事龄坪,我們除了要知道怎么使用之外,還得知道它的工作原理渔扎。
PHP 是一種適用于 Web 開發(fā)的腳本語言,可以將它看做是一個(gè)用C語言實(shí)現(xiàn)的包含大量組件的軟件框架信轿。
了解 PHP 的底層實(shí)現(xiàn)赞警,有助于我們更好的運(yùn)用它,優(yōu)化我們程序的性能虏两,從而實(shí)現(xiàn)更加強(qiáng)大的功能
1) Zend 引擎(核心)
Zend 引擎整體用C語言實(shí)現(xiàn)愧旦,是 PHP 的內(nèi)核部分,它負(fù)責(zé)將 PHP 代碼翻譯(詞法定罢、語法解析等一系列編譯過程)為可執(zhí)行的 opcode 操作碼笤虫,并實(shí)現(xiàn)相應(yīng)的處理方法、基本的數(shù)據(jù)結(jié)構(gòu)(如 hashtable祖凫、oo)琼蚯、內(nèi)存分配及管理、提供相應(yīng)的 API 方法供外部調(diào)用惠况。
Zend 是一切的核心遭庶,所有的外圍功能均圍繞 Zend 實(shí)現(xiàn)。
2) Extensions(擴(kuò)展)
圍繞著 Zend 引擎稠屠,Extensions 通過組件化的方式提供各種基礎(chǔ)服務(wù)峦睡,我們常見的各種內(nèi)置函數(shù)(例如變量操作函數(shù)翎苫、字符串操作函數(shù)等)以及標(biāo)準(zhǔn)庫等都是通過 Extensions 來實(shí)現(xiàn)。用戶也可以根據(jù)需要實(shí)現(xiàn)自己的 Extension 組件以達(dá)到功能擴(kuò)展榨了、性能優(yōu)化等目的煎谍,這就是高手常說的“編寫 PHP 擴(kuò)展”。
3) SAPI(服務(wù)器應(yīng)用程序編程接口)
SAPI 全稱是 Server Application Programming Interface龙屉,譯為“服務(wù)器應(yīng)用程序編程接口”呐粘。
SAPI 通過一系列鉤子函數(shù),使得 PHP 可以和外圍交互數(shù)據(jù)转捕,這是 PHP 非常優(yōu)雅和成功的一個(gè)設(shè)計(jì)作岖,通過 SAPI 成功的將 PHP 本身和上層應(yīng)用解耦隔離,PHP 可以不再考慮如何針對不同應(yīng)用進(jìn)行兼容五芝,而應(yīng)用本身也可以針對自己的特點(diǎn)實(shí)現(xiàn)不同的處理方式鳍咱。
4) Application(上層應(yīng)用)
這就是我們平時(shí)編寫的 PHP 程序,通過不同的 SAPI 方式得到各種各樣的應(yīng)用模式与柑,例如通過 Web 服務(wù)器實(shí)現(xiàn)網(wǎng)站后臺(tái)谤辜、在命令行下以腳本方式運(yùn)行等。
總結(jié)
如果將 PHP 看作一輛汽車价捧,那么車的框架就是 PHP 本身丑念,Zend 是車的引擎(發(fā)動(dòng)機(jī)),Ext 下面的各種組件就是車的輪子结蟋,SAPI 可以看做是公路脯倚,車可以跑在不同類型的公路上,而一次 PHP 程序的執(zhí)行就是汽車真正跑在公路上嵌屎。要想讓汽車跑得快推正,性能優(yōu)異的引擎+合適的車輪+正確的跑道都是缺一不可的。
PHP 常見的運(yùn)行模式
SAPI 即服務(wù)器應(yīng)用程序編程接口宝惰,是 PHP 與其他應(yīng)用交互的接口植榕,PHP 腳本要執(zhí)行有很多方式,比如通過 Web 服務(wù)器尼夺、命令行下或者嵌入在其他程序中尊残。SAPI 提供了一個(gè)和外部通信的接口,
常見的 SAPI 有:cgi淤堵、fast-cgi寝衫、cli、apache 模塊的 DLL拐邪、isapi 等慰毅。
CGI
CGI 即通用網(wǎng)關(guān)接口(Common Gateway Interface),它是一段程序扎阶,通俗的講 CGI 就象是一座橋汹胃,把網(wǎng)頁和 WEB 服務(wù)器中的執(zhí)行程序連接起來婶芭,它把 HTML 接收的指令傳遞給服務(wù)器的執(zhí)行程序,再把服務(wù)器執(zhí)行程序的結(jié)果返還給 HTML统台。
CGI 的跨平臺(tái)性能極佳,幾乎可以在任何操作系統(tǒng)上實(shí)現(xiàn)啡邑。
CGI 在遇到連接請求后贱勃,會(huì)先要?jiǎng)?chuàng)建 CGI 的子進(jìn)程,激活一個(gè) CGI 進(jìn)程谤逼,然后處理請求贵扰,處理完后結(jié)束這個(gè)子進(jìn)程,這就是 fork-and-execute 模式流部。
綜上所述戚绕,使用 CGI 方式的服務(wù)器有多少連接請求就會(huì)有多少 CGI 子進(jìn)程,子進(jìn)程反復(fù)加載 會(huì)導(dǎo)致 CGI 性能低下枝冀。當(dāng)用戶請求數(shù)量非常多時(shí)舞丛,會(huì)大量擠占系統(tǒng)的資源,如內(nèi)存果漾、CPU 時(shí)間等球切,造成性能低下。
FastCGI
fast-cgi 是 CGI 的升級版本绒障,F(xiàn)astCGI 像是一個(gè)常駐(long-live)型的 CGI吨凑,它激活后可以一直執(zhí)行著。
FastCGI 的工作原理:
Web Server 啟動(dòng)時(shí)載入 FastCGI 進(jìn)程管理器(IIS ISAPI 或 Apache Module)户辱;
FastCGI 進(jìn)程管理器自身初始化鸵钝,啟動(dòng)多個(gè) CGI 解釋器進(jìn)程(可見多個(gè) php-cgi)并等待來自 Web Server 的連接;
當(dāng)客戶端請求到達(dá) Web Server 時(shí)庐镐,F(xiàn)astCGI 進(jìn)程管理器選擇并連接到一個(gè) CGI 解釋器恩商。Web server 將 CGI 環(huán)境變量和標(biāo)準(zhǔn)輸入發(fā)送到 FastCGI子進(jìn)程 php-cgi;
FastCGI 子進(jìn)程完成處理后將標(biāo)準(zhǔn)輸出和錯(cuò)誤信息從同一連接返回 Web Server必逆。當(dāng) FastCGI 子進(jìn)程關(guān)閉連接時(shí)痕届,請求便處理完成了。FastCGI 子進(jìn)程接著等待并處理來自 FastCGI 進(jìn)程管理器(運(yùn)行在 Web Server 中)的下一個(gè)連接末患。 在 CGI 模式中研叫,php-cgi 在此便退出了。
APACHE2HANDLER
PHP作為Apache 的模塊璧针,Apache 服務(wù)器在系統(tǒng)啟動(dòng)后嚷炉,預(yù)先生成多個(gè)進(jìn)程副本駐留在內(nèi)存中,一旦有請求出現(xiàn)探橱,就立即使用這些空余的子進(jìn)程進(jìn)行處理申屹,這樣就不存在生成子進(jìn)程造成的延遲了绘证。這些服務(wù)器副本在處理完一次 HTTP 請求之后并不立即退出,而是停留在計(jì)算機(jī)中等待下次請求哗讥。對于客戶瀏覽器的請求反應(yīng)更快嚷那,性能較高。
apache模塊的DLL
該運(yùn)行模式是我們以前在 windows 環(huán)境下使用 apache 服務(wù)器經(jīng)常使用的杆煞,而在模塊化(DLL)中魏宽,PHP 是與 Web 服務(wù)器一起啟動(dòng)并運(yùn)行的。(是 apache 在 CGI 的基礎(chǔ)上進(jìn)行的一種擴(kuò)展决乎,可以加快 PHP 的運(yùn)行效率)
ISAPI
ISAPI 即 Internet Server Application Program Interface队询,是微軟提供的一套面向 Internet 服務(wù)的 API 接口。一個(gè) ISAPI 的 DLL构诚,可以在被用戶請求激活后長駐內(nèi)存蚌斩,等待用戶的另一個(gè)請求,還可以在一個(gè) DLL 里設(shè)置多個(gè)用戶請求處理函數(shù)范嘱,此外 ISAPI 的 DLL 應(yīng)用程序和 WWW 服務(wù)器處于同一個(gè)進(jìn)程中送膳,效率要顯著高于 CGI。
CLI
CLI(全稱:command-line interface)命令行界面丑蛤,是在圖形用戶界面得到普及之前使用最為廣泛的用戶界面肠缨,它通常不支持鼠標(biāo),用戶通過鍵盤輸入指令盏阶,計(jì)算機(jī)接收到指令后晒奕,予以執(zhí)行。也有人稱之為字符用戶界面(CUI)名斟。
PHP 的執(zhí)行流程和 opcode
我們再來看看 PHP 代碼執(zhí)行所經(jīng)過的流程脑慧。
圖:PHP 的執(zhí)行流程
一段PHP代碼會(huì)經(jīng)過詞法解析、語法解析等階段砰盐,會(huì)被翻譯成一個(gè)個(gè)指令(opcode)闷袒,然后 zend 虛擬機(jī)會(huì)順序執(zhí)行這些指令。PHP 本身是用C語言實(shí)現(xiàn)的岩梳,因此最終調(diào)用的也是C語言的函數(shù)囊骤,實(shí)際上我們可以把 PHP 看做一個(gè)C語言開發(fā)的軟件。
PHP 執(zhí)行的核心就是翻譯出來的一條一條指令冀值,也就是 opcode也物,opcode 是 PHP 程序執(zhí)行的最基本單位。
在計(jì)算機(jī)科學(xué)領(lǐng)域中列疗,操作碼(Operation Code)被用于描述機(jī)器語言指令中滑蚯,指定要執(zhí)行某種操作的那部分機(jī)器碼,構(gòu)成 opcode 的指令格式和規(guī)范由處理器的指令規(guī)范指定。
一個(gè) opcode 由兩個(gè)參數(shù)(op1告材,op2)坤次、返回值和處理函數(shù)組成。PHP 程序最終被翻譯為一組 opcode 處理函數(shù)的順序執(zhí)行斥赋。
下面列舉了幾個(gè)常見的處理函數(shù):
ZEND_ASSIGN_SPEC_CV_CV_HANDLER : 變量分配 (b)缰猴;
ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER:函數(shù)調(diào)用;
ZEND_CONCAT_SPEC_CV_CV_HANDLER:字符串拼接 b疤剑;
ZEND_ADD_SPEC_CV_CONST_HANDLER: 加法運(yùn)算 $a+2滑绒;
ZEND_IS_EQUAL_SPEC_CV_CONST:判斷相等 $a==1;
ZEND_IS_IDENTICAL_SPEC_CV_CONST:判斷相等 $a===1骚露。
HashTable
HashTable是Zend的核心數(shù)據(jù)結(jié)構(gòu)蹬挤,在PHP里面幾乎并用來實(shí)現(xiàn)所有常見功能缚窿,我們知道的PHP數(shù)組即是其典型應(yīng)用棘幸,此外在zend內(nèi)部,如函數(shù)符號(hào)表倦零、全局變量等也都是基于HashTable误续。
HashTable具有如下特點(diǎn):
支持典型的key->value查詢;
可以當(dāng)做數(shù)組使用扫茅;
添加蹋嵌、刪除節(jié)點(diǎn)是O(1)復(fù)雜度;
key支持混合類型葫隙,同時(shí)存在關(guān)聯(lián)數(shù)組合索引數(shù)組栽烂;
Value支持混合類型:array("string",2332);
支持線性遍歷恋脚,如 foreach腺办。
Zval
由于PHP 是一門弱類型語言,本身不嚴(yán)格區(qū)分變量的類型糟描。PHP 在聲明變量的時(shí)候不需要指定類型怀喉。PHP 在程序運(yùn)行期間可能進(jìn)行變量類型的隱式轉(zhuǎn)換。和其他強(qiáng)類型語言一樣船响,程序中也可以進(jìn)行顯式的類型轉(zhuǎn)換躬拢。Zval 是 Zend 中另一個(gè)非常重要的數(shù)據(jù)結(jié)構(gòu),用來標(biāo)識(shí)并實(shí)現(xiàn) PHP 變量见间。
Zval 主要由以下 3 部分組成聊闯。
Type:指定了變量所述的類型(整數(shù)、字符串米诉、數(shù)組等)馅袁;
refcount&is_ref:用來實(shí)現(xiàn)引用計(jì)數(shù);
value:是核心部分荒辕,存儲(chǔ)了變量的實(shí)際數(shù)據(jù)汗销。
Zval 用來保存一個(gè)變量的實(shí)際數(shù)據(jù)犹褒。因?yàn)橐鎯?chǔ)多種類型,所以 zval 是一個(gè) union弛针,也由此實(shí)現(xiàn)了弱類型叠骑。
引用計(jì)數(shù)在內(nèi)存回收、字符串操作等地方使用得非常廣泛削茁。PHP 中的變量就是引用計(jì)數(shù)的典型應(yīng)用宙枷。Zval 的引用計(jì)數(shù)通過成員變量 is_ref 和 ref_count 實(shí)現(xiàn)。通過引用計(jì)數(shù)茧跋,多個(gè)變量可以共享同一份數(shù)據(jù)慰丛,避免頻繁復(fù)制帶來的大量消耗。
在進(jìn)行賦值操作時(shí)瘾杭,Zend 將變量指向相同的 Zval诅病,同時(shí) ref_count++,在 unset 操作時(shí)粥烁,對應(yīng)的 ref_count-1贤笆。只有 ref_count 為 0 時(shí)才會(huì)真正執(zhí)行銷毀操作。如果是引用賦值讨阻,Zend 就會(huì)修改 is_ref 為 1芥永。
PHP 變量通過引用計(jì)數(shù)實(shí)現(xiàn)變量共享數(shù)據(jù),當(dāng)試圖寫入一個(gè)變量時(shí)钝吮,Zend 若發(fā)現(xiàn)該變量指向的 Zval 被多個(gè)變量共享埋涧,則為其復(fù)制一份 ref_count 為 1 的 Zval,并遞減原 Zval 的 refcount奇瘦,這個(gè)過程稱為“Zval分離”棘催。可見链患,只有在有寫操作發(fā)生時(shí)巧鸭,Zend 才進(jìn)行復(fù)制操作,因此也叫 copy-on-write(寫時(shí)復(fù)制)麻捻。
對于引用型變量纲仍,其要求和非引用型相反,引用賦值的變量間必須是捆綁的贸毕,修改一個(gè)變量就修改了所有捆綁變量郑叠。
點(diǎn)關(guān)注,不迷路
好了各位明棍,以上就是這篇文章的全部內(nèi)容了乡革,能看到這里的人呀,都是人才。之前說過沸版,PHP方面的技術(shù)點(diǎn)很多嘁傀,也是因?yàn)樘嗔耍瑢?shí)在是寫不過來视粮,寫過來了大家也不會(huì)看的太多细办,所以我這里把它整理成了PDF和文檔,如果有需要的可以點(diǎn)這里進(jìn)階PHP月薪30k>>>架構(gòu)師成長路線【視頻蕾殴、面試文檔免費(fèi)獲取】