PHP是直譯語言還是編譯型語言?

2021-05-26 19:01:13
本篇文章給大家介紹一下PHP的語言型別。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

編譯型語言

  • 使用專門的編譯器(類似於Windows下的Visual Studio)、針對特定平臺(作業系統)將某種高階語言原始碼一次性「翻譯」成該平臺硬體執行的機器碼(包括機器指令和運算元),幷包裝成該平臺所能識別的可執行性程式(.exe)的格式,這個轉換過程稱為編譯(Compile)。編譯生成的可執行程式可以以脫離開發環境,在特定的平臺上獨立執行。有些程式在編譯結束之後,還可能需要對其他編譯好的目的碼進行連結,即組裝兩個以上的目的碼模組生成最終的可執行程式,通過這種方式實現低層次的程式碼複用。

  • 編譯型語言的程式碼是一次編譯,迴圈使用。換句話說就是前人種樹,後人乘涼。

  • C、C++、Objective -C 等都屬於編譯型語言

直譯語言

  • 在程式執行前將源程式預編譯成中間語言,然後再由直譯器執行中間語言

  • 每次執行直譯語言的程式都需要進行一次編譯,因此直譯語言的程式執行效率通常較低,而且它不能脫離直譯器獨立執行。

  • C#、PHP、Python、Java等都是直譯語言。

OK,通過上面概念的簡單瞭解,你可能對解釋型、編譯型語言有了一個大概的瞭解。既然兩者平分天下,下面我們就來看一下兩者各有什麼優勢吧。

編譯型語言

優勢

  • 編譯型語言最大的優勢之一就是其執行速度。用C/C++編寫的程式執行速度要比用Java編寫的相同程式快30%-70%。

  • 編譯型程式比解釋型程式消耗的記憶體更少。

劣勢

  • 不利的一面——編譯器比直譯器要難寫得多

  • 編譯器在偵錯程式時提供不了多少幫助——有多少次在你的C語言程式碼中遇到一個「空指標異常」時,需要花費好幾個小時來明確錯誤到底在程式碼中的什麼位置。

  • 可執行的編譯型程式碼要比相同的解釋型程式碼大許多。例如,C/C++的.exe檔案要比同樣功能的Java的.class檔案大很多。

  • 編譯型程式是面向特定平臺的因而是平臺依賴的。

  • 編譯型程式不支援程式碼中實現安全性——例如,一個編譯型的程式可以存取記憶體的任何區域,並且可以對你的PC做它想做的任何事情(大部分病毒是使用編譯型語言編寫的)

  • 由於鬆散的安全性和平臺依賴性,編譯型語言不太適合開發因特網或者基於Web的應用。

直譯語言

優勢

  • 極佳的偵錯支援。一名PHP程式設計師只需要幾分鐘就可以定位並修復一個「空指標異常」,因為PHP執行環境不僅指明瞭異常的性質,而且給出了異常發生位置具體的行號和函數呼叫順序(著名的堆疊跟蹤資訊)。這樣的便利是編譯型語言所無法提供的。

  • 直譯器比編譯器容易實現

  • 極佳的平臺獨立性

  • 高度的安全性——這是網際網路應用迫切需要的

  • 中間語言程式碼的大小比編譯型可執行程式碼小很多

劣勢

  • 佔用更多的記憶體和CPU資源。這是由於,為了執行直譯語言編寫的程式,相關的直譯器必須首先執行。直譯器是複雜的,智慧的,大量消耗資源的程式並且它們會佔用很多CPU週期和記憶體。

  • 執行效率較編譯型程式慢很多。直譯器會做很多程式碼優化,執行時安全性檢查;這些額外的步驟佔用了更多的資源並進一步降低了應用的執行速度。

OK,通過上面的學習,相信大家對直譯語言與編譯型語言有了大致的瞭解,而且PHP語言是直譯語言,而且解釋PHP語言的直譯器就是Zend引擎。

而且,根據兩者的優劣勢比較可以發現,編譯型語言更適合做底層的操作,而直譯語言較多的用在了Web開發上。

再深入探討下PHP的執行過程:

php的編譯和執行是分離開的,亦即:先執行完編譯,而後再執行。很多人會說:c++也是如此啊,確實。不過php的這種分離可以給我們提供很多便利,當然不可避免也有很有缺點。

先說一下整個過程:

①php會呼叫編譯函數zend_compile_file()來進行編譯。 這個函數的具體實現其實是包括兩個主要過程的:詞法分析(Lex實現),語法分析(Yacc實現)。當執行完這個函數之後:php指令碼的編譯就算結束了。 這個函數的輸入是:php指令碼檔案,而輸出則是op_array.簡單一點說:編譯過程就是把指令碼給解析成一條條php虛擬機器器可以處理的指令,而op_array就是這些指令做成的一個array而已(這很類似一些編譯型語言編譯產生的組合程式碼了,也是一條條的命令)。

②:之後php虛擬機器器會呼叫zend_execute()這個函數來執行。該函數的輸入就是上邊編譯階段產生的op_array,在這裡他會解析每條命令並進行處理。 由於op命令一共有150左右,所以它需要處理這150中命令。這裡會產生一個很有意思的問題:它是如何處理這150種命令的呢?首先每條命令都是有對應的處理器來進行處理的。所以:虛擬機器器會依據op_array中各條命令的型別來分發給響應的處理器來進行處理。

這裡有兩個小問題: 1:這裡的處理器是什麼? 2:如何分發的?

要解答這兩個問題都是要從分發機制上來解釋:php虛擬機器器分發命令的機制有三種:CALL, SWITCH, 和GOTO這三種型別.php預設是使用CALL方式, 也就是所有的opcode處理器都定義為函數, 然後供虛擬機器器呼叫. 這種方式是傳統的方式, 也一般被認為是最穩定的方式.而SWITCH方式和GOTO方式則是通過switch和goto來分發opcode到對應的處理邏輯(段)執行的.

那現在來回答上邊兩個問題:

1:處理器其實是處理op命令的邏輯。其可以以函數的形式存在,也可能是以邏輯段的方式存在,這取決於命令的分發方式。

2:分發方式有call,switch和goto三種。哪種效率高呢?其實從上邊解釋已經可以初步瞭解了。switch和goto都是在zend_execute()這個函數中有對應的邏輯段,直接執行就可以了。而call是在zend_execute()這個函數中執行函數呼叫。明擺著:函數呼叫效率是最低的,呼叫一次就得壓棧啊!所以效率上:call是最低的。對於switch和goto:比如要執行第三種命令的處理:switch還要先挨個判斷是不是前兩種,而goto根本不需要判斷,直接跳到第三種命令的邏輯程式碼段去執行,這比switch少了順序從上到下判斷的損耗,所以:goto效率又比switch要高。 所以這三種分發方式總體而言:goto > switch > call

題外話:由於php預設是call,如果你想進一步榨乾php的效能,可以更改下其命令分發方式為goto。不過用goto方式雖然提高了執行速度,但是編譯速度上其實最慢的喔。

--------------------------------------------------------------------------------------------------------------------------------------------------

再說一下php這種編譯和執行分離的弱點:

其實也不能算是弱點,雖然zend engine(php的虛擬機器器)將編譯和執行嚴格分開,但是對於使用者而言:就跟沒分開一樣,因為我每次執行一個php指令碼請求都是要執行:編譯->執行 這兩個階段。任何一個階段都少不了。那麼這一點我們可以拿來和c++這種編譯型語言做一下對比: 同一個請求執行100遍

①對於c++,由於其前期只要編譯一遍,編譯好就不會再重複編譯了,只需要執行就ok,所以其損耗為:

1次編譯 + 100次執行

②對於php,其每次都要編譯+執行,所以其損耗為:

100次編譯 + 100次執行

顯然:解釋性語言從數量上來看:其消耗是比編譯型語言多的多。說白了就是:php這種編譯和執行相分離並不是真正的分離。而c++那種才算是真正的分離。

php也早就意識到這個問題了,於是就想了一個辦法來解決這個問題:這個解決方案就是eAccelerator。主要思路如下:

當指令碼第一次執行後,以某種方式儲存編譯後指令碼(裡邊存放的是op_array),在我們規定的快取有效時間內,當第二次執行該指令碼時就不在進行重複性的編譯工作,而是直接呼叫執行前面儲存的編譯後檔案,大大提高了程式效能。

這種方式一定程度上提高了php的效率,但不是最終極的方法,最終極的還是改成編譯型語言那種方式好了,吼吼~~~

---------------------------------------------------------------------------------------------------------------------------------------------------

最後說一下php編譯和執行分離的優點;

這個優點其實是針對程式設計師而言,對使用者而言沒什麼。因為這兩個階段的分離,我們可以在這裡做一些我們想做的事情。

比如想做檔案加解密,你想把一些php指令碼原始碼檔案加密,讓使用者看不到原始碼。同時呢這個加密後的原始碼檔案又可以被php虛擬機器器所解析和處理。當然:要實現這個前提是你先想好加解密演演算法並保證這個是可逆的過程。

現在你對php原始碼檔案已經加密了,此時你需要定義一下這種加密檔案的字尾,假設為:*.buaa。 那問題就是:我們怎麼讓php虛擬機器器可以處理這種字尾的檔案呢?這就要用到上邊所說的編譯和執行相分離的過程了。

回想一下:編譯階段的輸入是php原始檔,輸出是op_array。 ok,我們就在這個階段做文章。主要思路為:首先在zend_compile_file()這個編譯函數中:看一下輸入檔案的字尾:如果是正常的.php那就走正常邏輯,如果是*.buaa,那就先解密然後再走正常邏輯。。。

哈~就是這麼簡單。當然:這個過程沒有所說的這麼簡單,而且你也不可能直接修改zend_compile_file()函數,最後是自己擴充套件實現一個模組來處理這個過程。

結論:

PHP是直譯語言,將PHP程式碼解釋為opcode之後再交由Zend引擎執行。

使用APC快取了opcode,減少了PHP解釋為opcode這一步驟的時間。

推薦學習:

以上就是PHP是直譯語言還是編譯型語言?的詳細內容,更多請關注TW511.COM其它相關文章!