<delect id="sj01t"></delect>
  1. <em id="sj01t"><label id="sj01t"></label></em>
  2. <div id="sj01t"></div>
    1. <em id="sj01t"></em>

            <div id="sj01t"></div>
            php語言

            php內核分析之zval

            時間:2025-05-07 18:31:30 php語言 我要投稿
            • 相關推薦

            php內核分析之zval

              學習PHP的同學對php內核方面的知識也許了解的還不是很清楚,那么下面小編就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] = '\0';

              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;\0

              };

              結合到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

              #define IS_CONSTANT_AST 12

            【php內核分析之zval】相關文章:

            php內核分析之擴展10-03

            php內核分析之opcode08-04

            php內核分析之全局變量09-03

            php內核分析之zend-compile09-01

            php內核分析之do-cli05-30

            php內核分析之sapi-module-struct10-17

            php內核分析之ZTS和zend-try07-18

            php學習之php配置07-15

            php學習之php預定義變量07-29

            <delect id="sj01t"></delect>
            1. <em id="sj01t"><label id="sj01t"></label></em>
            2. <div id="sj01t"></div>
              1. <em id="sj01t"></em>

                      <div id="sj01t"></div>
                      黄色视频在线观看