7.ZVAL の内部構造

7.1.zval とは

 PHP スクリプト(ユーザーランド)では、変数やオブジェクトは頭に $ を付けるとか、関数やクラスはシンボルの前に functionclass が来るなど、文法的にさまざまな取り決めがあります。

 一方、PHP の内部(Zend Engine)では、すべての変数やオブジェクトは、それぞれが zval と呼ばれるコンテナによって統合的に管理されています。Extension を開発するにあたっては、ほとんどの内部処理は zval やそのメンバーに対して操作を行なうことになりますので、zval の理解は必須となります。ここでは zval の概要をご紹介します。

 zval は、Zend/zend_types.h で定義されています。

typedef struct _zval_struct     zval;

struct _zval_struct {
  zend_value        value;      /* value */
  union {
    struct {
      ZEND_ENDIAN_LOHI_4(
        zend_uchar    type,     /* active type */
        zend_uchar    type_flags,
        zend_uchar    const_flags,
        zend_uchar    reserved)     /* call info for EX(This) */
    } v;
    uint32_t type_info;
  } u1;
  union {
    uint32_t     next;                 /* hash collision chain */
    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 */
    uint32_t     extra;                /* not further specified */
  } u2;
};

 zval は、以下の3つのエリアから構成されています。

No. 変数名 説明
1 zend_value value オブジェクトの値(一部例外あり)
2 union u1 オブジェクトの型や属性
3 union u2 Zend Engine が内部的に利用する

 Extension で意識しないといけないのは、そのオブジェクトの値を保持する zend_value と、その属性を示す u1 共用体の中のメンバーです。もっとも、これらのメンバーをソース中に記載することはまずありません。基本的に Zend ディレクトリ配下にある膨大なマクロを駆使して、これらのメンバーにアクセスすることになります。

 後半の u2 共用体は、コメントを見ると foreach が使っていそうとかなんとなく想像できるものもありますが、これらは基本的に Zend Engine 自身が使っているもので、Extension を開発する分には特に意識する必要はありません。

7.2.zend_value

 zend_value の定義も Zend/zend_types.h の中にあります。

typedef union _zend_value {
  zend_long         lval;       /* long value */
  double            dval;       /* double value */
  zend_refcounted  *counted;
  zend_string      *str;
  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;
} zend_value;

 これらは union ですので、利用の際はこれらのいずれか一つを選択することになります。オブジェクトの型と、それに対応するプロパティを以下に示しますします。

No. 変数名 PHPにおける型 アクセサ・マクロ(値渡し)
1 zend_long lval 整数(integer) Z_LVAL(zval)
2 double dval 浮動小数点数(float/double) Z_DVAL(zval)
3 zend_refcounted *counted 12.参照カウント法 Z_COUNTED(zval)
4 zend_string *str 文字列(string) Z_STR(zval)
5 zend_array *arr 配列(array) Z_ARR(zval)
6 zend_object *obj オブジェクト Z_OBJ(zval)
7 zend_resource *res リソース Z_RES(zval)
8 zend_reference *ref リファレンス Z_REF(zval)
9 zend_ast_ref *ast 抽象構文木 Z_AST(zval)
10 zval *zv Z_INDIRECT(zval)
11 void *ptr Z_PTR(zval)
12 zend_class_entry *ce クラス Z_CE(zval)
13 zend_function *func コールバック / Callable Z_FUNC(zval)

 それぞれのプロパティに対して、対応するアクセサ・マクロが用意されています。たとえば保持している値が zend_long 値だとわかっている場合、値を取り出すためのアクセサは、値渡しなら Z_LVAL(zval) です。ポインタ渡しの場合のマクロ等のバリエーションについては後述します。

7.3.オブジェクトの型

 (一部の例外を除いて)zval の値は zend_value に入っていますが、その値の型は (zval).u1 にある type / type_flags / type_info といったプロパティで管理されています。実際の型は、Zend/zend_types.h で以下のように定義されています。

#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

 これらは必ずしも同じレベルではありません。たとえば bool 値を扱う場合 _IS_BOOL で判定しますが、実際にセットする値は IS_TRUE または IS_FALSE であり、セットする先も zend_value ではなく (zval).u1.type_info だったりします。ただマクロを使っている限りは、これらの差異を意識しなくても済みます。

 なお IS_STRING 以上の型については、それぞれに独自のコンストラクタとデストラクタを持つ場合があります。

7.4.アクセサ・マクロ

 前項の型を判定するためのマクロや、値にアクセスするためのアクセサ・マクロには、値渡し以外にポインタ渡しバージョンもあります。 これらのマクロ定義は Zend/zend_types.h の中にあります。

アクセサ・マクロ
No. 型名シンボル
アクセサ(値/ポインタ)
判定(値/ポインタ)
1 IS_UNDEF N/A
Z_ISUNDEF(zval) /
Z_ISUNDEF_P(zval_p)
2 IS_NULL N/A
Z_ISNULL(zval)
ZVAL_IS_NULL(z) /
Z_ISNULL_P(zval_p)
3
IS_FALSE
IS_TRUE
N/A N/A
4 IS_LONG
Z_LVAL(zval) / Z_LVAL_P(zval_p)
N/A
5 IS_DOUBLE
Z_DVAL(zval) / Z_DVAL_P(zval_p)
N/A
6 IS_STRING
Z_STR(zval) / Z_STR_P(zval_p)
(別途)
7 IS_ARRAY
Z_ARR(zval) / Z_ARR_P(zval_p)
Z_ARRVAL(zval) / Z_ARRVAL_P(zval_p)
(別途)
8 IS_OBJECT
Z_OBJ(zval) / Z_OBJ_P(zval_p)
Z_OBJ_HT(zval) / Z_OBJ_HT_P(zval_p)
Z_OBJ_HANDLER(zval, hf)
/ Z_OBJ_HANDLER_P(zv_p, hf)
Z_OBJ_HANDLE(zval) / Z_OBJ_HANDLE_P(zval_p)
Z_OBJCE(zval) / Z_OBJCE_P(zval_p)
Z_OBJPROP(zval) / Z_OBJPROP_P(zval_p)
Z_OBJDEBUG(zval,tmp) / Z_OBJDEBUG_P(zval_p,tmp)
(別途)
9 IS_RESOURCE
Z_RES(zval) / Z_RES_P(zval_p)
Z_RES_HANDLE(zval) / Z_RES_HANDLE_P(zval_p)
Z_RES_TYPE(zval) / Z_RES_TYPE_P(zval_p)
Z_RES_VAL(zval) / Z_RES_VAL_P(zval_p)
(別途)
10 IS_REFERENCE
Z_REF(zval) / Z_REF_P(zval_p)
Z_REFVAL(zval) / Z_REFVAL_P(zval_p)
Z_ISREF(zval) /
Z_ISREF_P(zval_p)

7.5.代入・返却用マクロ

 代入用マクロには、直に値を代入する以外にも、値を初期化したりコピーしたりするものもあります。

 呼び出し元のユーザーランドに値を返には return_value (疑似)グローバル変数に値をセットする必要がありますが、この return_valuezval * 型です。 return_value に値をセットするだけのマクロ、および値をセットしてそのまま return する返却用マクロが用意されています。

代入・返却用マクロ
型名シンボル 変数への代入 return_value への代入 呼び出し元への返却
IS_UNDEF ZVAL_UNDEF(z) N/A N/A
IS_NULL ZVAL_NULL(z) RETVAL_NULL() RETURN_NULL()
IS_FALSE
IS_TRUE
ZVAL_BOOL(z, b)
ZVAL_FALSE(z)
ZVAL_TRUE(z)
RETVAL_BOOL(b)
RETVAL_FALSE
RETVAL_TRUE
RETURN_BOOL(b)
RETURN_FALSE
RETURN_TRUE
IS_LONG ZVAL_LONG(z, l) RETVAL_LONG(l) RETURN_LONG(l)
IS_DOUBLE ZVAL_DOUBLE(z, d) RETVAL_DOUBLE(d) RETURN_DOUBLE(d)
IS_STRING
ZVAL_STR(z, s)
ZVAL_INTERNED_STR(z, s)
ZVAL_NEW_STR(z, s)
ZVAL_STR_COPY(z, s)
RETVAL_STR(s)
RETVAL_INTERNED_STR(s)
RETVAL_NEW_STR(s)
RETVAL_STR_COPY(s)
RETVAL_STRING(s)
RETVAL_STRINGL(s, l)
RETVAL_EMPTY_STRING()
RETURN_STR(s)
RETURN_INTERNED_STR(s)
RETURN_NEW_STR(s)
RETURN_STR_COPY(s)
RETURN_STRING(s)
RETURN_STRINGL(s, l)
RETURN_EMPTY_STRING()
IS_ARRAY
ZVAL_ARR(z, a)
ZVAL_NEW_ARR(z)
ZVAL_NEW_PERSISTENT
_ARR(z)
RETVAL_ARR(r) RETURN_ARR(r)
IS_OBJECT ZVAL_OBJ(z, o) RETVAL_OBJ(r) RETURN_OBJ(r)
IS_RESOURCE
ZVAL_RES(z, r)
ZVAL_NEW_RES(z, h, p, t)
ZVAL_NEW_PERSISTENT
_RES(z, h, p, t)
RETVAL_RES(r) RETURN_RES(r)
IS_REFERENCE
ZVAL_REF(z, r)
ZVAL_NEW_EMPTY_REF(z)
ZVAL_NEW_REF(z, r)
ZVAL_NEW_PERSISTENT
_REF(z, r)
ZVAL_UNREF(z)
ZVAL_COPY_UNREF(z, v)
N/A N/A

7.6.特殊な型

 内部で使用する特殊な型もあります。これらは関数の戻り値にはなりません。

型の定義

/* constant expressions */
#define IS_CONSTANT       11
#define IS_CONSTANT_AST   12

/* fake types */
#define _IS_BOOL          13
#define IS_CALLABLE       14
#define IS_ITERABLE       19
#define IS_VOID           18

/* internal types */
#define IS_INDIRECT       15
#define IS_PTR            17
#define _IS_ERROR         20

 これらに関するマクロには以下のようなものがあります。

特殊な型に関するマクロ
No. 型の定義 判定用マクロ アクセサマクロ 代入用マクロ
1
IS_CONSTANT
IS_CONSTANT_AST
Z_CONSTANT(zval)
Z_CONSTANT_P(zval_p)
N/A
Z_AST(zval) /
Z_AST_P(zval_p)
Z_ASTVAL(zval) /
Z_ASTVAL_P(zval_p)
ZVAL_NEW_AST(z, a)
2 _IS_ERROR
Z_ISERROR(zval) /
Z_ISERROR_P(zval_p)
N/A ZVAL_ERROR(z)
3 IS_INDIRECT N/A
Z_INDIRECT(zval) /
Z_INDIRECT_P(zval_p)
ZVAL_INDIRECT(z, v)

 上記には記載しきれないマクロもまだ多数ありますので、これらのヘッダファイルをじ っくりと研究する必要があります。なお、文字列や配列等については後述します。