zend.h 에 정의되어 있는 데이터타입.
zval 구조체의 type에 할당되는 값이다.



  Type Value   Purpose
IS_NULL 이 타입은 초기화되지 않은 변수에 자동적으로 할당되며, PHP 소스코드에서 변수에 null을 할당할 때 할당된다. 이 변수타입은 값이 없음을 뜻하며, Boolean타입의 False나 integer 타입의 0과는 다르다.
IS_BOOL Boolean 변수는 True나 False, 둘 중의 하나의 상태를 가진다. if, while, 삼항연산자(ternary), for 같은 구문에서 조건절은 묵시적으로 Boolean 타입으로 변환된다.
IS_LONG PHP의 integer 타입은 해당 시스템의 signed long 타입으로 저장된다. 대부분의 32비트 플랫폼에서 이 갑의 범위는 다음과 같다: -2147483648 ~ 2147483647. 몇가지 예외를 제외하고, PHP에서 이 범위를 벗어나는 integer 값을 할당하면, 해당 값은 자동적으로 double 타입으로 변환된다.
IS_DOUBLE float 타입은 해당 시스템의 signed double 타입으로 저장된다. (부동소수연산의 정확도에 관한 장황한 설명은 생략)
IS_STRING zval 구조체에 항상 문자열(char*)과 문자열의 길이(int)형태로 저장된다. 이런 형태는 null을 포함하고 있는 문자열도 중간에 잘리지 않도록 해준다. (binary safe)
IS_ARRAY PHP의 array는 C의 array와 같은 단일타입의 벡터형태가 아니라, 복합적인 형태이다. PHP의 array의 각 데이터는 HashTable로 이루어져 있으며, HashTable은 label과 data로 이루어져 있다. label은 연관 혹은 숫자형태의 인덱스 key이며 data는 label이 가리키는 값이다.
IS_OBJECT object는 method, 접근자, 상수, 이벤트 핸들러 등을 추가적으로 가지는 다중 엘리먼트 데이터다. extension 개발자는 object-oriented extension을 개발할 때 PHP4와 5에서 동시에 돌아가게 만들려면 신경을 좀 써야한다. Zend Engine 1(PHP4)와 Zend Engine2(PHP5)의 내부 object 구조가 많이 다르기 때문이다.
IS_RESOURCE stdio의 파일포인터나 libmysqlclient의 connection 핸들 같은 건 PHP코드로 쉽게 변환되지 않는다. (그렇게 되어도 곤란하고). 그러므로 해당 타입들은 리소스 타입으로 저장되며 PHP 소스코드에서는 resource id로 접근이 가능하다.







zval은 이렇게 생겼다. (zend.h)

struct _zval_struct {
    zvalue_value value;
    zend_uint refcount;
    zend_uchar type;
    zend_uchar is_ref;
};
typedef struct _zval_struct zval;




zval.zvalue_value 는 해당 변수의 실제 값이며, 여러 타입의 값을 저장할 수 있도록 union 으로 되어있다. (zend.h)

typedef union _zvalue_value {
    long lval;
    double dval;
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;
    zend_object_value obj;
};



zval.refcount는 레퍼런스 카운트이다. PHP의 변수할당은 Copy On Write 이므로 필요한 데이터이다.
zval.type은 해당 변수의 타입이다. 상단에 정의된 상수값 중 하나를 가진다.
zval.is_ref는 이 zval변수가 포인터참조가 되고 있는지 여부를 나타낸다.





zval의 타입을 알기 위해서는 type 데이터를 이용한다.
하지만 추후 변경될 가능성 등을 염두에 두고 type에 바로 접근하지 말고 제공되는 매크로를 이용하는 것이 좋다.

예를 들어 아래와 같이.
zval *foo; // 이게 어디선가 날아왔다고 치자.

// foo의 타입을 알기 위해서 type을 바로 참조해도 사실 무방하긴 하다.
if (foo->type == IS_NULL) ... ...

// 그러나 매크로를 사용하도록 하자.
if (Z_TYPE_P(foo) == IS_NULL) ... ...



각 타입별 매크로는 다음과 같다.
포인터 참조되는 depth(?)에 따라서 다음과 같이 구분된다.

zval foo  =>  Z_TYPE(foo)
zval *foo  => Z_TYPE_P(foo)
zval **foo => Z_TYPE_PP(foo)


zval의 값이 저장되어 있는 zval.value를 꺼내올 때에도 매크로를 이용한다.

Boolean : Z_BVAL
Long : Z_LVAL
Double : Z_DVAL
String : Z_STRVAL, Z_STRLEN
Array : Z_ARRVAL
Object : OBJ_HANDLE, OBJ_HT, OBJCE, OBJPROP, OBJ_HANDLER




zval에 값을 할당할 때에도 다음과 같은 매크로를 이용.

Boolean
ZVAL_BOOL(pzv, 1 or 0);
ZVAL_TRUE(pzv);
ZVAL_FALSE(pzv);

Long
ZVAL_LONG(pzv, 1);

Double
ZVAL_DOUBLE(pzv, 1.23);

String
ZVAL_STRING(pzv, str, len, dup);
ZVAL_STRINGL(pzv, str, dup);

Resource
ZVAL_RESOURCE(pzv, res);





Posted by bloodguy
,