一、PHP底層原理--知其然知其所以然,先看一張圖
1、我們寫的PHP代碼是不能直接啟動並執行,它首先經過詞法分析器-文法分析器和編譯器
這裡你可能會覺得,PHP怎麼會是編譯性語言呢。一般認為PHP是指令碼語言,是的。但嚴格的說來,PHP也是一種編譯語言,它會將PHP編譯為opcode的一個中繼語言,有點像JAVA中的class檔案。
2、產生cpcode之後再由zend公司開發的執行引擎執行
我們來做一個比較
C、C++語言
|
JAVA |
編譯成機器碼(二進位)來運行 |
先把JAVA檔案編譯成.class,被稱為bytecode,然後再用jvm來運行 所以JAVA是不可以跨平台的,而是jvm是跨平台的 |
解釋語言
|
PHP |
解譯器解釋執行,最經典的如:linux shell 解譯器逐條來執行命令 |
PHP有特殊,雖然它是一個指令碼語言,但不是靠解譯器解釋執行。 而是zend虛擬機器屏蔽了作業系統 PHP代碼編譯成opcode.由虛擬機器來執行opcode 但是--opcode,PHP指令碼一結束,opcode就清除了(java可以將.class打包發布) |
3、那麼opcode是否可以緩衝呢。
PHP本身不支援,但是apc,xcache等加速器實現了這樣的效果(如果一產生就扔掉就太浪費了)我們看看一個總結
zend編譯器(PHP->opcode) |
zend虛擬機器(執行opcode) |
作業系統層面 win/linux/mac |
二、PHP變數的底層實現
PHP底層用C語言來實現的,C語言是強型別,而PHP是弱類型語言(如必須加上變數類型)。那麼PHP是如何?的呢。
我們解壓PHP的原始碼包,看到如下的目錄:如圖所示
我們來分析
我們開啟Zend檔案,然後找到zend.h檔案
找到
typedef struct _zval_struct zval 的結構體
再看看它的實現
struct _zval_struct {
/* Variable information */
zvalue_value value; /* value */
zend_uint refcount__gc;
zend_uchar type; /* active type */
zend_uchar is_ref__gc;
}
PHP變數實現的核心就在上面的幾行代碼之中
PHP變數的值就是用上面的結構體來描述的
它由四個欄位組成
分析第一個,zvalue_value value;
typedef union _zvalue_value { // 聯合(枚舉)
long lval;
double dval;
struct {
char *val;
int len;
};
HashTable *ht;
zend_object_value obj;
} zvalue_value;
分析第二個,zend_uchar type;
它有可能是IS_NULL, IS_BOOL
所以判斷一個變數的類型是什麼,是根據zend_uchar type來決定的
它的值是多少是通過typedef union _zvalue_value它的聯合體來決定的
我們來分析一段代碼,看看PHP中的變數是怎麼實現的
$a = 3;/***一個結構體產生了{ union_value {long 3} // 描述值type IS_LONG // 描述類型refcount_gc:1is_ref_gc:0}***/
type欄位的值以常量形式存在
我們看看它具體儲存的是什麼
IS_NULL, IS_BOOL, IS_LONG, IS_DOUBLE, IS_STRING, IS_ARRARY. IS_OBJECT, IS_RESOURCE
它其實對應了PHP數組中的八種資料類型
這裡產生了一個問題,在zvalue_value中只提供了五中資料類型,下面是三種沒有對應上的類型
1: NULL直接對應zval->type=IS_NULL,就可以表示不必設定value的置 |
2: BOOL型,zval->type=IS_BOOL,再設定zval_value.lval = 1/0 |
3: Resource型,資源型往往是伺服器上開啟的一個介面,如果檔案讀取介面 zval->type=IS_RESOURCE, zval->type.lval=伺服器開啟的介面的編號 |
這裡需要我們特別注意一點:
PHP中,字串類型,長度是已經緩衝的,調用strlen不用計算,速度非常快;長度是直接放入其結構體中
$b = 'hello';/**{ { char:'hello'; len:5; } type: IS_STRING; refcount__gc:1 is_ref__gc:0}**/
再問一個問題,變數的名放在哪裡的
在PHP中,變數的名是放在符號表中的symbol_table
這裡要聞,符號表是什麼。
符號表就是一張雜湊表,裡面儲存了變數名->變數的zval結構體的地址
Zend/zend_globals.h找到HashTable synbol_table; /* main synbol table */
雜湊表我們就可以把它作為一個關聯陣列
看一段代碼,看見聲明一個變數,就必須要想到的事情
$a = 5;$b = 5.54321;$c = 'hello';/**產生了3個結構體同時,全域富豪表中,多了3條記錄a--> 0x123 --> 結構體 {5}b--> 0x213 --> 結構體 {5.54321}c--> 0x339 --> 結構體 {hello}**/
上面是全域的符號表,一般由全域的必定有局部的符號表
HashTable *active_synbol_table
原理都是一樣的
我們再看看變數的賦值與引用,先看如下代碼:
$a = 3;
$b = $a
在底層是否產生了2個結構體呢。
是的,在PHP實現的過程中並沒有產生一個新的結構體
$a = 3/**zvalue:3type:IS_LONGrefount__gc=1is_ref_gc:0**/$b = $a/**zvalue:3type:IS_LONGrefcount__gc=2 這裡發生變化is_ref_gc:0<pre name="code" class="php">它們在地址上都指向同一個結構體,節省空間的,並沒有發生結構體的複製
**/
123