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,儲存變數的值。另一個是u1.v.type 儲存變數的型別。這裡,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;
如果u1.v.type == IS_STRING, 那麼value.str就是指向了zend_string結構。好了,php的垃圾回收是通過引用計數來進行的,這個引用計數的計數器就放在zval.value.counted裡面。
我們對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] = ` `;
return ret;
}
這個函式可以看的點有幾個:
persistent
這個引數是用來代表申請的空間是不是“臨時”的。這裡說的臨時是zend提供的一種記憶體管理器,相關請求資料只服務於單個請求,最遲會在請求結束的時候釋放。
臨時記憶體申請對應的函式為:
void *emalloc(size_t size)
而永久記憶體申請對應的函式為:
malloc
zend_string_alloc
static zend_always_inline zend_string *zend_string_alloc(size_t len, int persistent)
{
zend_string *ret = (zend_string *)pemalloc(ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent);
GC_REFCOUNT(ret) = 1;
GC_TYPE_INFO(ret) = IS_STRING | ((persistent ? IS_STR_PERSISTENT : 0) << 8);
zend_string_forget_hash_val(ret);
ZSTR_LEN(ret) = len;
return ret;
}
我們先看看zend_string的結構:
// 字串
struct _zend_string {
zend_refcounted_h gc; // gc使用的被引用的次數
zend_ulong h; // 如果這個字串作為hashtable的key在查詢時候需要重複計算它的hash值,所以儲存一份在這裡
size_t len; // 字串長度
char val[1]; // 柔性陣列,雖然我們定義了陣列只有一個元素,但是在實際分配記憶體的時候,會分配足夠的記憶體
};
_ZSTR_STRUCT_SIZE(len) gc+h+len的空間,最後給了val留了len+1的長度
#define _ZSTR_STRUCT_SIZE(len) (_ZSTR_HEADER_SIZE + len + 1)
## GC_REFCOUNT(ret) = 1;
#define GC_REFCOUNT(p) (p)->gc.refcount
這裡就看到一個結構zend_refcounted_h
typedef struct _zend_refcounted_h {
uint32_t refcount; // 真正的計數
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type, // 冗餘了zval中的型別值
zend_uchar flags, // used for strings & objects中有特定作用
uint16_t gc_info) // 在GC緩衝區中的索引位置
} v;
uint32_t type_info; // 冗餘zval中的type_info
} u; // 型別資訊
} zend_refcounted_h;
回到我們的例項,我們呼叫的是
zend_string_init(s, l, 0) // s=char*(echo 12;) l=8
返回的zend_string實際值為:
struct _zend_string {
struct {
uint32_t refcount; // 1
union {
struct {
ZEND_ENDIAN_LOHI_3(
zend_uchar type, // IS_STRING
zend_uchar flags,
uint16_t gc_info)
} v;
uint32_t type_info; //IS_STRING | 0 => IS_STRING
} u;
} gc;
zend_ulong h; // 0
size_t len; // 8
char val[1]; // echo 12;
};
結合到zval裡面,那麼ZVAL_STRINGL(&pv, str, str_len);返回的zval為
// zval的結構
struct _zval_struct {
union _zend_value {
zend_long lval;
double dval;
zend_refcounted *counted;
zend_string *str; // 指向到上面定義的那個zend_string中
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} value;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar type,
zend_uchar type_flags,
zend_uchar const_flags,
zend_uchar reserved)
} v;
uint32_t type_info; // IS_STRING_EX
} u1;
union {
uint32_t next;
uint32_t cache_slot;
uint32_t lineno;
uint32_t num_args;
uint32_t fe_pos;
uint32_t fe_iter_idx;
uint32_t access_flags;
uint32_t property_guard;
} u2;
};
這裡,就對zval結構有初步瞭解了。
另外建議記住幾個常用的型別,後續除錯的時候會很有用
/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
/* constant expressions */
#define IS_CONSTANT 11
本文轉自軒脈刃部落格園部落格,原文連結:http://www.cnblogs.com/yjf512/p/6108444.html,如需轉載請自行聯絡原作者
相關文章
- PHP核心探索之變數(1)Zval(自己看過不錯兒)PHP變數
- php核心分析(六)-opcodePHP
- PHP 核心分析:Zend 虛擬機器PHP虛擬機
- PHP 核心分析(1):sapi_module_structPHPAPIStruct
- php核心分析(一)-sapi_module_structPHPAPIStruct
- PHP 核心分析(2):ZTS和zend_tryPHP
- ThinkPHP6 核心分析(五):多應用解析PHP
- 【PHP】CI框架原始碼分析核心檔案之Input.phpPHP框架原始碼
- 【PHP】CI框架原始碼分析核心檔案之Loader.phpPHP框架原始碼
- 【PHP】CI框架原始碼分析核心檔案之Utf8.phpPHP框架原始碼
- 【PHP】CI框架原始碼分析核心檔案之Controller.phpPHP框架原始碼Controller
- ThinkPHP3.1.3原始碼分析(五) App.class.phpPHP原始碼APP
- PHP系列(五)PHP字串處理PHP字串
- PHP 核心特性 - TraitPHPAI
- 自學PHP筆記(五) PHP運算子PHP筆記
- php 核心探祕之 PHP_FUNCTION 巨集PHPFunction
- [PHP核心探索]PHP中的雜湊表PHP
- 20242822《Linux核心原理與分析》第五週作業Linux
- PHP 核心 - 異常處理PHP
- PHP 核心特性 - 匿名函式PHP函式
- PHP核心探索之PHP中的雜湊表PHP
- toa 核心模組分析
- Android核心分析Android
- 【iCore3 雙核心板_FPGA】例程五:Signal Tapll實驗——邏輯分析儀FPGA
- Hibernate的五大核心介面
- PHP 核心特性 - 錯誤處理PHP
- PHP 核心特性 - 名稱空間PHP
- PHP 核心技術 --物件導向PHP物件
- PHP 模板引擎用到的核心方法PHP
- YARN 核心原始碼分析Yarn原始碼
- mmap核心原始碼分析原始碼
- epoll核心實現分析
- Linux核心分析。3Linux
- Linux核心分析。4Linux
- Linux核心分析。5Linux
- LINUX核心分析。6Linux
- LINUX核心分析。7Linux
- LINUX核心分析。8Linux