<?php
class test
{
public function getName()
{
$this->name='abc';
echo $this->name;
}
}
$a=new test();
$a->getName();
1.$this->name='abc'对应的BNF范式
expr_without_variable:
variable '=' expr { zend_check_writable_variable(&$1); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); }
;
variable:
base_variable_with_function_calls T_OBJECT_OPERATOR { zend_do_push_object(&$1 TSRMLS_CC); }
object_property { zend_do_push_object(&$4 TSRMLS_CC); } method_or_not variable_properties
{ zend_do_pop_object(&$$ TSRMLS_CC); $$.EA = $1.EA | ($7.EA ? $7.EA : $6.EA); }
;
base_variable_with_function_calls:
compound_variable { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); }
;
compound_variable:
T_VARIABLE { $$ = $1; }
;
object_property:
object_dim_list { $$ = $1; }
;
object_dim_list:
variable_name { znode tmp_znode; zend_do_pop_object(&tmp_znode TSRMLS_CC); zend_do_fetch_property(&$$, &tmp_znode, &$1 TSRMLS_CC);}
;
variable_name:
T_STRING { $$ = $1; }
理解版
expr_without_variable:
variable '=' expr { zend_check_writable_variable(&$1); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); }
;
variable:
base_variable T_OBJECT_OPERATOR object_property method_or_not variable_properties
;
base_variable:
T_VARIABLE { fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); }
;
object_property:
T_STRING { zend_do_fetch_property(&$$, &tmp_znode, &$1 TSRMLS_CC);}
;
2. 对于 $this 产生 opcode ZEND_FETCH_W
void fetch_simple_variable_ex(znode *result, znode *varname, int bp, zend_uchar op TSRMLS_DC) /* {{{ */
{
zend_op opline;
zend_op *opline_ptr;
zend_llist *fetch_list_ptr;
if (varname->op_type == IS_CONST) {
ulong hash;
if (Z_TYPE(varname->u.constant) != IS_STRING) {
convert_to_string(&varname->u.constant);
}
hash = str_hash(Z_STRVAL(varname->u.constant), Z_STRLEN(varname->u.constant));
//如果变量名不是this,那么在active_symbole_table中插入一个key
if (!zend_is_auto_global_quick(Z_STRVAL(varname->u.constant), Z_STRLEN(varname->u.constant), hash TSRMLS_CC) &&
!(Z_STRLEN(varname->u.constant) == (sizeof("this")-1) &&
!memcmp(Z_STRVAL(varname->u.constant), "this", sizeof("this") - 1)) &&
(CG(active_op_array)->last == 0 ||
CG(active_op_array)->opcodes[CG(active_op_array)->last-1].opcode != ZEND_BEGIN_SILENCE)) {
result->op_type = IS_CV;
result->u.op.var = lookup_cv(CG(active_op_array), Z_STRVAL(varname->u.constant), Z_STRLEN(varname->u.constant), hash TSRMLS_CC);
Z_STRVAL(varname->u.constant) = (char*)CG(active_op_array)->vars[result->u.op.var].name;
result->EA = 0;
return;
}
}
//如果变量名是$this,则生成新的opcode
opline_ptr = get_next_op(CG(active_op_array) TSRMLS_CC);
opline_ptr->opcode = op;
opline_ptr->result_type = IS_VAR;
opline_ptr->result.var = get_temporary_variable(CG(active_op_array));
SET_NODE(opline_ptr->op1, varname);
GET_NODE(result, opline_ptr->result);
SET_UNUSED(opline_ptr->op2);
opline_ptr->extended_value = ZEND_FETCH_LOCAL;
zend_llist_add_element(fetch_list_ptr, opline_ptr);
}
3.对于 $this->name修改上面的opcode 为 ZEND_FETCH_OBJ_W
void zend_do_fetch_property(znode *result, znode *object, const znode *property TSRMLS_DC) /* {{{ */
{
zend_llist *fetch_list_ptr;
if (fetch_list_ptr->count == 1) {
zend_llist_element *le = fetch_list_ptr->head;
zend_op *opline_ptr = (zend_op *) le->data;
if (opline_is_fetch_this(opline_ptr TSRMLS_CC)) {
zend_del_literal(CG(active_op_array), opline_ptr->op1.constant);
SET_UNUSED(opline_ptr->op1); /* this means $this for objects */
SET_NODE(opline_ptr->op2, property);
/* if it was usual fetch, we change it to object fetch */
switch (opline_ptr->opcode) {
case ZEND_FETCH_W:
opline_ptr->opcode = ZEND_FETCH_OBJ_W;
}
return;
}
}
}
4.对于$this->name='abc' ,执行方法zend_do_assign,设置为zend_ASSIGN_OBJ
void zend_do_assign(znode *result, znode *variable, znode *value TSRMLS_DC) /* {{{ */
{
last_op_number = get_next_op_number(CG(active_op_array));
if (variable->op_type == IS_VAR) {
zend_op *last_op;
last_op = &CG(active_op_array)->opcodes[last_op_number-n-1];
if (last_op->result_type == IS_VAR && last_op->result.var == variable->u.op.var) {
if (last_op->opcode == ZEND_FETCH_OBJ_W) {
last_op->opcode = ZEND_ASSIGN_OBJ;
zend_do_op_data(opline, value TSRMLS_CC);
SET_UNUSED(opline->result);
GET_NODE(result, last_op->result);
return;
}
}
}
}
5.执行ZEND_ASSIGN_OBJ
static int ZEND_FASTCALL ZEND_ASSIGN_OBJ_SPEC_UNUSED_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zval **object_ptr;
zval *property_name;
object_ptr = _get_obj_zval_ptr_ptr_unused(TSRMLS_C);
property_name = opline->op2.zv;
zend_assign_to_object(RETURN_VALUE_USED(opline)?&EX_T(opline->result.var).var.ptr:NULL, object_ptr, property_name, (opline+1)->op1_type, &(opline+1)->op1, execute_data, ZEND_ASSIGN_OBJ, ((IS_CONST == IS_CONST) ? opline->op2.literal : NULL) TSRMLS_CC);
}
static inline void zend_assign_to_object(zval **retval, zval **object_ptr, zval *property_name, int value_type, znode_op *value_op, const zend_execute_data *execute_data, int opcode, const zend_literal *key TSRMLS_DC)
{
zval *object = *object_ptr;
Z_OBJ_HT_P(object)->write_property(object, property_name, value, key TSRMLS_CC);
}
ZEND_API void zend_std_write_property(zval *object, zval *member, zval *value, const zend_literal *key TSRMLS_DC) /* {{{ */
{
zend_object *zobj;
zval *tmp_member = NULL;
zval **variable_ptr;
zend_property_info *property_info;
//define Z_OBJ_P(zval_p)
// ((zend_object*)(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zval_p)].bucket.obj.object))
zobj = Z_OBJ_P(object);
property_info = zend_get_property_info_quick(zobj->ce, member, (zobj->ce->__set != NULL), key TSRMLS_CC);
//执行完$a=new a()后,类a中的成员变量就已经复制到对象中的properties_table中去了,当对象动态增加成员变量时,会放到对象中的properties_table中去
if (EXPECTED(property_info != NULL) &&
((EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0) &&
property_info->offset >= 0) ?
(zobj->properties ?
((variable_ptr = (zval**)zobj->properties_table[property_info->offset]) != NULL) :
(*(variable_ptr = &zobj->properties_table[property_info->offset]) != NULL)) :
(EXPECTED(zobj->properties != NULL) &&
EXPECTED(zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS)))) {
/* if we already have this value there, we don't actually need to do anything */
if (EXPECTED(*variable_ptr != value)) {
/* if we are assigning reference, we shouldn't move it, but instead assign variable
to the same pointer */
if (PZVAL_IS_REF(*variable_ptr)) {
zval garbage = **variable_ptr; /* old value should be destroyed */
/* To check: can't *variable_ptr be some system variable like error_zval here? */
Z_TYPE_PP(variable_ptr) = Z_TYPE_P(value);
(*variable_ptr)->value = value->value;
if (Z_REFCOUNT_P(value) > 0) {
zval_copy_ctor(*variable_ptr);
} else {
efree(value);
}
zval_dtor(&garbage);
} else {
zval *garbage = *variable_ptr;
/* if we assign referenced variable, we should separate it */
Z_ADDREF_P(value);
if (PZVAL_IS_REF(value)) {
SEPARATE_ZVAL(&value);
}
*variable_ptr = value;
zval_ptr_dtor(&garbage);
}
}
}
}
2.$a->getName()对应的BNF范式
variable:
base_variable_with_function_calls T_OBJECT_OPERATOR { }
object_property { } method_or_not variable_properties
{ }
;
object_property:
variable_without_objects { } { znode tmp_znode; zend_do_pop_object(&tmp_znode TSRMLS_CC); zend_do_fetch_property(&$$, &tmp_znode, &$1 TSRMLS_CC);}
;
method_or_not:
method { $$ = $1; $$.EA = ZEND_PARSED_METHOD_CALL; }
| /* empty */ { $$.EA = ZEND_PARSED_MEMBER; }
;
method:
{ zend_do_begin_method_call(&$$ TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 1, 1 TSRMLS_CC); }
;
function_call_parameter_list:
'(' ')' { Z_LVAL($$.u.constant) = 0; }
| '(' non_empty_function_call_parameter_list ')' { $$ = $2; }
;
3.执行
void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC) /* {{{ */
{
zend_op *last_op;
last_op->opcode = ZEND_INIT_METHOD_CALL;
last_op->result_type = IS_UNUSED;
last_op->result.num = CG(context).nested_calls;
}
void zend_do_end_function_call(znode *function_name, znode *result, int is_method, int is_dynamic_fcall TSRMLS_DC) /* {{{ */
{
zend_op *opline;
zend_function_call_entry *fcall;
zend_stack_top(&CG(function_call_stack), (void **) &fcall);
opline = get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_DO_FCALL_BY_NAME;
SET_UNUSED(opline->op1);
SET_UNUSED(opline->op2);
}
static int ZEND_FASTCALL ZEND_INIT_METHOD_CALL_SPEC_CV_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zval *function_name;
char *function_name_strval;
int function_name_strlen;
call_slot *call = EX(call_slots) + opline->result.num;
function_name = opline->op2.zv;
function_name_strval = Z_STRVAL_P(function_name);
function_name_strlen = Z_STRLEN_P(function_name);
call->object = _get_zval_ptr_cv_BP_VAR_R(execute_data, opline->op1.var TSRMLS_CC);
/* First, locate the function. */
call->fbc = Z_OBJ_HT_P(call->object)->get_method(&call->object, function_name_strval, function_name_strlen, ((IS_CONST == IS_CONST) ? (opline->op2.literal + 1) : NULL) TSRMLS_CC);
EX(call) = call;
ZEND_VM_NEXT_OPCODE();
}
#define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data TSRMLS_DC
static int ZEND_FASTCALL ZEND_DO_FCALL_BY_NAME_SPEC_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
EX(function_state).function = EX(call)->fbc;
return zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU);
}
static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_ARGS)
{
USE_OPLINE
zend_bool should_change_scope = 0;
zend_function *fbc = EX(function_state).function;
zend_uint num_args;
EX(object) = EX(call)->object; // 重要
if (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) {
should_change_scope = 1;
EX(current_this) = EG(This);
EX(current_scope) = EG(scope);
EX(current_called_scope) = EG(called_scope);
EG(This) = EX(object); //重要
EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX(object)) ? fbc->common.scope : NULL;
EG(called_scope) = EX(call)->called_scope;
}
if (fbc->type == ZEND_USER_FUNCTION) {
EG(active_symbol_table) = NULL;
EG(active_op_array) = &fbc->op_array;
EG(return_value_ptr_ptr) = NULL;
if (EXPECTED(zend_execute_ex == execute_ex)) {
if (EXPECTED(EG(exception) == NULL)) {
ZEND_VM_ENTER();
}
}
}
}