php 底层体系,PHP底层原理-知其然知其所以然

本文详细解析了PHP中变量的底层实现,从zval结构体到变量的存储、类型、创建过程以及符号表的作用。介绍了函数执行环境中的符号表变化,以及静态变量的实现。同时探讨了常量的定义、存储结构及其全局有效性的原因。此外,还讨论了PHP的内存管理和垃圾回收机制,特别是引用计数和写时复制的概念。
摘要由CSDN通过智能技术生成

1·、PHP变量的底层实现

PHP代码执行图解

ec61ea945e390572a48e788ea226e0b9.png

1.1:变量在内存中的存储结构

PHP变量是通过zval结构体来存储的,文件: Zend/zend.h 316行左右

7d54781e0c0a3e260bb41cc41b69c473.png

1.2:值的存储

PHP变量的值是放在zval结构体中的value段中的,文件: Zend/zend.h

c72f38a6fd1cf12b400dadde9bb3ac95.png

1.3:结构体的字段解释

struct _zval_struct {

/* Variable information */

zvalue_value value;/*变量的值,是个联合体*/

zend_uint refcount__gc; /*指向次数*/

zend_uchar type;/* 变量类型 */

zend_uchar is_ref__gc; /*是否引用*/

};

type字段的值为以下常量

IS_NULL, IS_BOOL,IS_LONG,IS_DOUBLE

IS_STRING,IS_ARRAY,IS_OBJECT

IS_RESOURCE

1.4:联合体中的值

typedef union _zvalue_value {

long lval;/* long value */

double dval;/* double value */

struct {

char *val;

int len;

} str;

HashTable *ht;/* hash table value */

zend_object_value obj;

} zvalue_value;

联合中为什么只列出了5种值?

NULL不用,zval的type为IS_NULL即可

Bool以1,0存储在lval上

resource的type为resource,其resource的内容用long来标志(资源标记)

1.5:变量的结构图

cdfcafd9b021c99ccce95bc578d21cb9.png

1.6:变量的创建

创建变量的步骤: $str = "hello";

1:创建zval结构,并设置其类型 IS_STRING

2:设置其值为 hello

3:讲其加入符号表

{

zval *fooval;

MAKE_STD_ZVAL(fooval);

ZVAL_STRING(fooval, "hello", 1);

ZEND_SET_SYMBOL( EG(active_symbol_table) , "foo" , fooval);

}

1.7:符号表 symbol_table

符号表是什么?

符号表是一张哈希表,里面存储了变量名->变量的zval结构体的地址,

// zend/zend_globals.h 182行

struct _zend_executor_globals {

...

...

HashTable *active_symbol_table; /*活动符号表*/

HashTable symbol_table;/* 全局符号表 */

HashTable included_files;/* files already included */

1.8:符号表与函数

Zend/zend_compiles.h

struct _zend_execute_data {

...

zend_op_array *op_array; //函数的执行步骤

HashTable *symbol_table; // 此函数的符号表地址

zend_class_entry *current_scope;

zval *current_this;

zval *current_object;

...

};

上面这个,是当前函数执行时的符号表

1.9:符号表与作用域

当执行到函数时,会生成函数的"执行环境结构体",包含函数名,参数,执行步骤,所在的类(如果是方法),以及为这个函数生成一个符号表.符号表统一放在栈上.并把active_symbol_table指向刚产生的符号表

a790e71ad252adfceeeeb14d4b986ede.png

1.10:函数中静态变量的实现

75f895f87fc7f95ba65cb0ee254d3108.png

4ecdf26b9e864c3b8ae1fa6d2c42a29f.png

2.0:常量-常量结构体

结构体 Zend/constants.h 33行

typedef struct _zend_constant {

zval value; //变量结构体

int flags; //标志,是否大小写敏感等

char *name; //常量名

uint name_len;

int module_number;//模块名

} zend_constant;

2.1:常量的生成

int zend_register_constant(zend_constant *c TSRMLS_DC) {

...

...

zend_hash_add(EG(zend_constants), name, c->name_len, (void *) c, sizeof(zend_constant), NULL)==FAILURE)

...

...

}

2.2:define函数的实现

define函数当然是调用zend_register_constant声明的常量 :)

具体如下 Zend/zend_builtin_functions.c

关键代码:

c.value = *val;

zval_copy_ctor(&c.value);

if (val_free) {

zval_ptr_dtor(&val_free);

}

c.flags = case_sensitive; /* non persistent */

c.name = zend_strndup(name, name_len);

c.name_len = name_len+1;

c.module_number = PHP_USER_CONSTANT;

if (zend_register_constant(&c TSRMLS_CC) == SUCCESS) {

RETURN_TRUE;

} else {

RETURN_FALSE;

}

2.3:有趣的测试

1:常量并没有检测名字...

define('^_^',"laugh");

2:常量的第2个参数还可以是对象,与手册上介绍的不同

define('obj',new className());

(当然,对象要有toString方法等着)

2.4:常量为什么是全局有效的?

很简单,常量的哈希表只有一个

EG(zend_constants)

3.0:内存管理与垃圾回收

333f3ab328502b0ff569b4d446990c9c.png

PHP封装了对系统内存的请求,不要直接用malloc直接请求内存

3.1:PHP的hashtable太强大

854adb7a49c58013d1bd28b200def265.png

3.2:引用计数

$a = 1;

$b = $a;

?>

$a,$b的值及类型 都一样,有必要再申请一个zval结构吗?

2d92559a4f061d5a6f31282233f5305d.png

3.3:引用计数解释

$a = 1;

$b = $a;

?>

当$a的值赋给$b时,并没有为$b生成一个新的zval结构体.而是$b与$a共享一个结构体.

3.4:copy-on-write 写时复制

$a = 1;

$b = $a;

$b=6

?>

1febf961cce626dda33d5d04689d77a8.png

3.5:引用传值发生了什么?

$a = 1;

$b = &$a;

?>

3906211de60c73496a401767e399b25d.png

3.6:引用传值为什么影响了2个值

$a = 1;

$b = &$a;

$b = 6;

?>

内核修改zval的值时,发现is_ref_gc为1,则直接修改该value,而不是复制一份.如下图:

3afb6e4da140e4ad370b40afdfe09850.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值