PHP Source Code
<?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();
opcode
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();
}