當前位置:才華齋>計算機>php語言>

php核心分析之zval

php語言 閱讀(1.47W)

學習PHP的同學對php核心方面的知識也許瞭解的還不是很清楚,那麼下面小編就php核心之zval展開分析,希望對大家有用,更多內容請關注應屆畢業生網!

php核心分析之zval

這裡閱讀的php版本為PHP-7.1.0 RC3,閱讀程式碼的平臺為linux

實際上,從這個函式開始,就已經進入到了zend引擎的範圍了。

zend_eval_string_ex(exec_direct, NULL, "Command line code", 1)

實際上是呼叫Zend/zend_execute_API.c

zend_eval_stringl_ex(str, strlen(str), retval_ptr, string_name, handle_exceptions);

再進去是呼叫

result = zend_eval_stringl(str, str_len, retval_ptr, string_name);

這裡的retval_ptr為NULL,string_name為"Command line code", str為"echo 12;"

  zend_eval_stringl

其實這個函式主流程並不複雜。簡化下來就如下

ZEND_API int zend_eval_stringl(char *str, size_t str_len, zval *retval_ptr, char *string_name) /* {{{ */

{

...

new_op_array = zend_compile_string(&pv, string_name); // 這個是把php程式碼編譯成為opcode的過程

...

zend_execute(new_op_array, &local_retval); // 這個是具體的執行過程,執行opcode,把結果儲存到local_retval中

...

retval = SUCCESS;

return retval;

}

先把php編譯為opcode,然後執行這個opcode。只是這個函式有一些關鍵的結構需要理一下。

  zval

我們會看到

zval local_retval;

這樣的變數,然後會對這個變數進行如下操作:

ZVAL_UNDEF(&local_retval);

ZVAL_NULL(z)

ZVAL_FALSE(z)

ZVAL_TRUE(z)

ZVAL_BOOL(z, b)

ZVAL_LONG(z, l)

ZVAL_DOUBLE(z, d)

ZVAL_STR(z, s)

ZVAL_INTERNED_STR(z, s)

ZVAL_NEW_STR(z, s)

ZVAL_STR_COPY(z, s)

ZVAL_ARR(z, a)

ZVAL_NEW_ARR(z)

ZVAL_NEW_PERSISTENT_ARR(z)

ZVAL_OBJ(z, o)

ZVAL_RES(z, r)

ZVAL_NEW_RES(z, h, p, t)

ZVAL_NEW_PERSISTENT_RES(z, h, p, t)

ZVAL_REF(z, r)

ZVAL_NEW_EMPTY_REF(z)

ZVAL_NEW_REF(z, r)

ZVAL_NEW_PERSISTENT_REF(z, r)

ZVAL_NEW_AST(z, a)

ZVAL_INDIRECT(z, v)

ZVAL_PTR(z, p)

ZVAL_FUNC(z, f)

ZVAL_CE(z, c)

ZVAL_ERROR(z)

php是一個弱型別的語言,它可以用一個$var來代表string,int,array,object等。這個就是歸功於zval_struct結構

// zval的結構

struct _zval_struct {

zend_value value; // 儲存具體值,它的結構根據型別不同而不同

union {

struct {

ZEND_ENDIAN_LOHI_4(

zend_uchar type, // 這個位置標記了這個val是什麼型別的(IS_STRING/IS_INT)

zend_uchar type_flags, // 這個位置標記了這個val是什麼屬性 (IS_CALLABLE等)

zend_uchar const_flags, // 常量的一些屬性 (IS_CONSTANT_CLASS)

zend_uchar reserved) // 保留的一些欄位

} v;

uint32_t type_info; // 型別的一些額外資訊

} u1; // 儲存型別的一些關鍵資訊

union {

uint32_t next; // 如果是在hash連結串列中,這個指標代表下一個元素的index

uint32_t cache_slot; /* literal cache slot */

uint32_t lineno; /* line number (for ast nodes) */

uint32_t num_args; /* arguments number for EX(This) */

uint32_t fe_pos; /* foreach position */

uint32_t fe_iter_idx; /* foreach iterator index */

uint32_t access_flags; /* class constant access flags */

uint32_t property_guard; /* single property guard */

} u2; // 一些附屬欄位

};

這個介面最重要的兩個欄位是 value,儲存變數的值。另一個是 儲存變數的型別。這裡,value也是一個結構

typedef union _zend_value {

zend_long lval; /* long value */

double dval; /* double value */

zend_refcounted *counted;

zend_string *str; // string

zend_array *arr; // array

zend_object *obj; // object

zend_resource *res; // resource

zend_reference *ref; // 指標

zend_ast_ref *ast; // ast指標

zval *zv;

void *ptr;

zend_class_entry *ce; // class實體

zend_function *func; // 函式實體

struct {

uint32_t w1;

uint32_t w2;

} ww;

} zend_value;

如果 == IS_STRING, 那麼就是指向了zend_string結構。好了,php的垃圾回收是通過引用計數來進行的,這個引用計數的計數器就放在ted裡面。

我們對zval設定的時候設定了一些巨集來進行設定,比如:ZVAL_STRINGL是設定string,我們仔細看下呼叫堆疊:

ZVAL_STRINGL(&pv, str, str_len); // 把pv設定為string型別,值為str

這個函式就是把pv設定為zend_string型別

// 帶字串長度的設定zend_sting型別的zval

#define ZVAL_STRINGL(z, s, l) do {

ZVAL_NEW_STR(z, zend_string_init(s, l, 0));

} while (0)

注意到,這裡使用了一個寫法,do {} while(0) 來設定一個巨集,這個是C裡面比較好的寫法,這樣寫,能保證巨集中定義的東西在for,if,等各種流程語句中不會出現語法錯誤。不過其實我們學習程式碼的時候,可以忽略掉這個框框寫法。

zend_string_init(s, l, 0)

...

// 從char* + 長度 + 是否是臨時變數(persistent為0表示最遲這個申請的空間在請求結束的時候就進行釋放),轉變為zend_string*

static zend_always_inline zend_string *zend_string_init(const char *str, size_t len, int persistent)

{

zend_string *ret = zend_string_alloc(len, persistent); // 申請空間,申請的大小為zend_string結構大小(除了val)+ len + 1

memcpy(ZSTR_VAL(ret), str, len);

ZSTR_VAL(ret)[len] = '