class Student{}
$s = new Student;
https://3v4l.org/RsEDf/vld#output
對應的 opcode:
NOP
NEW
DO_FCALL
ASSIGN
RETURN
程式編譯完後生成 op_array,編譯階段的最終產物,也是執行階段的輸入;op_array屬性中包含了編譯好的opcodes
列印出的opcodes:
{{handler = 0x555555b4c981 <ZEND_NOP_SPEC_HANDLER>, op1 = {constant = 0, var = 0, num = 0, opline_num = 0, jmp_offset = 0}, op2 = {constant = 0, var = 0, num = 0, opline_num = 0,
jmp_offset = 0}, result = {constant = 0, var = 0, num = 0, opline_num = 0, jmp_offset = 0}, extended_value = 0, lineno = 3, opcode = 0 '\000', op1_type = 8 '\b', op2_type = 8 '\b',
result_type = 8 '\b'}, {handler = 0x555555b4fff2 <ZEND_NEW_SPEC_CONST_HANDLER>, op1 = {constant = 0, var = 0, num = 0, opline_num = 0, jmp_offset = 0}, op2 = {constant = 3, var = 3,
num = 3, opline_num = 3, jmp_offset = 3}, result = {constant = 112, var = 112, num = 112, opline_num = 112, jmp_offset = 112}, extended_value = 0, lineno = 5, opcode = 68 'D',
op1_type = 1 '\001', op2_type = 8 '\b', result_type = 4 '\004'}, {handler = 0x555555b4a2ac <ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER>, op1 = {constant = 8, var = 8, num = 8, opline_num = 8,
jmp_offset = 8}, op2 = {constant = 0, var = 0, num = 0, opline_num = 0, jmp_offset = 0}, result = {constant = 2, var = 2, num = 2, opline_num = 2, jmp_offset = 2}, extended_value = 0,
lineno = 5, opcode = 60 '<', op1_type = 8 '\b', op2_type = 8 '\b', result_type = 8 '\b'}, {handler = 0x555555ba3cd2 <ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER>, op1 = {constant = 80,
var = 80, num = 80, opline_num = 80, jmp_offset = 80}, op2 = {constant = 112, var = 112, num = 112, opline_num = 112, jmp_offset = 112}, result = {constant = 3, var = 3, num = 3,
opline_num = 3, jmp_offset = 3}, extended_value = 0, lineno = 5, opcode = 38 '&', op1_type = 16 '\020', op2_type = 4 '\004', result_type = 8 '\b'}, {
handler = 0x555555b4f852 <ZEND_RETURN_SPEC_CONST_HANDLER>, op1 = {constant = 32, var = 32, num = 32, opline_num = 32, jmp_offset = 32}, op2 = {constant = 0, var = 0, num = 0,
opline_num = 0, jmp_offset = 0}, result = {constant = 0, var = 0, num = 0, opline_num = 0, jmp_offset = 0}, extended_value = 4294967295, lineno = 6, opcode = 62 '>', op1_type = 1 '\001',
op2_type = 8 '\b', result_type = 8 '\b'}, {handler = 0xa0, op1 = {constant = 1443046120, var = 1443046120, num = 1443046120, opline_num = 1443046120, jmp_offset = 1443046120}, op2 = {
constant = 21845, var = 21845, num = 21845, opline_num = 21845, jmp_offset = 21845}, result = {constant = 0, var = 0, num = 0, opline_num = 0, jmp_offset = 0}, extended_value = 0,
lineno = 570, opcode = 0 '\000', op1_type = 0 '\000', op2_type = 0 '\000', result_type = 0 '\000'}}
ZEND_NOP_SPEC_HANDLER:
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NOP_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
ZEND_VM_NEXT_OPCODE();
}
#define ZEND_VM_NEXT_OPCODE() \
ZEND_VM_NEXT_OPCODE_EX(0, 1)
#define ZEND_VM_NEXT_OPCODE_EX(check_exception, skip) \
CHECK_SYMBOL_TABLES() \
if (check_exception) { \
OPLINE = EX(opline) + (skip); \
} else { \
OPLINE = opline + (skip); \
} \
ZEND_VM_CONTINUE()
ZEND_NEW_SPEC_CONST_HANDLER:
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NEW_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval *result;
zend_function *constructor;
zend_class_entry *ce;
zend_execute_data *call;
SAVE_OPLINE(); //EX(opline) = opline
if (IS_CONST == IS_CONST) {
//讀取快取 根據類名查詢zend_class_entry
ce = CACHED_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)));
if (UNEXPECTED(ce == NULL)) {
////第1步:根據類名查詢zend_class_entry
ce = zend_fetch_class_by_name(Z_STR_P(EX_CONSTANT(opline->op1)), EX_CONSTANT(opline->op1) + 1, ZEND_FETCH_CLASS_DEFAULT | ZEND_FETCH_CLASS_EXCEPTION);
if (UNEXPECTED(ce == NULL)) {
ZEND_ASSERT(EG(exception));
HANDLE_EXCEPTION();
}
CACHE_PTR(Z_CACHE_SLOT_P(EX_CONSTANT(opline->op1)), ce);
}
} else if (IS_CONST == IS_UNUSED) {
ce = zend_fetch_class(NULL, opline->op1.num);
if (UNEXPECTED(ce == NULL)) {
ZEND_ASSERT(EG(exception));
HANDLE_EXCEPTION();
}
} else {
ce = Z_CE_P(EX_VAR(opline->op1.var));
}
result = EX_VAR(opline->result.var);
//2.建立&初始化一個這個類的物件 建立&初始化一個這個類的物件
if (UNEXPECTED(object_init_ex(result, ce) != SUCCESS)) { //_object_and_properties_init
HANDLE_EXCEPTION();
}
//第3步:獲取構造方法=== 直接取zend_class_entry.constructor
//get_constructor => zend_std_get_constructor()
//#define Z_OBJ_HT(zval) Z_OBJ(zval)->handlers
//#define Z_OBJ_HT_P(zval_p) Z_OBJ_HT(*(zval_p))
//define Z_OBJ(zval) (zval).value.obj
//#define Z_OBJ_P(zval_p) Z_OBJ(*(zval_p))
constructor = Z_OBJ_HT_P(result)->get_constructor(Z_OBJ_P(result));
if (constructor == NULL) {
if (UNEXPECTED(EG(exception))) {
zval_ptr_dtor(result);
HANDLE_EXCEPTION();
}
/* If there are no arguments, skip over the DO_FCALL opcode. We check if the next
* opcode is DO_FCALL in case EXT instructions are used. */
//如果沒有引數,則跳過DO_FCALL操作碼。如果使用EXT指令,則檢查下一個操作碼是否為DO_FCALL
if (EXPECTED(opline->extended_value == 0 && (opline+1)->opcode == ZEND_DO_FCALL)) {
//此opcode之後還有傳參、呼叫構造方法的操作
//所以如果沒有定義構造方法則直接跳過這些操作
ZEND_VM_NEXT_OPCODE_EX(1, 2);
}
/* Perform a dummy function call */
call = zend_vm_stack_push_call_frame(
ZEND_CALL_FUNCTION, (zend_function *) &zend_pass_function,
opline->extended_value, NULL, NULL);
} else {
if (EXPECTED(constructor->type == ZEND_USER_FUNCTION) && UNEXPECTED(!constructor->op_array.run_time_cache)) {
//新增執行時方法的快取
init_func_run_time_cache(&constructor->op_array);
}
/* We are not handling overloaded classes right now */
// 現在一個頁面中不支援類的重新定義的,只支援方法的重寫,覆蓋定義
//初始化呼叫建構函式的zend_execute_data
call = zend_vm_stack_push_call_frame(
ZEND_CALL_FUNCTION | ZEND_CALL_RELEASE_THIS | ZEND_CALL_CTOR,
constructor,
opline->extended_value,
ce,
Z_OBJ_P(result));
Z_ADDREF_P(result);
}
call->prev_execute_data = EX(call);
EX(call) = call;
ZEND_VM_NEXT_OPCODE();
}
1)zend_fetch_class_by_name:
zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, const zval *key, int fetch_type) /* {{{ */
{
zend_class_entry *ce;
if (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) {
//沒有自動載入
return zend_lookup_class_ex(class_name, key, 0);
//有自動載入,
} else if ((ce = zend_lookup_class_ex(class_name, key, 1)) == NULL) {
if ((fetch_type & ZEND_FETCH_CLASS_SILENT) == 0 && !EG(exception)) {
if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_INTERFACE) {
//介面沒有找到
zend_throw_or_error(fetch_type, NULL, "Interface '%s' not found", ZSTR_VAL(class_name));
} else if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_TRAIT) {
//多繼承的一種方式,其中一種沒有找到
zend_throw_or_error(fetch_type, NULL, "Trait '%s' not found", ZSTR_VAL(class_name));
} else {
//類沒有找到
zend_throw_or_error(fetch_type, NULL, "Class '%s' not found", ZSTR_VAL(class_name));
}
}
return NULL;
}
return ce;
}
2) object_init_ex:物件初始化
ZEND_API int _object_init_ex(zval *arg, zend_class_entry *class_type ZEND_FILE_LINE_DC) /* {{{ */
{
return _object_and_properties_init(arg, class_type, 0 ZEND_FILE_LINE_RELAY_CC);
}
這個函式需要“properties”包含類中宣告的所有屬性,並且所有屬性都是公共的。如果只有一個子集是給定的,或者類有受保護的成員,那麼您需要透過呼叫zend_merge_properties()來分別合併屬性。
//初始化物件的操作
ZEND_API int _object_and_properties_init(zval *arg, zend_class_entry *class_type, HashTable *properties ZEND_FILE_LINE_DC) /* {{{ */
{
//檢查類是否可以例項化
if (UNEXPECTED(class_type->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) {
if (class_type->ce_flags & ZEND_ACC_INTERFACE) {
//介面不能例項化
zend_throw_error(NULL, "Cannot instantiate interface %s", ZSTR_VAL(class_type->name));
} else if (class_type->ce_flags & ZEND_ACC_TRAIT) {
//為實現多繼承定義的 trait開頭的,不能例項化
zend_throw_error(NULL, "Cannot instantiate trait %s", ZSTR_VAL(class_type->name));
} else {
//抽象類不能例項化
zend_throw_error(NULL, "Cannot instantiate abstract class %s", ZSTR_VAL(class_type->name));
}
ZVAL_NULL(arg);
Z_OBJ_P(arg) = NULL;
return FAILURE;
}
if (UNEXPECTED(!(class_type->ce_flags & ZEND_ACC_CONSTANTS_UPDATED))) {
if (UNEXPECTED(zend_update_class_constants(class_type) != SUCCESS)) {
ZVAL_NULL(arg);
Z_OBJ_P(arg) = NULL;
return FAILURE;
}
}
//使用者自定義的類create_object都是NULL
//只有PHP幾個內部的類有這個值,比如exception、error等
if (class_type->create_object == NULL) {
//分配一個物件 分配物件結構:zend_object ******
ZVAL_OBJ(arg, zend_objects_new(class_type));
if (properties) {
//初始化成員屬性****
object_properties_init_ex(Z_OBJ_P(arg), properties);
} else {
object_properties_init(Z_OBJ_P(arg), class_type);
}
} else {
//呼叫自定義的建立object的鉤子函式
ZVAL_OBJ(arg, class_type->create_object(class_type));
}
return SUCCESS;
}
zend_objects_new:
//注意:此操作並沒有將屬性複製到zend_object中:由object_properties_init()完成
ZEND_API zend_object *zend_objects_new(zend_class_entry *ce)
{
//properties_table 是一個變長陣列 存放非靜態屬性的值, 分配zend_object時需要加上非靜態屬性所佔用的記憶體大小
//根據普通非靜態屬性個數確定,如果沒有定義__get()、__set()等魔術方法則佔用記憶體就是: 屬性數*sizeof(zval),
//如果定義了這些魔術方法那麼會多分配一個zval的空間 *******************
//分配zend_object
zend_object *object = emalloc(sizeof(zend_object) + zend_object_properties_size(ce));
zend_object_std_init(object, ce);
//設定物件的操作handler為std_object_handlers
object->handlers = &std_object_handlers;
return object;
}
zend_object_std_init:
ZEND_API void zend_object_std_init(zend_object *object, zend_class_entry *ce)
{
zval *p, *end;
GC_REFCOUNT(object) = 1;
GC_TYPE_INFO(object) = IS_OBJECT;
object->ce = ce;
object->properties = NULL;
//將object編號並插入EG(objects_store).object_buckets陣列
zend_objects_store_put(object); //next
p = object->properties_table;
if (EXPECTED(ce->default_properties_count != 0)) {
end = p + ce->default_properties_count;
//值指向,寫時複製;
do {
ZVAL_UNDEF(p);
p++;
} while (p != end);
}
if (UNEXPECTED(ce->ce_flags & ZEND_ACC_USE_GUARDS)) {
GC_FLAGS(object) |= IS_OBJ_USE_GUARDS;
ZVAL_UNDEF(p);
}
}
3) get_constructor
zend_vm_stack_push_call_frame
ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER(本頁最簡單的程式碼中會跳過這個handler)
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_execute_data *call = EX(call);
zend_function *fbc = call->func;
zend_object *object;
zval *ret;
SAVE_OPLINE();
EX(call) = call->prev_execute_data;
//deprecated棄用, 抽象的
if (UNEXPECTED((fbc->common.fn_flags & (ZEND_ACC_ABSTRACT|ZEND_ACC_DEPRECATED)) != 0)) {
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_ABSTRACT) != 0)) {
//不能呼叫抽象方法
zend_throw_error(NULL, "Cannot call abstract method %s::%s()", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
HANDLE_EXCEPTION();
}
if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_DEPRECATED) != 0)) {
//棄用的方法
zend_error(E_DEPRECATED, "Function %s%s%s() is deprecated",
fbc->common.scope ? ZSTR_VAL(fbc->common.scope->name) : "",
fbc->common.scope ? "::" : "",
ZSTR_VAL(fbc->common.function_name));
if (UNEXPECTED(EG(exception) != NULL)) {
HANDLE_EXCEPTION();
}
}
}
LOAD_OPLINE(); //LOAD_OPLINE() opline = EX(opline)
//使用者定義的類
if (EXPECTED(fbc->type == ZEND_USER_FUNCTION)) {
ret = NULL;
if (0) {
ret = EX_VAR(opline->result.var);
ZVAL_NULL(ret);
}
//把當前的execute_data儲存一下
call->prev_execute_data = execute_data;
//初始化當前方法的execute_data
i_init_func_execute_data(call, &fbc->op_array, ret);
if (EXPECTED(zend_execute_ex == execute_ex)) {
ZEND_VM_ENTER();
} else {
//#define ZEND_ADD_CALL_FLAG_EX(call_info, flag) do { \
// call_info |= ((flag) << ZEND_CALL_INFO_SHIFT); \
//} while (0)
//#define ZEND_ADD_CALL_FLAG(call, flag) do { \
//ZEND_ADD_CALL_FLAG_EX(Z_TYPE_INFO((call)->This), flag); \
//} while (0)
ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP);
zend_execute_ex(call); //(*zend_execute_ex)(zend_execute_data *execute_data);
}
} else if (EXPECTED(fbc->type < ZEND_USER_FUNCTION)) {// 非使用者定義的
zval retval;
call->prev_execute_data = execute_data;
EG(current_execute_data) = call;
/* function has typed arguments */
//#define ZEND_ACC_HAS_TYPE_HINTS 0x10000000 hints提示
if (UNEXPECTED(fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS)
//internal內部 verify驗證 所有引數的型別
&& UNEXPECTED(!zend_verify_internal_arg_types(fbc, call))) {
if (0) {
ZVAL_UNDEF(EX_VAR(opline->result.var));
}
goto fcall_end;
}
ret = 0 ? EX_VAR(opline->result.var) : &retval;
ZVAL_NULL(ret);
if (!zend_execute_internal) {
/* saves one function call if zend_execute_internal is not used */
//如果沒有使用zend_execute_internal,則儲存一個函式呼叫
fbc->internal_function.handler(call, ret);
} else {
zend_execute_internal(call, ret);
}
#if ZEND_DEBUG
if (!EG(exception) && call->func) {
ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) ||
zend_verify_internal_return_type(call->func, ret));
ZEND_ASSERT((call->func->common.fn_flags & ZEND_ACC_RETURN_REFERENCE)
? Z_ISREF_P(ret) : !Z_ISREF_P(ret));
}
#endif
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
if (!0) {
//銷燬
zval_ptr_dtor(ret);
}
} else { /* ZEND_OVERLOADED_FUNCTION */ //過載
zval retval;
ret = 0 ? EX_VAR(opline->result.var) : &retval;
call->prev_execute_data = execute_data;
if (UNEXPECTED(!zend_do_fcall_overloaded(fbc, call, ret))) {
HANDLE_EXCEPTION();
}
if (!0) {
zval_ptr_dtor(ret);
}
}
fcall_end:
if (UNEXPECTED(ZEND_CALL_INFO(call) & ZEND_CALL_RELEASE_THIS)) {
object = Z_OBJ(call->This);
#if 0
if (UNEXPECTED(EG(exception) != NULL) && (opline->op1.num & ZEND_CALL_CTOR)) {
#else
if (UNEXPECTED(EG(exception) != NULL) && (ZEND_CALL_INFO(call) & ZEND_CALL_CTOR)) {
#endif
GC_REFCOUNT(object)--;
//有異常
zend_object_store_ctor_failed(object);
}
OBJ_RELEASE(object);
}
zend_vm_stack_free_call_frame(call);
if (UNEXPECTED(EG(exception) != NULL)) {
//有異常
zend_throw_exception_internal(NULL);
if (0) {
zval_ptr_dtor(EX_VAR(opline->result.var));
}
HANDLE_EXCEPTION();
}
ZEND_VM_SET_OPCODE(opline + 1);
ZEND_VM_CONTINUE();
}
zend_do_fcall_overloaded
static zend_never_inline int zend_do_fcall_overloaded(zend_function *fbc, zend_execute_data *call, zval *ret) /* {{{ */
{
zend_object *object;
/* Not sure what should be done here if it's a static method */
//如果它是一個靜態方法,不知道這裡應該做什麼
if (UNEXPECTED(Z_TYPE(call->This) != IS_OBJECT)) {
//不是物件
zend_vm_stack_free_args(call);
if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
zend_string_release(fbc->common.function_name);
}
efree(fbc);
zend_vm_stack_free_call_frame(call);
zend_throw_error(NULL, "Cannot call overloaded function for non-object");
return 0;
}
object = Z_OBJ(call->This);
ZVAL_NULL(ret);
EG(current_execute_data) = call;
//呼叫方法, 引數:方法,物件,引數,返回值
object->handlers->call_method(fbc->common.function_name, object, call, ret);
EG(current_execute_data) = call->prev_execute_data;
zend_vm_stack_free_args(call);
if (fbc->type == ZEND_OVERLOADED_FUNCTION_TEMPORARY) {
zend_string_release(fbc->common.function_name);
}
efree(fbc);
return 1;
}
ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_SPEC_CV_VAR_RETVAL_UNUSED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_free_op free_op2;
zval *value;
zval *variable_ptr;
SAVE_OPLINE(); //EX(opline) = opline
//zval *ret = EX_VAR(var);
//*should_free = ret;
//return ret;
value = _get_zval_ptr_var(opline->op2.var, execute_data, &free_op2);
//EX_VAR(var);第二個引數,| EX_VAR(n) END_CALL_VAR(execute_data, n)
variable_ptr = _get_zval_ptr_cv_undef_BP_VAR_W(execute_data, opline->op1.var);
//不走
if (IS_CV == IS_VAR && UNEXPECTED(Z_ISERROR_P(variable_ptr))) {
zval_ptr_dtor_nogc(free_op2);
if (UNEXPECTED(0)) {
ZVAL_NULL(EX_VAR(opline->result.var));
}
} else {
//賦值
value = zend_assign_to_variable(variable_ptr, value, IS_VAR);
if (UNEXPECTED(0)) {
ZVAL_COPY(EX_VAR(opline->result.var), value);
}
//zend_assign_to_variable()總是處理op2,從不釋放它
/* zend_assign_to_variable() always takes care of op2, never free it! */
}
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
}
ZEND_RETURN_SPEC_CONST_HANDLER
- step1: 首先根據類名去EG(class_table)中找到具體的類,即zend_class_entry
- step2: 分配zend_object結構,一起分配的還有普通非靜態屬性值的記憶體
- step3: 初始化物件的非靜態屬性,將屬性值從zend_class_entry淺複製到物件中
- step4: 查詢當前類是否定義了建構函式,如果沒有定義則跳過執行建構函式的opcode,否則為呼叫建構函式的執行進行一些準備工作(分配zend_execute_data)
- step5: 例項化完成,返回新例項化的物件(如果返回的物件沒有變數使用則直接釋放掉了)
本作品採用《CC 協議》,轉載必須註明作者和本文連結