PHP存储变量的方法,zval结构体你了解吗?

php变量使我们后端开发人员经常使用到的,那么你真的会使用php的全局变量吗,下面这篇文章就为您详细阐述了关于php变量和zval的一些结构体,阅读后希望可以帮助到你。

PHP 源码中的 zval

在 PHP 中定义一个变量是不需要声明类型的,一开始给变量 $a 赋予一个整型值,后面又可以轻而易举地将其改变为其他类型。那在 PHP 的源码中是如何来存储这个变量 $a 的呢?带着这个疑问我们一起去看一看 PHP 的源码。

PHP 的源码是由 C 编写的,在 PHP 的源码中使用了一个 zval 的结构体来存储在 PHP 代码中创建的变量。我们把 zval 结构体的定义拿出来简单分析一下。

这是 PHP 在 Github 上的官方仓库:github.com/php/php-src,本文使用的分支是 PHP-7.4.29。

phpbianliang

zval 结构体

在 PHP 的源码中找到这个文件:php-src/Zend/zend_types.h,可以看到其中 zval 结构体的定义如下,左侧是源码。源码中使用了 PHP 自己定义的类型 zend_uchar 、uint16_t 、uint32_t 等,这些类型会针对不同平台和编译器会转为该平台下的 char short int 等。为了便于理解,我将其翻译为普通类型并展示在了源码的右侧。同时还把其中的宏函数 ZEND_ENDIAN_LOHI_3() 也展开了。

typedef struct _zval_struct zval;

《源代码》 《翻译后》
——————————————————————————————-
struct _zval_struct { | struct _zval_struct {
zend_value value; | zend_value value;
union { | union {
struct { | struct {
ZEND_ENDIAN_LOHI_3( | unsigned char type;
zend_uchar type, | unsigned char type_flags;
zend_uchar type_flags, | union {
union { | unsigned short extra;
uint16_t extra; | } u;
} u | } v;
) | unsigned int type_info;
} v; | } u1;
uint32_t type_info; | union {
} u1; | unsigned int next;
union { | unsigned int cache_slot;
uint32_t next; | unsigned int opline_num;
uint32_t cache_slot; | unsigned int lineno;
uint32_t opline_num; | unsigned int num_args;
uint32_t lineno; | unsigned int fe_pos;
uint32_t num_args; | unsigned int fe_iter_idx;
uint32_t fe_pos; | unsigned int access_flags;
uint32_t fe_iter_idx; | unsigned int property_guard;
uint32_t access_flags; | unsigned int constant_flags;
uint32_t property_guard; | unsigned int extra;
uint32_t constant_flags; | } u2;
uint32_t extra; | };
} u2; |
}; |

在 zval 结构体中,变量的值就存储在 zend_value 类型的 value 属性中。并通过 u1.v.type 来记录这个值是什么类型的,比如 IS_LONG 对应整型,IS_STRING 对应字符串类型。

zend_value 联合体

zend_value 类型也是在 php-src/Zend/zend_types.h 中定义的,是一个联合体,下面是 zend_value 联合体的定义,左侧是源码。同样在右侧我也做了简单的翻译,把 zend_long uint32_t 翻译为普通类型便于查看。

《源代码》 《翻译后》
————————————————————————————
typedef union _zend_value { | typedef union _zend_value {
zend_long lval; /* long value */ | long lval;
double dval; /* double value */ | double dval;
zend_refcounted *counted; | zend_refcounted *counted;
zend_string *str; | zend_string *str;
zend_array *arr; | zend_array *arr;
zend_object *obj; | zend_object *obj;
zend_resource *res; | zend_resource *res;
zend_reference *ref; | zend_reference *ref;
zend_ast_ref *ast; | zend_ast_ref *ast;
zval *zv; | zval *zv;
void *ptr; | void *ptr;
zend_class_entry *ce; | zend_class_entry *ce;
zend_function *func; | zend_function *func;
struct { | struct {
uint32_t w1; | unsigned int w1;
uint32_t w2; | unsigned int w2;
} ww; | } ww;
} zend_value; | } zend_value;

联合体的一个特点是其占用的内存是其属性中最大类型对应的长度。其中的 zend_long 就是 long 类型,可以看到 long 类型的 lval 和 double 类型的 dval 占用的长度都是 8 个字节。里面其他指针类型,也均为 8 个字节。最后面的结构体属性 ww 是由两个 int 型构成,长度相加也是 8 个字节。因此此联合体的长度为 8 个字节。

在我们写的 PHP 代码中,整型和浮点型数据的值会直接存放到 lval 和 dval 中。如果是字符串、数组以及其他类型时会开辟一段空间存储数据,并将其地址存放在 zend_value 中,也就是 zval.value 属性,如:zval.value.zend_long = 9527、zval.value.zend_string = 字符串地址 、zval.value.zend_array = 数组地址。然后在 zval.u1.v.type 上标记这个 zval.value 是整型、或浮点型、或字符串、或其他类型。

zval.u1.v.type 类型定义也是在 php-src/Zend/zend_types.h 文件中,全部的定义如下:

/* regular data types */
#define IS_UNDEF 0
#define IS_NULL 1
#define IS_FALSE 2
#define IS_TRUE 3
#define IS_LONG 4
#define IS_DOUBLE 5
#define IS_STRING 6
#define IS_ARRAY 7
#define IS_OBJECT 8
#define IS_RESOURCE 9
#define IS_REFERENCE 10
/* constant expressions */
#define IS_CONSTANT_AST 11
/* internal types */
#define IS_INDIRECT 13
#define IS_PTR 14
#define IS_ALIAS_PTR 15
#define _IS_ERROR 15
/* fake types used only for type hinting (Z_TYPE(zv) can not use them) */
#define _IS_BOOL 16
#define IS_CALLABLE 17
#define IS_ITERABLE 18
#define IS_VOID 19
#define _IS_NUMBER 20

zval 结构体内存占用

接下来我们分析一下 zval 所需占用的内存。

value:zend_value 类型 8 个字节。

u1:

u1.v.type:unsigned char 1 个字节,u1.v.type_flags:unsigned char 1 个字节,u1.v.u:联合体中只有一个 unsigned short 的 extra 属性 2 个字节,因此 u1.v 的结构体总共是 4 个字节。

u1.type_info:unsigned int 4 个字节。

因此 u1 这个联合体的长度取最长的属性的长度:4 个字节。

u2:也是一个联合体,里面都是 int 型的属性,因此长度是 4 个字节。

所以 zval 总共占用的内存是 8 + 4 + 4 = 16 个字节。

也就是说当我们在写 PHP 代码时,如果创建了一个整型的变量,那么实际上它在运行中会占用 16 个字节的内存,内存开销至少是 C 语言的两倍。当然这两倍的开销也带来了 PHP 处理变量的灵活性。

php全局变量存储

解决方案

$vfp_oledb_CString=”Provider=VFPOLEDB.1;Data Source=d:/testoledb; Collating Sequence=MACHINE”;

testgol();

function testgol(){

global $vfp_oledb_CString;

echo $vfp_oledb_CString;

}

不建议使用global关键字,还是用$GLOBALS超全局变量吧

装个yac吧。进程间共享内存,很好用。

还需要在调用时再声明一次吗? .

这个字串是用来声明. vfp表的目录位置的, 我想把它作为一个配置文件 用来include

然后在一个PHP 文件的各个 FUNCTION 中使用, 要达到这样的效果. 应该使用那种方法比较好

也可以用$GLOBAL超全局变量

$vfp_oledb_CString=”Provider=VFPOLEDB.1;Data Source=d:/testoledb; Collating Sequence=MACHINE”;

testgol();

function testgol(){

echo $GLOBALS[‘vfp_oledb_CString’];

}

如果你的字串是固定的,你可以把它定义成常量,常量的范围是全局的。不用管作用区域就可以在脚本的任何地方访问常量。

关于php的全局变量就介绍到这里,如果你还有什么关于php的疑问,可以查看底部的相关阅读或者底部留言都是可以的。

本文来自投稿,不代表(钦钦技术栈)立场,如若转载,请注明出处:https://www.qin1qin.com/catagory/367/

(0)
上一篇 2022年 6月 22日 9:54:28
下一篇 2022年 6月 23日 12:47:35

软件定制开发公司

相关阅读

发表回复

登录后才能评论
通知:禁止投稿所有关于虚拟货币,币圈类相关文章,发现立即永久封锁账户ID!