변수의 기본 구조
우리 모두는 PHP 변수가 약한 유형이라는 것을 알고 있으므로 선언할 때 유형을 지정할 필요가 없습니다. 그렇다면 이것은 어떻게 구현됩니까? 이는 변수의 기본 구조부터 시작해야 합니다.
Zval 구현
소스 코드 파일 zend_type.h에서 zval의 정의를 볼 수 있습니다.
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의 구조는 변수 유형의 값 또는 포인터를 저장하는 Union Union zend_value와 두 개의 Union Union으로 구성됩니다. u1과 u2는
- u1
u1로 구성되며, 변수 유형과 해당 정보를 저장하는 데 사용됩니다.
type: 변수 유형을 기록합니다. u2.v.type을 통해
type_flags에 액세스할 수 있습니다. 고유한 유형의 변수(예: 상수 유형, 참조 계산 유형, 불변 유형)에 해당하는 플래그입니다.
const_flags: 상수 유형 플래그
reserved: 예약된 필드
- u2
u2는 주로 보조 기능입니다. 구조의 메모리 정렬로 인해 u2가 있든 없든 u2의 공간이 이미 공간을 차지했습니다. , 그러니 활용해보세요. u2의 보조 필드는 많은 유형 정보를 기록하는데, 이는 내부 기능에 큰 이점을 주거나 캐시 친화성을 향상시키거나 메모리 주소 지정 작업을 줄입니다. 여기에는 이러한 필드 중 일부가 소개되어 있습니다.
next: 해시 충돌 문제(해시 충돌은 아직 이해되지 않음)를 해결하고 충돌의 다음 요소 위치를 기록하는 데 사용됩니다.
cache_slot: 런타임 캐시. 함수를 실행할 때 먼저 캐시에서 검색합니다. 캐시에서 찾을 수 없으면 전역 함수 테이블에서 검색합니다.
num_args: 함수 호출 시 전달되는 매개변수 수
access_flags: public protected private과 같은 객체 클래스의 액세스 플래그.
- zend_value
typedef union _zend_value { zend_long lval; /* 整型*/ double dval; /* 浮点型 */ 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;
zend__value에서 볼 수 있듯이 long 및 double 유형은 값을 직접 저장하는 반면, 다른 유형은 해당 구조를 가리키는 포인터입니다. 따라서 zval의 구조로 인해 PHP 변수는 변수를 선언할 때 해당 유형을 명시적으로 지정할 필요가 없습니다. 변수에 할당한 값의 유형에 관계없이 해당 저장 구조를 찾는 데 도움이 될 수 있기 때문입니다.
값이 문자열인 변수를 예로 들면 그 구조는 다음과 같습니다.
PHP5와 PHP7의 zval 구조 비교
- PHP5
- PHP7
php7의 zval을 볼 수 있습니다. PHP5의 zval이 차지하는 48바이트에 비해 총 16바이트만 차지하므로 많은 메모리를 절약합니다.
또한 PHP5에서는 모든 변수가 힙에 할당되지만 임시 변수의 경우 힙에 할당할 필요가 없습니다. 따라서 이는 PHP7에서 최적화되었으며 임시 변수는 스택에 직접 적용됩니다.
공통 변수 유형
다음은 몇 가지 일반적인 유형의 변수 구조를 소개합니다. 더 많은 유형에 대해서는 소스 코드를 직접 볼 수 있습니다.
정수 및 부동 소수점 유형
정수 및 부동 소수점 유형의 경우 공간이 작기 때문에 정수 값은 zval에 직접 저장되고 lval에 저장되는 반면 부동 소수점 값은 dval에 저장됩니다. .
typedef union _zend_value { zend_long lval; /* 整型*/ double dval; /* 浮点型 */ ... }
Strings
PHP 7에서는 새로운 문자열 구조가 정의되었습니다. 구조는 다음과 같습니다.
struct _zend_string { zend_refcounted_h ; zend_ulong h; /* hash value */ size_t len; char val[1]; };
위 각 필드의 의미:
gc: 변수 참조 정보, 참조 카운팅을 사용하는 모든 변수 유형은 이 구조를 갖습니다.
h: 배열의 인덱스를 계산할 때 사용되는 해시 값입니다. (이 작업을 수행하면 PHP7의 성능이 5% 향상된다고 합니다.)
len: 문자열 길이, 이 값을 통해 바이너리 보안이 보장됩니다.
val: 문자열 내용, 가변 길이 구조체, 할당 시 len 길이에 따라 메모리를 적용합니다.
Array
배열은 PHP의 매우 강력한 데이터 구조입니다. 기본 구현은 일반적인 순서의 HashTable입니다. 자세한 내용은 나중에 설명하겠습니다.
typedef struct _zend_array HashTable; struct _zend_array { zend_refcounted_h gc; union { struct { ZEND_ENDIAN_LOHI_4( zend_uchar flags, zend_uchar nApplyCount, zend_uchar nIteratorsCount, zend_uchar consistency) } v; uint32_t flags; } u; uint32_t nTableMask; Bucket *arData; uint32_t nNumUsed; uint32_t nNumOfElements; uint32_t nTableSize; uint32_t nInternalPointer; zend_long nNextFreeElement; dtor_func_t pDestructor; }
Objects
PHP7의 객체 구조도 재설계되었으며 이는 PHP5 구현과 매우 다릅니다.
struct _zend_object { zend_refcounted_h gc; uint32_t handle; zend_class_entry *ce; const zend_object_handlers *handlers; HashTable *properties; zval properties_table[1]; };
다음은 일부 필드입니다.
gc: gc 헤더
*ce: 개체에 해당하는 클래스
*properties: HashTable 구조, key는 개체의 속성 이름, value는 개체의 속성 값입니다. Properties_tables array 의 오프셋, 오프셋을 통해 Properties_talbe에서 해당 속성 값을 찾습니다.
properties_talbe[1]: 객체의 속성 값을 저장합니다
좋습니다. 먼저 여기에 적어보겠습니다.