page contents

PHP中的类,在内存中的存储结构原来是这样

类的定义 类是现实世界或思维世界中的实体在计算机中的反映,它将某些具有关联关系的数据以及这些数据上的操作封装在一起。在面向对象中类是对象的抽象,对象是类的具体实例。 我们先来看一下...

类的定义

类是现实世界或思维世界中的实体在计算机中的反映,它将某些具有关联关系的数据以及这些数据上的操作封装在一起。在面向对象中类是对象的抽象,对象是类的具体实例。

我们先来看一下PHP中是如何定义类的:

class 类名 {

常量;

成员属性;

成员方法;

}

一个类可以包含有属于自己的常量、变量(称为“属性”)以及函数(称为“方法”),下面我们将围绕这三部分具体弄清楚以下几个问题:

  • 类的存储及索引
  • 成员属性的存储结构
  • 成员方法的存储结构

类的结构及存储

话不多说,先来看下类的数据结构:

struct _zend_class_entry {

char type; //类的类型:内部类ZEND_INTERNAL_CLASS(1)、用户自定义类ZEND_USER_CLASS(2)

zend_string *name; //类名,PHP类不区分大小写,统一为小写

struct _zend_class_entry *parent; //父类

int refcount;

uint32_t ce_flags; //类掩码,如普通类、抽象类、接口等

int default_properties_count; //普通属性数,包括public、private

int default_static_members_count; //静态属性数,static

zval *default_properties_table; //普通属性值数组

zval *default_static_members_table; //静态属性值数组

zval *static_members_table;

HashTable function_table; //成员方法哈希表

HashTable properties_info; //成员属性基本信息哈希表,key为成员名,value为zend_property_info

HashTable constants_table; //常量哈希表,通过constant定义的

//以下是构造函授、析构函数、魔术方法的指针

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 *__debugInfo;

union _zend_function *serialize_func;

union _zend_function *unserialize_func;

zend_class_iterator_funcs iterator_funcs;

//自定义的钩子函数,通常是定义内部类时使用,可以灵活的进行一些个性化的操作

//用户自定义类不会用到,暂时忽略即可

zend_object* (*create_object)(zend_class_entry *class_type);

zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref);

int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type); /* a class implements this interface */

union _zend_function *(*get_static_method)(zend_class_entry *ce, zend_string* method);

/* serializer callbacks */

int (*serialize)(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data);

int (*unserialize)(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data);

uint32_t num_interfaces; //实现的接口数

uint32_t num_traits;

zend_class_entry **interfaces; //实现的接口

zend_class_entry **traits;

zend_trait_alias **trait_aliases;

zend_trait_precedence **trait_precedences;

union {

struct {

zend_string *filename;

uint32_t line_start;

uint32_t line_end;

zend_string *doc_comment;

} user;

struct {

const struct _zend_function_entry *builtin_functions;

struct _zend_module_entry *module; //所属扩展

} internal;

} info;

}

来看一个具体例子:定义一个User类,它继承了Human类,User类中有一个常量、一个静态属性、两个普通属性:

//父类

class Human {}

class User extends Human

{

const type = 110;

static $name = "uuu";

public $uid = 900;

public $sex = 'w';

public function __construct(){

}

public function getName(){

return $this->name;

}

}

其对应的zend_class_entry存储结构如下图:

PHP中的类,在内存中的存储结构原来是这样,终于弄明白了

类是编译阶段的产物,编译完成后我们定义的每个类都会生成一个zend_class_entry,它保存着类的全部信息,在执行阶段所有类相关的操作都是用的这个结构。

所有PHP脚本中定义的类以及内核、扩展中定义的内部类通过一个以"类名"作为索引的哈希表存储,这个哈希表保存在Zend引擎global变量中:zend_executor_globals.class_table(即:EG(class_table)),与function的存储相同。如下图所示:

PHP中的类,在内存中的存储结构原来是这样,终于弄明白了

类常量

PHP中可以把在类中始终保持不变的值定义为常量,在定义和使用常量的时候不需要使用 $ 符号,常量的值必须是一个定值,不能是变量、数学运算的结果或函数调用,也就是说它是只读的,无法进行赋值。

常量通过 const 定义:

class my_class {

const 常量名 = 常量值;

}

常量通过 class_name::常量名 访问,或在class内部通过 self::常量名 访问。

常量通过zend_class_entry.constants_table进行存储,这是一个哈希结构,通过 常量名 索引,value就是具体定义的常量值。

成员属性

类的变量成员叫做“属性”。属性声明是由关键字 publicprotected 或者 private 开头,然后跟一个普通的变量声明来组成。

【修饰符(public/private/protected/static)】【成员属性名】= 【属性默认值】;

属性中的变量可以初始化,但是初始化的值必须是常数,这里的常数是指 PHP 脚本在编译阶段时就可以得到其值,而不依赖于运行时的信息才能求值,如public $time = time();这样定义一个属性就会触发语法错误。

成员属性又分为两类:普通属性静态属性。静态属性通过 static 声明,通过 self::$property 或 类名::$property 访问;普通属性通过 $this->property 或 $object->property 访问。

class my_class {

//普通属性

public $property = 初始化值;

//静态属性

public static $property_2 = 初始化值;

}

与常量的存储方式不同,成员属性的 初始化值 并不是 直接 用以"属性名"作为索引的哈希表存储的,而是通过数组保存的,普通属性、静态属性各有一个数组分别存储。

PHP中的类,在内存中的存储结构原来是这样,终于弄明白了

但是访问时仍然是根据以"属性名"为索引的哈希表查找具体VALUE的,而且都存储在了一个哈希表中:HashTable properties_info 。此哈希表存储元素的value类型为 zend_property_info 。结构如下:

typedef struct _zend_property_info {

uint32_t offset; //普通成员变量的内存偏移值

//静态成员变量的数组索引

uint32_t flags; //属性掩码,如public、private、protected及是否为静态属性

zend_string *name; //属性名:并不是原始属性名

zend_string *doc_comment;

zend_class_entry *ce; //所属类

} zend_property_info;

//flags标识位

#define ZEND_ACC_PUBLIC 0x100

#define ZEND_ACC_PROTECTED 0x200

#define ZEND_ACC_PRIVATE 0x400

#define ZEND_ACC_STATIC 0x01

所以访问成员属性时首先是根据属性名查找到此属性的存储位置,然后再进一步获取属性值。

例子来啦:

class my_class {

public $property_1 = "aa";

public $property_2 = array();

public static $property_3 = 110;

}

则 default_properties_tabledefault_static_properties_tableproperties_info 关系图:

PHP中的类,在内存中的存储结构原来是这样,终于弄明白了

但是静态属性和普通成员属性不同:静态成员变量保存在类中,各对象共享同一份数据,而普通属性属于对象,各对象独享。

成员方法

每个类可以定义若干属于本类的函数(称之为成员方法),这种函数与普通的function相同,只是以类的维度进行管理,不是全局性的,所以成员方法保存在类中。

PHP中的类,在内存中的存储结构原来是这样,终于弄明白了

成员方法的定义如下:

【修饰符(public/private/protected/static/abstruct/final)】function 【&】【成员方法名】(【参数列表】)【返回值类型】{【成员方法】};

成员方法也有静态、非静态之分,静态方法中不能使用$this,因为其操作的作用域全部都是类的而不是对象的,而非静态方法中可以通过$this访问属于本对象的成员属性。

静态方法也是通过static关键词定义:

class my_class {

static public function test() {

$a = "hi~";

echo $a;

}

}

//静态方法可以这么调用:

my_class::test();

//也可以这样:

$method = 'test';

my_class::$method();

好啦,看到这里,是不是对PHP中的类有了更深的了解呢。我是PHP程序媛,努力向前,成长可见。

  • 发表于 2020-02-26 17:33
  • 阅读 ( 503 )
  • 分类:PHP开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章