1.BNF范式(语法规则)
%token T_PAAMAYIM_NEKUDOTAYIM ":: (T_PAAMAYIM_NEKUDOTAYIM)" %token T_EXTENDS "extends (T_EXTENDS)"
unticked_class_declaration_statement: class_entry_type T_STRING extends_from { zend_do_begin_class_declaration(&$1, &$2, &$3 TSRMLS_CC); } implements_list ‘{‘ class_statement_list ‘}‘ { zend_do_end_class_declaration(&$1, &$3 TSRMLS_CC); } | interface_entry T_STRING { zend_do_begin_class_declaration(&$1, &$2, NULL TSRMLS_CC); } interface_extends_list ‘{‘ class_statement_list ‘}‘ { zend_do_end_class_declaration(&$1, NULL TSRMLS_CC); } ;
class_entry_type:
T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = 0; }
| T_ABSTRACT T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; }
| T_TRAIT { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_TRAIT; }
| T_FINAL T_CLASS { $$.u.op.opline_num = CG(zend_lineno); $$.EA = ZEND_ACC_FINAL_CLASS; }
;
extends_from:
/* empty */ { $$.op_type = IS_UNUSED; }
| T_EXTENDS fully_qualified_class_name { zend_do_fetch_class(&$$, &$2 TSRMLS_CC); }
;
class_statement_list: class_statement_list class_statement | /* empty */ ; class_statement: variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } class_variable_declaration ‘;‘ | class_constant_declaration ‘;‘ | trait_use_statement | method_modifiers function is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$4, 1, $3.op_type, &$1 TSRMLS_CC); } ‘(‘ parameter_list ‘)‘ method_body { zend_do_abstract_method(&$4, &$1, &$9 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); } ; variable_modifiers: non_empty_member_modifiers { $$ = $1; } | T_VAR { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } ;
method_modifiers: /* empty */ { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } | non_empty_member_modifiers { $$ = $1; if (!(Z_LVAL($$.u.constant) & ZEND_ACC_PPP_MASK)) { Z_LVAL($$.u.constant) |= ZEND_ACC_PUBLIC; } } ;
non_empty_member_modifiers:
member_modifier { $$ = $1; }
| non_empty_member_modifiers member_modifier { Z_LVAL($$.u.constant) = zend_do_verify_access_types(&$1, &$2); }
;
member_modifier:
T_PUBLIC { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; }
| T_PROTECTED { Z_LVAL($$.u.constant) = ZEND_ACC_PROTECTED; }
| T_PRIVATE { Z_LVAL($$.u.constant) = ZEND_ACC_PRIVATE; }
| T_STATIC { Z_LVAL($$.u.constant) = ZEND_ACC_STATIC; }
| T_ABSTRACT { Z_LVAL($$.u.constant) = ZEND_ACC_ABSTRACT; }
| T_FINAL { Z_LVAL($$.u.constant) = ZEND_ACC_FINAL; }
;
method_body: ‘;‘ /* abstract method */ { Z_LVAL($$.u.constant) = ZEND_ACC_ABSTRACT; } | ‘{‘ inner_statement_list ‘}‘ { Z_LVAL($$.u.constant) = 0; } ; inner_statement_list: inner_statement_list { zend_do_extended_info(TSRMLS_C); } inner_statement { HANDLE_INTERACTIVE(); } | /* empty */ ; inner_statement: statement | function_declaration_statement | class_declaration_statement | T_HALT_COMPILER ‘(‘ ‘)‘ ‘;‘ { zend_error(E_COMPILE_ERROR, "__HALT_COMPILER() can only be used from the outermost scope"); } ; statement: unticked_statement { DO_TICKS(); } | T_STRING ‘:‘ { zend_do_label(&$1 TSRMLS_CC); } ; unticked_statement: | T_STATIC static_var_list ‘;‘ | expr ‘;‘ { zend_do_free(&$1 TSRMLS_CC); } ; expr: r_variable { $$ = $1; } ; r_variable: variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; } ; 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 { $$ = $1; } ; base_variable_with_function_calls: base_variable { $$ = $1; } | array_function_dereference { $$ = $1; } | function_call { zend_do_begin_variable_parse(TSRMLS_C); $$ = $1; $$.EA = ZEND_PARSED_FUNCTION_CALL; } ; base_variable: reference_variable { $$ = $1; $$.EA = ZEND_PARSED_VARIABLE; } | simple_indirect_reference reference_variable { zend_do_indirect_references(&$$, &$1, &$2 TSRMLS_CC); $$.EA = ZEND_PARSED_VARIABLE; } | static_member { $$ = $1; $$.EA = ZEND_PARSED_STATIC_MEMBER; } ; //类名::$静态成员变量名字 static_member: class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { $$ = $3; zend_do_fetch_static_member(&$$, &$1 TSRMLS_CC); } | variable_class_name T_PAAMAYIM_NEKUDOTAYIM variable_without_objects { $$ = $3; zend_do_fetch_static_member(&$$, &$1 TSRMLS_CC); } ; static_var_list: static_var_list ‘,‘ T_VARIABLE { zend_do_fetch_static_variable(&$3, NULL, ZEND_FETCH_STATIC TSRMLS_CC); } | static_var_list ‘,‘ T_VARIABLE ‘=‘ static_scalar { zend_do_fetch_static_variable(&$3, &$5, ZEND_FETCH_STATIC TSRMLS_CC); } | T_VARIABLE { zend_do_fetch_static_variable(&$1, NULL, ZEND_FETCH_STATIC TSRMLS_CC); } | T_VARIABLE ‘=‘ static_scalar { zend_do_fetch_static_variable(&$1, &$3, ZEND_FETCH_STATIC TSRMLS_CC); } ; function_declaration_statement: unticked_function_declaration_statement { DO_TICKS(); } ; class_declaration_statement: unticked_class_declaration_statement { DO_TICKS(); } ; non_empty_member_modifiers: member_modifier { $$ = $1; } | non_empty_member_modifiers member_modifier { Z_LVAL($$.u.constant) = zend_do_verify_access_types(&$1, &$2); } ; member_modifier: T_PUBLIC { Z_LVAL($$.u.constant) = ZEND_ACC_PUBLIC; } | T_PROTECTED { Z_LVAL($$.u.constant) = ZEND_ACC_PROTECTED; } | T_PRIVATE { Z_LVAL($$.u.constant) = ZEND_ACC_PRIVATE; } | T_STATIC { Z_LVAL($$.u.constant) = ZEND_ACC_STATIC; } | T_ABSTRACT { Z_LVAL($$.u.constant) = ZEND_ACC_ABSTRACT; } | T_FINAL { Z_LVAL($$.u.constant) = ZEND_ACC_FINAL; } ; class_variable_declaration: class_variable_declaration ‘,‘ T_VARIABLE { zend_do_declare_property(&$3, NULL, CG(access_type) TSRMLS_CC); } | class_variable_declaration ‘,‘ T_VARIABLE ‘=‘ static_scalar { zend_do_declare_property(&$3, &$5, CG(access_type) TSRMLS_CC); } | T_VARIABLE { zend_do_declare_property(&$1, NULL, CG(access_type) TSRMLS_CC); } | T_VARIABLE ‘=‘ static_scalar { zend_do_declare_property(&$1, &$3, CG(access_type) TSRMLS_CC); } ; static_scalar: /* compile-time evaluated scalars */ common_scalar { $$ = $1; } | namespace_name { zend_do_fetch_constant(&$$, NULL, &$1, ZEND_CT, 1 TSRMLS_CC); } | T_NAMESPACE T_NS_SEPARATOR namespace_name { $$.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$$.u.constant); zend_do_build_namespace_name(&$$, &$$, &$3 TSRMLS_CC); $3 = $$; zend_do_fetch_constant(&$$, NULL, &$3, ZEND_CT, 0 TSRMLS_CC); } | T_NS_SEPARATOR namespace_name { char *tmp = estrndup(Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); memcpy(&(tmp[1]), Z_STRVAL($2.u.constant), Z_STRLEN($2.u.constant)+1); tmp[0] = ‘\\‘; efree(Z_STRVAL($2.u.constant)); Z_STRVAL($2.u.constant) = tmp; ++Z_STRLEN($2.u.constant); zend_do_fetch_constant(&$$, NULL, &$2, ZEND_CT, 0 TSRMLS_CC); } | ‘+‘ static_scalar { ZVAL_LONG(&$1.u.constant, 0); add_function(&$2.u.constant, &$1.u.constant, &$2.u.constant TSRMLS_CC); $$ = $2; } | ‘-‘ static_scalar { ZVAL_LONG(&$1.u.constant, 0); sub_function(&$2.u.constant, &$1.u.constant, &$2.u.constant TSRMLS_CC); $$ = $2; } | T_ARRAY ‘(‘ static_array_pair_list ‘)‘ { $$ = $3; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; } | ‘[‘ static_array_pair_list ‘]‘ { $$ = $2; Z_TYPE($$.u.constant) = IS_CONSTANT_ARRAY; } | static_class_constant { $$ = $1; } | T_CLASS_C { $$ = $1; } ; common_scalar: T_LNUMBER { $$ = $1; } | T_DNUMBER { $$ = $1; } | T_CONSTANT_ENCAPSED_STRING { $$ = $1; } | T_LINE { $$ = $1; } | T_FILE { $$ = $1; } | T_DIR { $$ = $1; } | T_TRAIT_C { $$ = $1; } | T_METHOD_C { $$ = $1; } | T_FUNC_C { $$ = $1; } | T_NS_C { $$ = $1; } | T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC { $$ = $2; CG(heredoc) = Z_STRVAL($1.u.constant); CG(heredoc_len) = Z_STRLEN($1.u.constant); } | T_START_HEREDOC T_END_HEREDOC { ZVAL_EMPTY_STRING(&$$.u.constant); INIT_PZVAL(&$$.u.constant); $$.op_type = IS_CONST; CG(heredoc) = Z_STRVAL($1.u.constant); CG(heredoc_len) = Z_STRLEN($1.u.constant); } ;
void zend_do_begin_class_declaration(const znode *class_token, znode *class_name, const znode *parent_class_name TSRMLS_DC) /* {{{ */ { zend_op *opline; int doing_inheritance = 0; zend_class_entry *new_class_entry; char *lcname; int error = 0; zval **ns_name, key; if (CG(active_class_entry)) { zend_error(E_COMPILE_ERROR, "Class declarations may not be nested"); return; } lcname = zend_str_tolower_dup(class_name->u.constant.value.str.val, class_name->u.constant.value.str.len); if (!(strcmp(lcname, "self") && strcmp(lcname, "parent"))) { efree(lcname); zend_error(E_COMPILE_ERROR, "Cannot use ‘%s‘ as class name as it is reserved", Z_STRVAL(class_name->u.constant)); } /* Class name must not conflict with import names */ if (CG(current_import) && zend_hash_find(CG(current_import), lcname, Z_STRLEN(class_name->u.constant)+1, (void**)&ns_name) == SUCCESS) { error = 1; } if (CG(current_namespace)) { /* Prefix class name with name of current namespace */ znode tmp; tmp.op_type = IS_CONST; tmp.u.constant = *CG(current_namespace); zval_copy_ctor(&tmp.u.constant); zend_do_build_namespace_name(&tmp, &tmp, class_name TSRMLS_CC); *class_name = tmp; efree(lcname); lcname = zend_str_tolower_dup(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant)); } if (error) { char *tmp = zend_str_tolower_dup(Z_STRVAL_PP(ns_name), Z_STRLEN_PP(ns_name)); if (Z_STRLEN_PP(ns_name) != Z_STRLEN(class_name->u.constant) || memcmp(tmp, lcname, Z_STRLEN(class_name->u.constant))) { zend_error(E_COMPILE_ERROR, "Cannot declare class %s because the name is already in use", Z_STRVAL(class_name->u.constant)); } efree(tmp); } new_class_entry = emalloc(sizeof(zend_class_entry)); new_class_entry->type = ZEND_USER_CLASS; new_class_entry->name = zend_new_interned_string(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant) + 1, 1 TSRMLS_CC); new_class_entry->name_length = Z_STRLEN(class_name->u.constant); zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC); new_class_entry->info.user.filename = zend_get_compiled_filename(TSRMLS_C); new_class_entry->info.user.line_start = class_token->u.op.opline_num; new_class_entry->ce_flags |= class_token->EA; if (parent_class_name && parent_class_name->op_type != IS_UNUSED) { switch (parent_class_name->EA) { case ZEND_FETCH_CLASS_SELF: zend_error(E_COMPILE_ERROR, "Cannot use ‘self‘ as class name as it is reserved"); break; case ZEND_FETCH_CLASS_PARENT: zend_error(E_COMPILE_ERROR, "Cannot use ‘parent‘ as class name as it is reserved"); break; case ZEND_FETCH_CLASS_STATIC: zend_error(E_COMPILE_ERROR, "Cannot use ‘static‘ as class name as it is reserved"); break; default: break; } doing_inheritance = 1; } opline = get_next_op(CG(active_op_array) TSRMLS_CC); opline->op1_type = IS_CONST; build_runtime_defined_function_key(&key, lcname, new_class_entry->name_length TSRMLS_CC); opline->op1.constant = zend_add_literal(CG(active_op_array), &key TSRMLS_CC); Z_HASH_P(&CONSTANT(opline->op1.constant)) = zend_hash_func(Z_STRVAL(CONSTANT(opline->op1.constant)), Z_STRLEN(CONSTANT(opline->op1.constant))); opline->op2_type = IS_CONST; if (doing_inheritance) { /* Make sure a trait does not try to extend a class */ if ((new_class_entry->ce_flags & ZEND_ACC_TRAIT) == ZEND_ACC_TRAIT) { zend_error(E_COMPILE_ERROR, "A trait (%s) cannot extend a class. Traits can only be composed from other traits with the ‘use‘ keyword. Error", new_class_entry->name); } opline->extended_value = parent_class_name->u.op.var; opline->opcode = ZEND_DECLARE_INHERITED_CLASS; } else { opline->opcode = ZEND_DECLARE_CLASS; } LITERAL_STRINGL(opline->op2, lcname, new_class_entry->name_length, 0); CALCULATE_LITERAL_HASH(opline->op2.constant); zend_hash_quick_update(CG(class_table), Z_STRVAL(key), Z_STRLEN(key), Z_HASH_P(&CONSTANT(opline->op1.constant)), &new_class_entry, sizeof(zend_class_entry *), NULL); CG(active_class_entry) = new_class_entry; opline->result.var = get_temporary_variable(CG(active_op_array)); opline->result_type = IS_VAR; GET_NODE(&CG(implementing_class), opline->result); if (CG(doc_comment)) { CG(active_class_entry)->info.user.doc_comment = CG(doc_comment); CG(active_class_entry)->info.user.doc_comment_len = CG(doc_comment_len); CG(doc_comment) = NULL; CG(doc_comment_len) = 0; } }
2.定义变量的编译
void zend_do_declare_property(const znode *var_name, const znode *value, zend_uint access_type TSRMLS_DC) /* {{{ */ { zval *property; zend_property_info *existing_property_info; char *comment = NULL; int comment_len = 0; //接口不能包含变量 if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { zend_error(E_COMPILE_ERROR, "Interfaces may not include member variables"); } //属性不能定义为抽象 if (access_type & ZEND_ACC_ABSTRACT) { zend_error(E_COMPILE_ERROR, "Properties cannot be declared abstract"); } //变量前面的修饰符不能为final if (access_type & ZEND_ACC_FINAL) { zend_error(E_COMPILE_ERROR, "Cannot declare property %s::$%s final, the final modifier is allowed only for methods and classes", CG(active_class_entry)->name, var_name->u.constant.value.str.val); } //不能多次定义同一变量 if (zend_hash_find(&CG(active_class_entry)->properties_info, var_name->u.constant.value.str.val, var_name->u.constant.value.str.len+1, (void **) &existing_property_info)==SUCCESS) { zend_error(E_COMPILE_ERROR, "Cannot redeclare %s::$%s", CG(active_class_entry)->name, var_name->u.constant.value.str.val); } ALLOC_ZVAL(property); if (value) { *property = value->u.constant; } else { INIT_PZVAL(property); Z_TYPE_P(property) = IS_NULL; } if (CG(doc_comment)) { comment = CG(doc_comment); comment_len = CG(doc_comment_len); CG(doc_comment) = NULL; CG(doc_comment_len) = 0; } zend_declare_property_ex(CG(active_class_entry), zend_new_interned_string(var_name->u.constant.value.str.val, var_name->u.constant.value.str.len + 1, 0 TSRMLS_CC), var_name->u.constant.value.str.len, property, access_type, comment, comment_len TSRMLS_CC); efree(var_name->u.constant.value.str.val); }
ZEND_API int zend_declare_property_ex(zend_class_entry *ce, const char *name, int name_length, zval *property, int access_type, const char *doc_comment, int doc_comment_len TSRMLS_DC) /* {{{ */ { zend_property_info property_info, *property_info_ptr; const char *interned_name; ulong h = zend_get_hash_value(name, name_length+1); if (!(access_type & ZEND_ACC_PPP_MASK)) { access_type |= ZEND_ACC_PUBLIC; } if (access_type & ZEND_ACC_STATIC) { //修饰符为static的,例如 public static $xxx if (zend_hash_quick_find(&ce->properties_info, name, name_length + 1, h, (void**)&property_info_ptr) == SUCCESS && (property_info_ptr->flags & ZEND_ACC_STATIC) != 0) { property_info.offset = property_info_ptr->offset; zval_ptr_dtor(&ce->default_static_members_table[property_info.offset]); zend_hash_quick_del(&ce->properties_info, name, name_length + 1, h); } else { property_info.offset = ce->default_static_members_count++; ce->default_static_members_table = perealloc(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count, ce->type == ZEND_INTERNAL_CLASS); } ce->default_static_members_table[property_info.offset] = property; if (ce->type == ZEND_USER_CLASS) { ce->static_members_table = ce->default_static_members_table; } } else { //这里应该是public $xxx这样的变量 if (zend_hash_quick_find(&ce->properties_info, name, name_length + 1, h, (void**)&property_info_ptr) == SUCCESS && (property_info_ptr->flags & ZEND_ACC_STATIC) == 0) { property_info.offset = property_info_ptr->offset; zval_ptr_dtor(&ce->default_properties_table[property_info.offset]); zend_hash_quick_del(&ce->properties_info, name, name_length + 1, h); } else { property_info.offset = ce->default_properties_count++; ce->default_properties_table = perealloc(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count, ce->type == ZEND_INTERNAL_CLASS); } ce->default_properties_table[property_info.offset] = property; } if (ce->type & ZEND_INTERNAL_CLASS) { switch(Z_TYPE_P(property)) { case IS_ARRAY: case IS_CONSTANT_ARRAY: case IS_OBJECT: case IS_RESOURCE: zend_error(E_CORE_ERROR, "Internal zval‘s can‘t be arrays, objects or resources"); break; default: break; } } switch (access_type & ZEND_ACC_PPP_MASK) { case ZEND_ACC_PRIVATE: { char *priv_name; int priv_name_length; zend_mangle_property_name(&priv_name, &priv_name_length, ce->name, ce->name_length, name, name_length, ce->type & ZEND_INTERNAL_CLASS); property_info.name = priv_name; property_info.name_length = priv_name_length; } break; case ZEND_ACC_PROTECTED: { char *prot_name; int prot_name_length; zend_mangle_property_name(&prot_name, &prot_name_length, "*", 1, name, name_length, ce->type & ZEND_INTERNAL_CLASS); property_info.name = prot_name; property_info.name_length = prot_name_length; } break; case ZEND_ACC_PUBLIC: if (IS_INTERNED(name)) { property_info.name = (char*)name; } else { property_info.name = ce->type & ZEND_INTERNAL_CLASS ? zend_strndup(name, name_length) : estrndup(name, name_length); } property_info.name_length = name_length; break; } interned_name = zend_new_interned_string(property_info.name, property_info.name_length+1, 0 TSRMLS_CC); if (interned_name != property_info.name) { if (ce->type == ZEND_USER_CLASS) { efree((char*)property_info.name); } else { free((char*)property_info.name); } property_info.name = interned_name; } property_info.flags = access_type; property_info.h = (access_type & ZEND_ACC_PUBLIC) ? h : zend_get_hash_value(property_info.name, property_info.name_length+1); property_info.doc_comment = doc_comment; property_info.doc_comment_len = doc_comment_len; property_info.ce = ce; zend_hash_quick_update(&ce->properties_info, name, name_length+1, h, &property_info, sizeof(zend_property_info), NULL); return SUCCESS; }
类名::$静态成员名字
void zend_do_fetch_static_member(znode *result, znode *class_name TSRMLS_DC) /* {{{ */ { znode class_node; zend_llist *fetch_list_ptr; zend_llist_element *le; zend_op *opline_ptr; zend_op opline; if (class_name->op_type == IS_CONST && ZEND_FETCH_CLASS_DEFAULT == zend_get_class_fetch_type(Z_STRVAL(class_name->u.constant), Z_STRLEN(class_name->u.constant))) { zend_resolve_class_name(class_name, ZEND_FETCH_CLASS_GLOBAL, 1 TSRMLS_CC); class_node = *class_name; } else { zend_do_fetch_class(&class_node, class_name TSRMLS_CC); } zend_stack_top(&CG(bp_stack), (void **) &fetch_list_ptr); if (result->op_type == IS_CV) { init_op(&opline TSRMLS_CC); opline.opcode = ZEND_FETCH_W; opline.result_type = IS_VAR; opline.result.var = get_temporary_variable(CG(active_op_array)); opline.op1_type = IS_CONST; LITERAL_STRINGL(opline.op1, estrdup(CG(active_op_array)->vars[result->u.op.var].name), CG(active_op_array)->vars[result->u.op.var].name_len, 0); CALCULATE_LITERAL_HASH(opline.op1.constant); GET_POLYMORPHIC_CACHE_SLOT(opline.op1.constant); if (class_node.op_type == IS_CONST) { opline.op2_type = IS_CONST; opline.op2.constant = zend_add_class_name_literal(CG(active_op_array), &class_node.u.constant TSRMLS_CC); } else { SET_NODE(opline.op2, &class_node); } GET_NODE(result,opline.result); opline.extended_value |= ZEND_FETCH_STATIC_MEMBER; opline_ptr = &opline; zend_llist_add_element(fetch_list_ptr, &opline); } else { le = fetch_list_ptr->head; opline_ptr = (zend_op *)le->data; if (opline_ptr->opcode != ZEND_FETCH_W && opline_ptr->op1_type == IS_CV) { init_op(&opline TSRMLS_CC); opline.opcode = ZEND_FETCH_W; opline.result_type = IS_VAR; opline.result.var = get_temporary_variable(CG(active_op_array)); opline.op1_type = IS_CONST; LITERAL_STRINGL(opline.op1, estrdup(CG(active_op_array)->vars[opline_ptr->op1.var].name), CG(active_op_array)->vars[opline_ptr->op1.var].name_len, 0); CALCULATE_LITERAL_HASH(opline.op1.constant); GET_POLYMORPHIC_CACHE_SLOT(opline.op1.constant); if (class_node.op_type == IS_CONST) { opline.op2_type = IS_CONST; opline.op2.constant = zend_add_class_name_literal(CG(active_op_array), &class_node.u.constant TSRMLS_CC); } else { SET_NODE(opline.op2, &class_node); } opline.extended_value |= ZEND_FETCH_STATIC_MEMBER; COPY_NODE(opline_ptr->op1, opline.result); zend_llist_prepend_element(fetch_list_ptr, &opline); } else { if (opline_ptr->op1_type == IS_CONST) { GET_POLYMORPHIC_CACHE_SLOT(opline_ptr->op1.constant); } if (class_node.op_type == IS_CONST) { opline_ptr->op2_type = IS_CONST; opline_ptr->op2.constant = zend_add_class_name_literal(CG(active_op_array), &class_node.u.constant TSRMLS_CC); } else { SET_NODE(opline_ptr->op2, &class_node); } opline_ptr->extended_value |= ZEND_FETCH_STATIC_MEMBER; } } }
static int ZEND_FASTCALL zend_fetch_var_address_helper_SPEC_CONST_CONST(int type, ZEND_OPCODE_HANDLER_ARGS) { USE_OPLINE zend_free_op free_op1; zval *varname; zval **retval; zval tmp_varname; HashTable *target_symbol_table; ulong hash_value; SAVE_OPLINE(); varname = opline->op1.zv; if (IS_CONST != IS_UNUSED) { zend_class_entry *ce; if (IS_CONST == IS_CONST) { if (CACHED_PTR(opline->op2.literal->cache_slot)) { ce = CACHED_PTR(opline->op2.literal->cache_slot); } else { ce = zend_fetch_class_by_name(Z_STRVAL_P(opline->op2.zv), Z_STRLEN_P(opline->op2.zv), opline->op2.literal + 1, 0 TSRMLS_CC); if (UNEXPECTED(ce == NULL)) { if (IS_CONST != IS_CONST && varname == &tmp_varname) { zval_dtor(&tmp_varname); } CHECK_EXCEPTION(); ZEND_VM_NEXT_OPCODE(); } CACHE_PTR(opline->op2.literal->cache_slot, ce); } } else { ce = EX_T(opline->op2.var).class_entry; } retval = zend_std_get_static_property(ce, Z_STRVAL_P(varname), Z_STRLEN_P(varname), 0, ((IS_CONST == IS_CONST) ? opline->op1.literal : NULL) TSRMLS_CC); } else { //代码省略 } zend_class_entry *zend_fetch_class_by_name(const char *class_name, uint class_name_len, const zend_literal *key, int fetch_type TSRMLS_DC) /* {{{ */ { zend_class_entry **pce; int use_autoload = (fetch_type & ZEND_FETCH_CLASS_NO_AUTOLOAD) == 0; if (zend_lookup_class_ex(class_name, class_name_len, key, use_autoload, &pce TSRMLS_CC) == FAILURE) { if (use_autoload) { if ((fetch_type & ZEND_FETCH_CLASS_SILENT) == 0 && !EG(exception)) { if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_INTERFACE) { zend_error(E_ERROR, "Interface ‘%s‘ not found", class_name); } else if ((fetch_type & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_TRAIT) { zend_error(E_ERROR, "Trait ‘%s‘ not found", class_name); } else { zend_error(E_ERROR, "Class ‘%s‘ not found", class_name); } } } return NULL; } return *pce; } ZEND_API zval **zend_std_get_static_property(zend_class_entry *ce, const char *property_name, int property_name_len, zend_bool silent, const zend_literal *key TSRMLS_DC) /* {{{ */ { zend_property_info *property_info; ulong hash_value; if (UNEXPECTED(!key) || (property_info = CACHED_POLYMORPHIC_PTR(key->cache_slot, ce)) == NULL) { if (EXPECTED(key != NULL)) { hash_value = key->hash_value; } else { hash_value = zend_hash_func(property_name, property_name_len+1); } if (UNEXPECTED(zend_hash_quick_find(&ce->properties_info, property_name, property_name_len+1, hash_value, (void **) &property_info)==FAILURE)) { if (!silent) { zend_error_noreturn(E_ERROR, "Access to undeclared static property: %s::$%s", ce->name, property_name); } return NULL; } #if DEBUG_OBJECT_HANDLERS zend_printf("Access type for %s::%s is %s\n", ce->name, property_name, zend_visibility_string(property_info->flags)); #endif if (UNEXPECTED(!zend_verify_property_access(property_info, ce TSRMLS_CC))) { if (!silent) { zend_error_noreturn(E_ERROR, "Cannot access %s property %s::$%s", zend_visibility_string(property_info->flags), ce->name, property_name); } return NULL; } if (UNEXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0)) { if (!silent) { zend_error_noreturn(E_ERROR, "Access to undeclared static property: %s::$%s", ce->name, property_name); } return NULL; } zend_update_class_constants(ce TSRMLS_CC); //不知道为什么这里update? if (EXPECTED(key != NULL)) { CACHE_POLYMORPHIC_PTR(key->cache_slot, ce, property_info); } } return &CE_STATIC_MEMBERS(ce)[property_info->offset]; }