Home >Backend Development >PHP Tutorial >In-depth understanding of the member variables and methods of the PHP kernel (14) class, in-depth understanding of the kernel_PHP tutorial
Original link: http://www.orlion.ga/1237/
The member variables of a class are essentially variables in PHP, but these variables all belong to a certain class, and there is access control for these variables.
The member method of a class is essentially a function in PHP, but this function exists as a class method. It may be a class method or an instance method, and class access control is added to these methods. The member methods of a class are abstractions of the behavior of real-world entities and can be used to implement the behavior of the class.
1. Member variables
Member variables have been registered in the class structure at compile time. The zend_do_begin_class_declaration function is called when the class declaration is compiled at compile time. This function is used to initialize the basic information of the class, including the member variables of the class. The calling sequence is [zend_do_begin_class_declaration]–>[zend_initialize_class_data]–>[zend_hash_init_ex]
zend_hash_init_ex(&ce->default_properties, 0, NULL, zval_ptr_dtor_func, persistent_hashes, 0);
Because the member variables of the class are stored in HashTable, the data is initialized using the zend_hash_init_ex function.
When declaring a class, the HashTable where the member variables of the class are located is initialized. If there is a new member variable attribute declaration later, zend_do_declare_property will be used at compile time. The function first checks some conditions that are not allowed in member variables:
Member variables are not allowed in interfaces
Member variables cannot have abstract properties
Cannot declare member variables as final
Attributes cannot be declared repeatedly
If a property is declared final in a class:
public final $var
Will report an error: Fatal error: Cannot declare property...This error is thrown by the zend_do_declare_property function:
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); }
After the definition check is correct, the function will initialize the member variables.
ALLOC_ZVAL(property); // ¾ĘŴ if (value) { // ÒʻĻļUɩ ȐďĤ *property = value->u.constant; } else { INIT_PZVAL(property); Z_TYPE_P(property) = IS_NULL; }
During the initialization process, the program will first allocate memory. If this member variable has initialized data, the data will be assigned directly to the attribute. Otherwise, ZVAL will be initialized and its type will be set to IS_NULL. After the initialization process is completed, the program adds this member variable to the specified class structure by calling the zend_declare_property_ex function.
Regular member variables will eventually be registered in the default_propertiles field of the class. In our daily work, we may not use the processes mentioned above, but we may use the get_class_vars() function to view the member variables of the class. This function returns an associative array consisting of the default properties of the class. The elements of this array exist in the form of varname=>value. The core code for its implementation is as follows:
if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC) == FAILURE) { RETURN_FALSE; } else { array_init(return_value); zend_update_class_constants(*pce TSRMLS_CC); add_class_vars(*pce, &(*pce)->default_properties, return_value TSRMLS_CC); add_class_vars(*pce, CE_STATIC_MEMBERS(*pce), return_value TSRMLS_CC); }
First call the zend_lookup_class function to find the class named class_name and copy it to the pce variable. The core of this search process is a HashTable search function zend_hash_quick_find, which will search for EG (class_table). Determine whether the class exists and return it directly if it exists. If it does not exist, you need to determine whether it can be automatically loaded. If it can be automatically loaded, it will load the class and then return. If the class cannot be found, returns FALSE. If the class is found, initialize the returned array, update the class's static member variables, and add the class's member variables to the returned array. There is an update process for the static member variables of the class. We will introduce this process in the section about static variables below.
2. Static member variables
Static member variables of a class are common to all instances. They belong to this class, so they are also called class variables. In the PHP class structure, the static variables of the class itself exist in the default_static_memebers field of the class structure.
Unlike ordinary member variables, class variables can be called directly through the class name, which also reflects the specialness of being called a class variable. A PHP example:
class Tipi { public static $var = 10; } Tipi::$var;
View the intermediate code generated through the VLD extension:
function name: (null) number of ops: 6 compiled vars: !0 = $var line # * op fetch ext return operands ------------------------------------------------------------------------------- - - 2 0 > EXT_STMT 1 NOP 6 2 EXT_STMT 3 ZEND_FETCH_CLASS :1 'Tipi' 4 FETCH_R static member 'var' 5 > RETURN 1 branch: # 0; line: 2- 6; sop: 0; eop: 5 path #1: 0, Class Tipi: [no user functions]
This intermediate code only corresponds to the Tipi::$var call. It has little to do with the previous class definition. According to the content generated by VLD, we can know the PHP code: Tipi::$var, and the generated intermediate codes include ZEND_FETCH_CLASS and FETCH_R. This is just a call to a static variable, but it generates two intermediate codes. Reason: We want to call the static variables of a class. Of course, we must first find the class and then obtain the variables of this class. Judging from the PHP source code, this is because the zend_do_fetch_static_member function is called during compilation, and the zend_do_fetch_class function is called in this function, thus generating the ZEND_FETCH_CLASS intermediate code. Its corresponding execution function is ZEND_FETCH_CLASS_SPEC_CONST_HANDLER. This function calls the zend_fetch_class function (Zend/zend_execute_API.c). The zend_fetch_class function will eventually call the zend_lookup_class_ex function to find the class.
找到了类接着应该就是查找类的静态成员变量,其最终调用的函数为:zend_std_get_static_property。这里由于第二个参数的类型为ZEND_FETCH_STATIC_MEMBER。这个函数最后是从static_members字段中查找对应的值返回。而在查找前会和前面一样,执行zend_update_class_constant函数,从而更新此类的所有静态成员变量,静态变量更新流程图:
三、成员方法
成员方法从本质上来将也是一种函数,所以其存储结构也和常规函数一样,存储在zend_function结构体中。对于一个类的多个成员方法,它是以HashTable的数据结构存储了多个zend_function结构体。和前面的成员变量一样,在类声明时成员方法也通过调用zend_initalize_class_data方法,初始化了整个方法列表所在的HashTable。
除去访问控制关键字,一个成员方法和常规函数是一样的,从语法解析中调用的函数一样(都是zend_do_begin_function_declaration函数),但是其调用的参数有一些不同,第三个参数is_method,成员方法的赋值为1,表示它作为成员方法的属性。在这个函数中会有一系统的编译判断,比如在接口中不能声明私有的成员方法。
在此程序判断后,程序将方法直接添加到类结构的function_table字段,在此之后,又是若干的编译检测。比如接口的一些魔术方法不能设置为非公有,不能被设置为static,如__call()、__callStatic()、__get()等。
与成员变量一样,成员方法也有一个返回所有成员方法的函数–get_class_methods()。此函数返回由指定的类中定义的方法名所组成的数组。
四、静态成员方法
类的静态成员方法通常也叫做类方法。与静态成员变量不同,静态成员方法与成员方法都存储在类结构的function_table字段。
class Tipi{ public static function t() { echo 1; } } Tipi::t();
以上的代码在VLD扩展下生成的部分中间代码:
number of ops: 8 compiled vars: none line # * op fetch ext return operands ------------------------------------------------------------------------------- -- 2 0 > EXT_STMT 1 NOP 8 2 EXT_STMT 3 ZEND_INIT_STATIC_METHOD_CALL 'Tipi','t' 4 EXT_FCALL_BEGIN 5 DO_FCALL_BY_NAME 0 6 EXT_FCALL_END 9 7 > RETURN 1 branch: # 0; line: 2- 9; sop: 0; eop: 7 path #1: 0, Class Tipi: Function t: Finding entry points Branch analysis from position: 0
从以上的内容可以看出整个静态成员方法的调用是一个先查找方法再调用的过程。而对于调用操作,对应的中间代码为ZEND_INIT_STATIC_METHOD_CALL。由于类名和方法名都是常量,于是我们可以知道中间代码对应的函数是ZEND_INIT_STATIC_METHOD_CALL_SPEC_CONST_CONST_HANDLER。在这个函数中,它会首先调用zend_fetch_class函数,通过类名在EG(class_table)中查找类,然后再执行静态方法的获取方法。
if (ce->get_static_method) { EX(fbc) = ce->get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); } else { EX(fbc) = zend_std_get_static_method(ce, function_name_strval, function_name_strlen TSRMLS_CC); }
如果类结构中的get_static_method方法存在,则调用此方法,如果不存在,则调用zend_std_get_static_method。在PHP的源码中get_static_method方法一般都是NULL,这里我们重点查看zend_std_get_static_method函数。此函数会查找ce->function_table列表,在查找到方法后检查方法的访问控制权限,如果不允许访问,则报错,否则返回函数结构体。