<?php class A { private function foo() { echo 'A foo()' . PHP_EOL; } public function test() { $this->foo(); } } class C extends A { public function foo() { echo 'C foo()' . PHP_EOL; } } $c = new C(); $c->test();
Finding entry points Branch analysis from position: 0 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: (null) number of ops: 9 compiled vars: !0 = $c line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 2 0 E > NOP 15 1 NOP 2 NOP 23 3 NEW $4 :-4 4 DO_FCALL 0 5 ASSIGN !0, $4 24 6 INIT_METHOD_CALL !0, 'test' 7 DO_FCALL 0 25 8 > RETURN 1 Class A: Function foo: Finding entry points Branch analysis from position: 0 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: foo number of ops: 2 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 6 0 E > ECHO 'A+foo%28%29%0A' 7 1 > RETURN null End of function foo Function test: Finding entry points Branch analysis from position: 0 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: test number of ops: 3 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 11 0 E > INIT_METHOD_CALL 'foo' 1 DO_FCALL 0 12 2 > RETURN null End of function test End of class A. Class C: Function foo: Finding entry points Branch analysis from position: 0 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: foo number of ops: 2 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 19 0 E > ECHO 'C+foo%28%29%0A' 20 1 > RETURN null End of function foo Function test: Finding entry points Branch analysis from position: 0 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: test number of ops: 3 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 11 0 E > INIT_METHOD_CALL 'foo' 1 DO_FCALL 0 12 2 > RETURN null End of function test End of class C.
Finding entry points Branch analysis from position: 0 Add 0 Add 1 Add 2 Add 3 Add 4 Add 5 Add 6 Add 7 Add 8 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: (null) number of ops: 9 compiled vars: !0 = $c line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 2 0 E > NOP 15 1 NOP 2 NOP 23 3 NEW RES[ IS_VAR $4 ] OP1[ :-4 ] 4 DO_FCALL 0 RES[ ] OP1[ IS_UNUSED ] 5 ASSIGN RES[ ] OP1[ IS_CV !0 ] OP2[ , IS_VAR $4 ] 24 6 INIT_METHOD_CALL RES[ IS_UNUSED ] OP1[ IS_CV !0 ] OP2[ , IS_CONST (4) 'test' ] 7 DO_FCALL 0 RES[ ] OP1[ IS_UNUSED ] 25 8 > RETURN OP1[ IS_CONST (6) 1 ] branch: # 0; line: 2- 25; sop: 0; eop: 8; out1: -2 path #1: 0, Class A: Function foo: Finding entry points Branch analysis from position: 0 Add 0 Add 1 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: foo number of ops: 2 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 6 0 E > ECHO OP1[ IS_CONST (0) 'A+foo%28%29%0A' ] 7 1 > RETURN OP1[ IS_CONST (1) null ] branch: # 0; line: 6- 7; sop: 0; eop: 1; out1: -2 path #1: 0, End of function foo Function test: Finding entry points Branch analysis from position: 0 Add 0 Add 1 Add 2 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: test number of ops: 3 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 11 0 E > INIT_METHOD_CALL RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_CONST (0) 'foo' ] 1 DO_FCALL 0 RES[ ] OP1[ IS_UNUSED ] 12 2 > RETURN OP1[ IS_CONST (2) null ] branch: # 0; line: 11- 12; sop: 0; eop: 2; out1: -2 path #1: 0, End of function test End of class A. Class C: Function foo: Finding entry points Branch analysis from position: 0 Add 0 Add 1 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: foo number of ops: 2 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 19 0 E > ECHO OP1[ IS_CONST (0) 'C+foo%28%29%0A' ] 20 1 > RETURN OP1[ IS_CONST (1) null ] branch: # 0; line: 19- 20; sop: 0; eop: 1; out1: -2 path #1: 0, End of function foo Function test: Finding entry points Branch analysis from position: 0 Add 0 Add 1 Add 2 Jump found. (Code = 62) Position 1 = -2 filename: /home/minsec/php_demo/a.php function name: test number of ops: 3 compiled vars: none line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 11 0 E > INIT_METHOD_CALL RES[ IS_UNUSED ] OP1[ IS_UNUSED ] OP2[ IS_CONST (0) 'foo' ] 1 DO_FCALL 0 RES[ ] OP1[ IS_UNUSED ] 12 2 > RETURN OP1[ IS_CONST (2) null ] branch: # 0; line: 11- 12; sop: 0; eop: 2; out1: -2 path #1: 0, End of function test End of class C. A foo()
static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zval *function_name; zval *object; zend_function *fbc; zend_class_entry *called_scope; zend_object *obj; zend_execute_data *call; uint32_t call_info; SAVE_OPLINE(); object = _get_zval_ptr_cv_undef(opline->op1.var EXECUTE_DATA_CC); if (IS_CV == IS_UNUSED && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { ZEND_VM_TAIL_CALL(zend_this_not_in_object_context_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU)); } function_name = EX_CONSTANT(opline->op2); if (IS_CONST != IS_CONST && UNEXPECTED(Z_TYPE_P(function_name) != IS_STRING)) { do { if ((IS_CONST & (IS_VAR|IS_CV)) && Z_ISREF_P(function_name)) { function_name = Z_REFVAL_P(function_name); if (EXPECTED(Z_TYPE_P(function_name) == IS_STRING)) { break; } } else if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(function_name) == IS_UNDEF)) { GET_OP2_UNDEF_CV(function_name, BP_VAR_R); if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } } zend_throw_error(NULL, "Method name must be a string"); HANDLE_EXCEPTION(); } while (0); } if (IS_CV != IS_UNUSED) { do { if (IS_CV == IS_CONST || UNEXPECTED(Z_TYPE_P(object) != IS_OBJECT)) { if ((IS_CV & (IS_VAR|IS_CV)) && EXPECTED(Z_ISREF_P(object))) { object = Z_REFVAL_P(object); if (EXPECTED(Z_TYPE_P(object) == IS_OBJECT)) { break; } } if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(object) == IS_UNDEF)) { object = GET_OP1_UNDEF_CV(object, BP_VAR_R); if (UNEXPECTED(EG(exception) != NULL)) { HANDLE_EXCEPTION(); } } zend_throw_error(NULL, "Call to a member function %s() on %s", Z_STRVAL_P(function_name), zend_get_type_by_const(Z_TYPE_P(object))); HANDLE_EXCEPTION(); } } while (0); } obj = Z_OBJ_P(object); called_scope = obj->ce; if (IS_CONST == IS_CONST && EXPECTED(CACHED_PTR(Z_CACHE_SLOT_P(function_name)) == called_scope)) { fbc = CACHED_PTR(Z_CACHE_SLOT_P(function_name) + sizeof(void*)); } else { zend_object *orig_obj = obj; if (UNEXPECTED(obj->handlers->get_method == NULL)) { zend_throw_error(NULL, "Object does not support method calls"); HANDLE_EXCEPTION(); } /* First, locate the function. */ fbc = obj->handlers->get_method(&obj, Z_STR_P(function_name), ((IS_CONST == IS_CONST) ? (EX_CONSTANT(opline->op2) + 1) : NULL)); if (UNEXPECTED(fbc == NULL)) { if (EXPECTED(!EG(exception))) { zend_throw_error(NULL, "Call to undefined method %s::%s()", ZSTR_VAL(obj->ce->name), Z_STRVAL_P(function_name)); } HANDLE_EXCEPTION(); } if (IS_CONST == IS_CONST && EXPECTED(fbc->type <= ZEND_USER_FUNCTION) && EXPECTED(!(fbc->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE|ZEND_ACC_NEVER_CACHE))) && EXPECTED(obj == orig_obj)) { CACHE_POLYMORPHIC_PTR(Z_CACHE_SLOT_P(function_name), called_scope, fbc); } if (EXPECTED(fbc->type == ZEND_USER_FUNCTION) && UNEXPECTED(!fbc->op_array.run_time_cache)) { init_func_run_time_cache(&fbc->op_array); } } call_info = ZEND_CALL_NESTED_FUNCTION; if (UNEXPECTED((fbc->common.fn_flags & ZEND_ACC_STATIC) != 0)) { obj = NULL; } else if (IS_CV & (IS_VAR|IS_TMP_VAR|IS_CV)) { /* CV may be changed indirectly (e.g. when it's a reference) */ call_info = ZEND_CALL_NESTED_FUNCTION | ZEND_CALL_RELEASE_THIS; GC_REFCOUNT(obj)++; /* For $this pointer */ } if ((IS_CV & (IS_VAR|IS_TMP_VAR)) && UNEXPECTED(EG(exception))) { HANDLE_EXCEPTION(); } call = zend_vm_stack_push_call_frame(call_info, fbc, opline->extended_value, called_scope, obj); call->prev_execute_data = EX(call); EX(call) = call; ZEND_VM_NEXT_OPCODE(); }