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.
These days are busy. I don't have time to write. I have to write more articles over the weekend.
Currently, most languages support classes.
What is a class? Class is object-oriented (OOP). The English name is object oriented programming.
What Is Object-Oriented? Is a programming architecture.
One basic principle of OOP is that a computer program is composed of a single unit or object that can act as a subroutine. Oop achieves three objectives of Software Engineering: reusability, flexibility, and scalability.
This is because we are not talking about the simple description here. If you do not know what a class is or what an object-oriented class is, this article is not suitable for you currently.
[PHP]
Class person {
};
[/PHP]
The above is to create a PHP class. Class is the keyword of PHP. Through it, we can find out how Zend creates a class.
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_entry_type: T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = 0; } | T_ABSTRACT T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } | T_FINAL T_CLASS { $$.u.opline_num = CG(zend_lineno); $$.u.EA.type = ZEND_ACC_FINAL_CLASS; };
T_class, t_abstract t_class, and t_final are three types of PHP patterns.
T_class: a standard class.
T_abstract: declares an abstract class.
T_final: declares a class that cannot be inherited or extended.
There are also interfaces.
They are defined in the Zend/zend_complie.h file.
# Define zend_acc_implicit_abstract_class 0x10 // The statement is not declared as abstract, however, there is an internal abstract method # define zend_acc_explicit_abstract_class 0x20 // abstraction # define zend_acc_final_class 0x40 // final # define zend_acc_interface 0x80 // Interface
These three rules record the current row and set the class type.
The zend_do_begin_class_declaration and zend_do_end_class_declaration methods are called during class definition,
The class keyword, the class name and the inherited parent class are passed as parameters to these two functions.
Zend_do_begin_class_declaration is used to declare the class, set the type, create
Zend_do_end_class_declaration is used to process attributes and methods in the class.
Before talking about two functions, you must first talk about the structure of the stored class zend_class_entry.
It is defined in Zend/Zend. h.
Struct _ zend_class_entry {char type; char * Name; // class name zend_uint name_length; struct _ zend_class_entry * parent; // The inherited parent class int refcount; // reference zend_bool constants_updated; // class type zend_uint ce_flags; // class type abstraction? Interface? Final? Hashtable function_table; // function table hashtable default_properties; // attribute hashtable properties_info; // function access level hashtable default_static_members; // static member hashtable * static_members; // static member, when the declared class is default_static_members, the built-in class is null hashtable constants_table; const struct _ zend_function_entry * builtin_functions; // familiar ??? Right. here is the magic function .. union _ zend_function * constructor; Union _ zend_function * destructor; Union _ zend_function * clone; Union _ zend_function * _ Get; Union _ zend_function * _ set; union _ zend_function * _ unset; Union _ zend_function * _ isset; Union _ zend_function * _ call; Union _ zend_function * _ callstatic; Union _ zend_function * _ tostring; union _ zend_function * serialize_func; Union _ zend_function * Handler; handler;/* handlers */zend_object_value (* create_object) (handler * class_type tsrmls_dc); handler * (* get_iterator) (zend_class_entry * ce, zval * object, int by_ref tsrmls_dc); int (* interface_gets_implemented) (zend_class_entry * iface, zend_class_entry * class_type tsrmls_dc ); /* a class implements this interface */Union _ zend_function * (* get_static_method) (zend_class_entry * ce, char * method, int method_len tsrmls_dc ); /* serializer callbacks */INT (* serialize) (zval * object, unsigned char ** buffer, zend_uint * buf_len, zend_serialize_data * Data tsrmls_dc); int (* unserialize) (zval ** object, zend_class_entry * ce, const unsigned char * Buf, zend_uint buf_len, zend_unserialize_data * Data tsrmls_dc); Parameters ** interfaces; zend_uint num_interfaces; char * filename; // declare the file address of the class zend_uint line_start; // class start line zend_uint line_end; // class end line char * doc_comment; zend_uint doc_comment_len; struct _ zend_module_entry * module ;};
After the structure is clear, let's look at the zend_do_begin_class_declaration function.
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; 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", class_name->u.constant.value.str.val); } /* 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.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 = class_name->u.constant.value.str.val; new_class_entry->name_length = class_name->u.constant.value.str.len; zend_initialize_class_data(new_class_entry, 1 TSRMLS_CC); new_class_entry->filename = zend_get_compiled_filename(TSRMLS_C); new_class_entry->line_start = class_token->u.opline_num; new_class_entry->ce_flags |= class_token->u.EA.type;if (parent_class_name && parent_class_name->op_type != IS_UNUSED) { switch (parent_class_name->u.EA.type) { 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.op_type = IS_CONST; build_runtime_defined_function_key(&opline->op1.u.constant, lcname, new_class_entry->name_length TSRMLS_CC); opline->op2.op_type = IS_CONST; opline->op2.u.constant.type = IS_STRING; Z_SET_REFCOUNT(opline->op2.u.constant, 1); if (doing_inheritance) { opline->extended_value = parent_class_name->u.var; opline->opcode = ZEND_DECLARE_INHERITED_CLASS; } else { opline->opcode = ZEND_DECLARE_CLASS; }opline->op2.u.constant.value.str.val = lcname; opline->op2.u.constant.value.str.len = new_class_entry->name_length; zend_hash_update(CG(class_table), opline->op1.u.constant.value.str.val, opline->op1.u.constant.value.str.len, &new_class_entry, sizeof(zend_class_entry *), NULL); CG(active_class_entry) = new_class_entry; opline->result.u.var = get_temporary_variable(CG(active_op_array)); opline->result.op_type = IS_VAR; CG(implementing_class) = opline->result; if (CG(doc_comment)) { CG(active_class_entry)->doc_comment = CG(doc_comment); CG(active_class_entry)->doc_comment_len = CG(doc_comment_len); CG(doc_comment) = NULL; CG(doc_comment_len) = 0; }}
Lcname = zend_str_tolower_dup (class_name-> U. Constant. value. Str. Val, class_name-> U. Constant. value. Str. Len );
Converts all classes to lowercase. This is why PHP is case insensitive.
If (! (Strcmp (lcname, "self") & strcmp (lcname, "parent "))){
Efree (lcname );
Zend_error (e_compile_error, "cannot use '% s' as class name as it is reserved", class_name-> U. Constant. value. Str. Val );
}
The class name cannot be self or parent.
Row 23-26 is used to check whether the class name is defined repeatedly.
Line 27-37 is used to set the namespace. This is a new feature of php5.3.
Row 39-47 is used to throw an error with repeated definitions.
Row 49-57 initializes the structure of the Storage Class
Zend_initialize_class_data (new_class_entry, 1 tsrmls_cc); function is used to initialize hashtable in the structure, magic method.
This function also has the reason (hashtable * static_members; // static member, when the class declared by the user is default_static_members and the built-in class is null) mentioned above.
Row 88-73 is also used to check whether the parent class name contains the reserved keywords self, parent, static
The rest is used to generate an op,
Internal class: The generated op intermediate code is zend_declare_inherited_class.
User class: the op intermediate code is zend_declare_class.
After this, the. Zend engine will call the zend_execute function to execute the intermediate code zend_declare_class_spec_handler of the op.
It is defined in Zend/zend_vm_execute.h.
This function will execute key code
Ex_t (opline-> result. U. var). class_entry = do_bind_class (opline, eg (class_table), 0 tsrmls_cc );
Do_bind_class will put this class in class_table. Of course, this function will also judge whether the class exists. If it does not exist, an error will be thrown.
Internal Zend error-missing class information for % s
If yes, it will be added successfully.
The class is successfully created here.
The next section will go deep into the class...