Abstract: Original: PHP kernel research and other implementations... 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.
ClassPerson {
};
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_policact_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, but 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 {
Chartype;
Char * name; // class name
Zend_uint name_length;
Struct_zend_class_entry * parent; // inherited parent class
Intrefcount; // number of references
Zend_bool constants_updated; // type of the class
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;
Conststruct_zend_function_entry * builtin_functions;
// Are you familiar with it ??? 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 * unserialize_func;
Zend_class_iterator_funcs iterator_funcs;
/* Handlers */
Zend_object_value (* create_object) (zend_class_entry * class_type TSRMLS_DC );
Zend_object_iterator * (* get_iterator) (zend_class_entry * ce, zval * object, intby_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, intmethod_len TSRMLS_DC );
/* Serializer callbacks */
Int (* serialize) (zval * object, unsignedchar ** buffer, zend_uint * buf_len, zend_serialize_data * data TSRMLS_DC );
Int (* unserialize) (zval ** object, zend_class_entry * ce, constunsignedchar * buf, zend_uint buf_len, zend_unserialize_data * data TSRMLS_DC );
Zend_class_entry ** interfaces;
Zend_uint num_interfaces;
Char * filename; // file address of the declaration class
Zend_uint line_start; // class start line
Zend_uint line_end; // end row of the class
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.
Voidzend_do_begin_class_declaration (constznode * class_token, znode * class_name, constznode * parent_class_name TSRMLS_DC )/*{{{*/
{
Zend_op * opline;
Intdoing_inheritance = 0;
Zend_class_entry * new_class_entry;
Char * lcname;
Interror = 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 ){
CaseZEND_FETCH_CLASS_SELF:
Zend_error (E_COMPILE_ERROR, "Cannot use 'self 'as class name as it is reserved ");
Break;
CaseZEND_FETCH_CLASS_PARENT:
Zend_error (E_COMPILE_ERROR, "Cannot use 'parent' as class name as it is reserved ");
Break;
CaseZEND_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...
The above is original: PHP kernel research and other implementation content. For more information, see PHP Chinese network (www.php1.cn )!