PHP中$GLOBALS和global的区别,简单了解符号表、zval
前言
单位里有一套老代码,写了这么一个换库逻辑。
function conn() {
global $conn;
if ($conn) {
unset($conn);
}
$conn = mysqli_connect...;
return $conn;
}
这套代码之前的换库操作,都是使用的返回值的$conn,一直相安无事。直到有新同事接手,用了另一个封装使用global $conn的查询方法,就出了问题。
按照这个设计,理论上应当是在任意位置都可以使用唯一的global $conn。那么为什么会出错呢,这就涉及到PHP中,global关键字,和$GLOBALS全局变量的区别陷阱了。
现象
$b = 2;
function aa()
{
global $a;
$a = 1;
unset($a);
unset($GLOBALS['b']);
}
aa();
echo $a, PHP_EOL;
echo $GLOBALS['a'], PHP_EOL;
var_dump($b);
输出:
1
1
Null
解释
在函数中,使用global关键字时,实际上是生成了一个局部变量,指向对应的全局变量,如果全局变量不存在,会先生成对应的全局变量,再指向它。
如果对这个局部变量使用unset,实际上只是将这个局部变量销毁,并不会影响到全局变量。
也即是说,global $a 和 $a = &$GLOBALS['a'] 在使用和表现形式上是等价的。因底层实现有区别,推荐使用gloabal关键字,消耗较少。
但使用$GLOBALS超全局变量时,$GLOBALS['b'] 和 全局的 $b 是完全一致的,使用unset会被影响。
底层原理
简而言之,
- $GLOBALS['a'],是全局$a本身。K存储在全局符号表中,K(a)->zval(a)。
- 函数中global $a,是指向全局$a的局部符号。K存储在局部符号表中,K->zval(a)。
- 函数中$_a = &$GLOBALS['a'],是指向全局$a的局部变量。K存储在局部变量表中,K->zval(_a)->zval(a)。
zval
每一个变量,最终指向的都是zval。
简单介绍
zval 是 PHP 的核心数据结构之一,用于表示 PHP 变量的内部实现。PHP 是一种弱类型语言,变量的类型可以动态变化,而 zval 则是实现这种动态类型的关键结构。它不仅存储变量的值,还包含了类型信息、引用计数等元数据。
struct _zval_struct {
zend_value value; /* value */
union {
uint32_t type_info;
struct {
ZEND_ENDIAN_LOHI_3(
uint8_t type, /* active type */
uint8_t type_flags,
union {
uint16_t extra; /* not further specified */
} u)
} v;
} u1;
union {
uint32_t next; /* hash collision chain */
uint32_t cache_slot; /* cache slot (for RECV_INIT) */
uint32_t opline_num; /* opline number (for FAST_CALL) */
uint32_t lineno; /* line number (for ast nodes) */
uint32_t num_args; /* arguments number for EX(This) */
uint32_t fe_pos; /* foreach position */
uint32_t fe_iter_idx; /* foreach iterator index */
uint32_t guard; /* recursion and single property guard */
uint32_t constant_flags; /* constant flags */
uint32_t extra; /* not further specified */
} u2;
};
typedef union _zend_value {
zend_long lval; /* long value */
double dval; /* double value */
zend_refcounted *counted;
zend_string *str;
zend_array *arr;
zend_object *obj;
zend_resource *res;
zend_reference *ref;
zend_ast_ref *ast;
zval *zv;
void *ptr;
zend_class_entry *ce;
zend_function *func;
struct {
uint32_t w1;
uint32_t w2;
} ww;
} zend_value;
struct _zend_string {
zend_refcounted_h gc;
zend_ulong h; /* hash value */
size_t len;
char val[1];
};
...
zval 的基本结构
变量值 (value):存储变量的实际值,可以是整型、浮点型、字符串、数组、对象等。
变量类型 (type):表示当前变量的类型,如 IS_LONG (整型)、IS_STRING (字符串)、IS_ARRAY (数组) 等。
引用计数 (refcount):用来跟踪有多少个变量引用了该 zval,用于实现引用计数的垃圾回收机制。
引用标志 (is_ref):表示该 zval 是否是一个引用。如果变量被引用了(例如使用 &),这个标志会被设置。
zval 中的重要概念
写时复制(Copy-On-Write, COW)、引用计数和垃圾回收、动态类型转换。
符号表
在PHP中,变量的管理是通过符号表(Symbol Table)来实现的。符号表是一个哈希表,存储变量名和变量的值(实际上是一个 zval 结构)。每个作用域(如全局作用域、函数作用域)都有自己的符号表。
可以把符号表简单的理解为一个KV结构。变量名为K,zval为V。
在 PHP 脚本开始执行时,全局符号表就会被创建。也就是$GLOBALS。
局部符号表是在进入函数或方法时动态创建的。当函数调用结束时,局部符号表会被销毁。
哈希表
HashTable结构体,也叫zend_array,同样是_zend_value中的一种数据类型。
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
uint8_t flags,
uint8_t _unused,
uint8_t nIteratorsCount,
uint8_t _unused2)
} v;
uint32_t flags;
} u;
uint32_t nTableMask;
union {
uint32_t *arHash; /* hash table (allocated above this pointer) */
Bucket *arData; /* array of hash buckets */
zval *arPacked; /* packed array of zvals */
};
uint32_t nNumUsed;
uint32_t nNumOfElements;
uint32_t nTableSize;
uint32_t nInternalPointer;
zend_long nNextFreeElement;
dtor_func_t pDestructor;
};
题外话,符号表的生命周期,Swoole相关
PHP中,有一个叫EG的宏定义,用于存储PHP执行引擎的全局状态信息,符号表就存储在EG的结构体_zend_executor_globals中。
struct _zend_executor_globals {
...
zend_array symbol_table; /* main symbol table */
}
_zend_executor_globals 是 PHP 执行引擎的全局结构体,包含了所有与脚本执行相关的状态信息。该结构体被设计为线程局部存储(TLS),在多线程环境中,每个线程都有自己独立的 executor_globals 实例。
由于EG是线程共享的,所以Swoole协程中,对($GLOBALS、$_SESSION、$_SERVER)等的操作会互相污染,这也是为什么Swoole对全局变量,$_SESSION等操作要单独隔离处理的原因。
global $a 和 $a = &$GLOBALS['a'] 的区别
符号表绑定 vs. 引用赋值:
global $a; 是通过符号表来实现的,局部作用域中的 $a 直接指向全局符号表中的同名变量。这意味着局部变量 $a 是全局变量 $a 的一个别名。
$a = &$GLOBALS['a']; 是通过显式的引用赋值来实现的,局部变量 $a 被赋值为全局符号表中 $a 的引用。虽然效果是一样的,但这里涉及的是通过 $GLOBALS 数组的访问。
底层操作差异:
global $a; 只是修改了局部符号表的指针,使得局部符号表中的 $a 指向全局符号表中的变量。
$a = &$GLOBALS['a']; 则是通过引用赋值操作,直接修改局部变量的 zval 引用,使它与 $GLOBALS['a'] 指向同一个 zval。
执行效率:
global $a; 的执行稍微快一些,因为它仅仅修改符号表中的指针。
$a = &$GLOBALS['a']; 需要通过 $GLOBALS 数组进行一次查找操作,然后执行引用赋值,因此可能稍微慢一些。
PHP中$GLOBALS和global的区别,简单了解符号表、zval的更多相关文章
- PHP中超全局变量$GLOBALS和global的区别
一.超全局变量$GLOBALS PHP超全局变量有很多,如下的都属于超全局变量(Superglobal): $GLOBALS,$_SERVER,$_GET,$_POST,$_FILES,$_COOKI ...
- PHP中$GLOBALS和global的区别
很多人都认为$GLOBALS['var']和global $var只是写法上不同,其实并不是这样 根据官方的解释是 $GLOBALS['var']是外部全局变量$var的本身, 而global $v ...
- php中global与$GLOBALS的用法及区别-转载
php中global 与 $GLOBALS[""] 差别 原本觉得global和$GLOBALS除了写法不一样觉得,其他都一样,可是在实际利用中发现2者的差别还是很大的! 先看下面 ...
- php中global与$GLOBALS的用法及区别
php中global 与 $GLOBALS[""] 差别 原本觉得global和$GLOBALS除了写法不一样觉得,其他都一样,可是在实际利用中发现2者的差别还是很大的! 先看下面 ...
- python简单实现用户表单登录
实现简单的用户表单验证登录 user="desperado" pwd=" s=0 for i in range(10): if s < 3: username = ...
- c语言描述简单的线性表,获取元素,删除元素,
//定义线性表 #define MAXSIZE 20 typedef int ElemType; typedef struct { ElemType data[MAXSIZE]; //这是数组的长度, ...
- 简单的多表插入(oracle)
简单的多表插入语句: insert all into 表1(字段1,2...) values(值1,值2......) into 表2(字段1,2...)) values(值1,值2......) s ...
- 15.5 自学Zabbix之路15.5 Zabbix数据库表结构简单解析-其他 表
点击返回:自学Zabbix之路 自学Zabbix之路15.5 Zabbix数据库表结构简单解析-其他 表 1. Actions表 actions表记录了当触发器触发时,需要采用的动作. 2.Aler ...
- 自学Zabbix之路15.1 Zabbix数据库表结构简单解析-Hosts表、Hosts_groups表、Interface表
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 自学Zabbix之路15.1 Zabbix数据库表结构简单解析-Hosts表.Hosts_grou ...
- 自学Zabbix之路15.2 Zabbix数据库表结构简单解析-Items表
点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 自学Zabbix之路15.2 Zabbix数据库表结构简单解析-Items表 Items表记录了i ...
随机推荐
- 局域网环境下,如何在Ubuntu中发现Windows10下的共享文件夹
参考: https://blog.csdn.net/rangfei/article/details/124225799 ======================================== ...
- vscode 设置窗口菜单栏显示字体大小
最近换了一块大些的显示屏,发现vscode的窗口字体有些小了,不是很方便,于是研究了一下如何设置vscode的窗口字体大小. 需要注意的是这里的设置是对窗口字体的而不是编辑器的字体. 1 . 通过主 ...
- P2P下载为什么不流行了——在线视频与P2P下载的一些比较
平时习惯性发呆,这两天发呆想到了这么一个问题,那就是"P2P下载为什么不流行了--在线视频与P2P下载的比较".想到这个问题其实还是与自己的一些个人经历有关,在14年前读大学的时候 ...
- 什么是MMU
一.MMU的定义 MMU是Memory Management Unit的缩写,中文名是内存管理单元,有时也称作分页内存管理单元(Paged Memory Management Unit,缩写为PM ...
- ai识图测试
var code = "9392b629-0d84-43ef-9b0f-34740fb024a6"
- 记一次 .NET某实验室自动进样系统 崩溃分析
一:背景 1. 讲故事 前些天有位朋友在微信上联系到我,说他们的程序在客户那边崩掉了,让我帮忙看下怎么回事,dump也拿到了,那就上手分析吧. 二:WinDbg 分析 1. 哪里的崩溃 既然是程序的崩 ...
- 安装 Ubuntu / Windows 双系统
最近给自己的老笔记本换了一块大硬盘,顺便装了 Windows 和 Ubuntu 两个操作系统.记录一下安装过程. 提示:Ubuntu 安装程序可以检测到磁盘已有的 Windows 安装.所以如果先安装 ...
- [python][selenium] Web UI自动化页面切换iframe框架
关联文章:Web UI自动化8种页面元素定位方式 1.切换iframe的方法:switch_to.frame 入参有4种: 1.1.id 1.2.name 1.3.index索引 1.4.i ...
- nacos未授权访问漏洞,导致被网警找
背景 公司收购了另一家公司,所以相应的后端服务和服务器都交给我管理,但是没有任何的交接文档,大概看了一下代码,依赖的东西就去忙别的了. 直到今天网警突然打电话给我说系统有漏洞(CVE-2021-294 ...
- 【合合TextIn】深度解析智能文档处理技术与应用
一.智能文档处理介绍 智能文档处理(Intelligent Document Processing, IDP)是利用人工智能(AI).机器学习(ML).计算机视觉(CV).自然语言处理(NLP)等技术 ...