Statement: This article is an original article by the author, all of which have been analyzed by the author one by one. I hope you can give me some advice if there is something wrong with it.
Blog address: PhP technical blog will also be updated in csdn.
You are welcome to repost. Please indicate the source for reprinting.
The previous chapter describes the implementation of classes.
This article details the member attributes and methods of the PHP class.
The previous article introduced zend_do_begin_class_declaration, which is used to create and initialize a zend_class_entry
All information of the class is saved in this structure. How are attributes and Methods saved?
class Person{ public $name;}
Do you still remember the zend_initialize_class_data function mentioned in the previous article? It doesn't matter if you don't remember. Let's take a closer look at this function.
Zend_initialize_class_data (new_class_entry, 1 tsrmls_cc); <! -- More -->
ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify_handlers TSRMLS_DC) /* {{{ */{ zend_bool persistent_hashes = (ce->type == ZEND_INTERNAL_CLASS) ? 1 : 0; dtor_func_t zval_ptr_dtor_func = ((persistent_hashes) ? ZVAL_INTERNAL_PTR_DTOR : ZVAL_PTR_DTOR); ce->refcount = 1; ce->constants_updated = 0; ce->ce_flags = 0; ce->doc_comment = NULL; ce->doc_comment_len = 0; zend_hash_init_ex(&ce->default_properties, 0, NULL, zval_ptr_dtor_func, persistent_hashes, 0); zend_hash_init_ex(&ce->properties_info, 0, NULL, (dtor_func_t) (persistent_hashes ? zend_destroy_property_info_internal : zend_destroy_property_info), persistent_hashes, 0); zend_hash_init_ex(&ce->default_static_members, 0, NULL, zval_ptr_dtor_func, persistent_hashes, 0); zend_hash_init_ex(&ce->constants_table, 0, NULL, zval_ptr_dtor_func, persistent_hashes, 0); zend_hash_init_ex(&ce->function_table, 0, NULL, ZEND_FUNCTION_DTOR, persistent_hashes, 0); if (ce->type == ZEND_INTERNAL_CLASS) {#ifdef ZTS int n = zend_hash_num_elements(CG(class_table)); if (CG(static_members) && n >= CG(last_static_member)) { /* Support for run-time declaration: dl() */ CG(last_static_member) = n+1; CG(static_members) = realloc(CG(static_members), (n+1)*sizeof(HashTable*)); CG(static_members)[n] = NULL; } ce->static_members = (HashTable*)(zend_intptr_t)n;#else ce->static_members = NULL;#endif } else { ce->static_members = &ce->default_static_members; } if (nullify_handlers) { ce->constructor = NULL; ce->destructor = NULL; ce->clone = NULL; ce->__get = NULL; ce->__set = NULL; ce->__unset = NULL; ce->__isset = NULL; ce->__call = NULL; ce->__callstatic = NULL; ce->__tostring = NULL; ce->create_object = NULL; ce->get_iterator = NULL; ce->iterator_funcs.funcs = NULL; ce->interface_gets_implemented = NULL; ce->get_static_method = NULL; ce->parent = NULL; ce->num_interfaces = 0; ce->interfaces = NULL; ce->module = NULL; ce->serialize = NULL; ce->unserialize = NULL; ce->serialize_func = NULL; ce->unserialize_func = NULL; ce->builtin_functions = NULL; }}
Zend_bool persistent_hashes = (Ce-> type = zend_internal_class )? 1: 0;
The memory allocation method for common user classes and internal classes is different... why is there a difference ??? I haven't studied it yet... ^. ^
Check Lines 13-16.
Zend_hash_init_ex (& Ce-> default_properties, 0, null, zval_ptr_dtor_func, persistent_hashes, 0 );
Zend_hash_init_ex (& Ce-> default_static_members, 0, null, zval_ptr_dtor_func, persistent_hashes, 0 );
Zend_hash_init_ex (& Ce-> constants_table, 0, null, zval_ptr_dtor_func, persistent_hashes, 0 );
Zend_hash_init_ex (& Ce-> function_table, 0, null, zend_function_dtor, persistent_hashes, 0 );
If you have read the previous article, you must know that it is initializing hashtable.
Yes .. it is,
Default_properties and default_static_members are all hashtable pointers. Therefore, zend_hash_init is required for initialization.
Line 36-61 initialize magic methods
However, this is only initialization. It seems that the attribute. $ name is not set. How is the attribute added to the Attribute Table ???
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, &$2 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, &$2 TSRMLS_CC); };class_statement: variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } class_variable_declaration ';' | class_constant_declaration ';' | 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); };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); };
Remember this?
After the class initialization is successful, you must execute class_statement_list... ^. ^.
The class body calls zend_do_declare_property for processing.
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"); } 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) { if (!(existing_property_info->flags & ZEND_ACC_IMPLICIT_PUBLIC)) { 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), var_name->u.constant.value.str.val, var_name->u.constant.value.str.len, property, access_type, comment, comment_len TSRMLS_CC); efree(var_name->u.constant.value.str.val);}
Line 8-25:
If your class declares an interface, interfaces may not include member variables will be thrown if this interface does not have attributes.
If the attribute of the class is set to abstract, properties cannot be declared abstract will be thrown.
If the class property is set to final, cannot declare property % s: $ % s final, the final modifier is allowed only for methods and classes will be thrown.
If everything is okay, A zval data will be allocated,
If the attribute has an initial value, the data is allocated to zval. If no value exists, init_pzval is called to initialize zval and is_null is set;
Zend_declare_property_ex will be called to add the zval to the specified active_class_entry.
<Strong> method of the class </strong>
[PHP]
Class person {
Public Function Test (){
Echo 1;
}
}
[/PHP]
What if it is a method ?? How is this handled?
First view rules
class_statement: variable_modifiers { CG(access_type) = Z_LVAL($1.u.constant); } class_variable_declaration ';' | class_constant_declaration ';' | 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); }
The first is the attribute, and the third is the method ..
Is zend_do_begin_function_declaration familiar?
If you have read the previous articles, you must be familiar with them.
If you have not read it, first read this article. <a href = "http://imsiren.com/archives/295"> Function Definition </a>
I will not go into detail here.
Let's just talk about the content not mentioned in that article.
There is a judgment in this function
if (is_method) { if (CG(active_class_entry)->ce_flags & ZEND_ACC_INTERFACE) { if ((Z_LVAL(fn_flags_znode->u.constant) & ~(ZEND_ACC_STATIC|ZEND_ACC_PUBLIC))) { zend_error(E_COMPILE_ERROR, "Access type for interface method %s::%s() must be omitted", CG(active_class_entry)->name, function_name->u.constant.value.str.val); } Z_LVAL(fn_flags_znode->u.constant) |= ZEND_ACC_ABSTRACT; /* propagates to the rest of the parser */ } fn_flags = Z_LVAL(fn_flags_znode->u.constant); /* must be done *after* the above check */ } else { fn_flags = 0; }
Obviously, if it is a method, it will be processed.
3-5 rows:
If you set the attribute of the interface class to private or protected, the access type for interface method % s: % s () must be omitted will be thrown.
Then
If (zend_hash_add (& CG (active_class_entry)-> function_table, lcname, name_len + 1, & op_array, sizeof (zend_op_array), (void **) & CG (active_op_array )) = failure ){
Zend_error (e_compile_error, "cannot redeclare % s: % s ()", CG (active_class_entry)-> name, name );
}
Directly add the method to function_table.
Next we will make different judgments based on different class declarations.
Source: Original: Member attributes and methods such as PHP kernel Research