PHP学习笔记,二:类结构之

第一组问题:

1.成员变量与静态成员变量可否同名;

2.类的成员变量和静态成员变量交换访问方式能否访问到。

第二组问题:

1.成员方法与静态成员方法能否同名;

2.成员方法或静态成员方法是否可以与成员变量或静态成员变量同名;

3.如果交换访问方式是否可以成功访问。

实验准备:

class Test{
    public static $sv = 1;
    public $pv = 2;
    
    public static function sv(){
        echo 'static function sv.<br>';
    }
    public function pv(){
        echo 'normal function pv.<br>';
    }
}

第一组问题实验:

echo Test::$sv;//1
echo Test::$pv;//Fatal error: Uncaught Error: Access to undeclared static property: Test::$pv
$test = new Test();
echo $test->sv;//空白
var_dump($test->sv);//NULL

1.成员变量与静态成员变量不能同名;

2.交换访问方式不能访问到

第二组问题实验:

Test::sv();//static function sv.
Test::pv();//normal function pv.
$test = new Test();
$test->sv();//static function sv.
$test->pv();//normal function pv.

1.成员方法与静态成员方法不能同名;

2.成员方法/静态成员方法可以与成员变量/静态成员变量同名;

3.交换访问方式可以访问得到,但不推荐交换调用。

从类的存储结构出发理解以上问题:

//Zend\zend.h:116
struct _zend_class_entry {
    //......省略部分内容
    int default_properties_count;
    int default_static_members_count;
    zval *default_properties_table;
    zval *default_static_members_table;
    zval *static_members_table;
    HashTable function_table;
    HashTable properties_info;
    HashTable constants_table;
    //......又省略大部分内容
};

1. 类的成员变量数组(zval *default_properties_table)和静态成员变量数组(zval *default_static_members_table)虽然是类结构体的不同属性,但属性的访问均通过(HashTable properties_info)来查找(参见大牛分享的:PHP7类结构分析)。简单来说就是:

静态成员变量:根据属性名在properties_info中找到属性在静态变量数组中的下标,再根据下标去类结构的default_static_members_table中取值,静态变量的操作在类空间内,各实例对象共享;

普通成员变量:根据属性名在properties_info中找到属性在对象空间的偏移(普通成员变量的操作对应于类的实例对象,各个对象有自己独立的空间),根据该偏移取到对象普通成员变量值。

(1)不能同名是因为均需通过属性名在同一个HashTable中查找;

(2)不能交换访问是因为:

静态变量访问会执行zend_object_handlers.c中的zend_std_get_static_property方法,通过属性名在类的properties_info中找到对应的zend_property_info,通过其flags判断访问的属性类型是否为静态,若不是静态的会抛出我们看到的异常。

普通成员变量访问会执行zend_object_handlers.c中的zend_std_read_property方法,在其中调用zend_get_property_offset方法时判断了要访问的属性如果时静态的,则抛出E_NOTICE级别的错误,再后面的处理还没看懂。

2. 成员方法与静态成员方法存于类结构体的同一属性(HashTable function_table)中,所以不能重名;

可以交换访问方式的原因:

(1)调用静态成员方法会执行zend_object_handlers.c中的zend_std_get_static_method方法,从类的function_table中查找到对应方法后会判断如果方法不是静态的将抛出异常,前提是满足预处理指令#if MBO_0,它的含义暂不清楚,猜测和php设置的错误级别有关,经测试,将error_reporting设置为E_ALL时,将抛出如下错误:

Deprecated: Non-static method Test::pv() should not be called statically

(2)用普通成员方法调用会执行zend_object_handlers.c中的zend_std_get_method方法,其中并未特别处理静态方法的情况,所以可以访问到静态方法。