<address id="ousso"></address>
<form id="ousso"><track id="ousso"><big id="ousso"></big></track></form>
  1. php內核分析之zval

    時間:2025-11-19 09:11:44 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); /pic/p>

      ...

      zend_execute(new_op_array, &local_retval); /pic/p>

      ...

      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結構

      /pic/p>

      struct _zval_struct {

      zend_value value; /pic/p>

      union {

      struct {

      ZEND_ENDIAN_LOHI_4(

      zend_uchar type, /pic/IS_INT)

      zend_uchar type_flags, /pic/p>

      zend_uchar const_flags, /pic/p>

      zend_uchar reserved) /pic/p>

      } v;

      uint32_t type_info; /pic/p>

      } u1; /pic/p>

      union {

      uint32_t next; /pic/p>

      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; /pic/p>

      };

      這個接口最重要的兩個字段是 value,存儲變量的值。另一個是u1.v.type 存儲變量的類型。這里,value也是一個結構

      typedef union _zend_value {

      zend_long lval; /* long value */

      double dval; /* double value */

      zend_refcounted *counted;

      zend_string *str; /pic/p>

      zend_array *arr; /pic/p>

      zend_object *obj; /pic/p>

      zend_resource *res; /pic/p>

      zend_reference *ref; /pic/p>

      zend_ast_ref *ast; /pic/p>

      zval *zv;

      void *ptr;

      zend_class_entry *ce; /pic/p>

      zend_function *func; /pic/p>

      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); /pic/p>

      這個函數就是把pv設置為zend_string類型

      /pic/p>

      #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)

      ...

      /pic/p>

      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); /pic/p>

      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的結構:

      /pic/p>

      struct _zend_string {

      zend_refcounted_h gc; /pic/p>

      zend_ulong h; /pic/p>

      size_t len; /pic/p>

      char val[1]; /pic/p>

      };

      _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; /pic/p>

      union {

      struct {

      ZEND_ENDIAN_LOHI_3(

      zend_uchar type, /pic/p>

      zend_uchar flags, /pic/p>

      uint16_t gc_info) /pic/p>

      } v;

      uint32_t type_info; /pic/p>

      } u; /pic/p>

      } zend_refcounted_h;

      回到我們的實例,我們調用的是

      zend_string_init(s, l, 0) /pic/p>

      返回的zend_string實際值為:

      struct _zend_string {

      struct {

      uint32_t refcount; /pic/p>

      union {

      struct {

      ZEND_ENDIAN_LOHI_3(

      zend_uchar type, /pic/p>

      zend_uchar flags,

      uint16_t gc_info)

      } v;

      uint32_t type_info; /pic/p>

      } u;

      } gc;

      zend_ulong h; /pic/p>

      size_t len; /pic/p>

      char val[1]; /pic/p>

      };

      結合到zval里面,那么ZVAL_STRINGL(&pv, str, str_len);返回的zval為

      /pic/p>

      struct _zval_struct {

      union _zend_value {

      zend_long lval;

      double dval;

      zend_refcounted *counted;

      zend_string *str; /pic/p>

      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; /pic/p>

      } 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內核分析之擴展01-31

    php內核分析之opcode02-15

    php內核分析之do-cli01-09

    php內核分析之zend-compile10-17

    php內核分析之全局變量12-14

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

    php內核分析之ZTS和zend-try08-24

    php學習之php配置03-11

    php學習之php預定義變量11-02

    <address id="ousso"></address>
    <form id="ousso"><track id="ousso"><big id="ousso"></big></track></form>
    1. 日日做夜狠狠爱欧美黑人