发布于 2026-01-06 0 阅读
0

PHP 变量底层解析 DEV 全球展示挑战赛,由 Mux 呈现:展示你的项目!

PHP底层变量

由 Mux 赞助的 DEV 全球展示挑战赛:展示你的项目!

PHP 中的变量是一些容器,用于存储变量的类型、值、引用此容器的变量的数量以及标志(此变量是否被引用)。

PHP底层变量

结构体和指针

结构体与类非常相似,但它们不能包含方法,只能包含数据、指向数据的指针和指向函数的指针。在 C 语言中声明结构体时,需要定义其数据类型。定义变量时,可以用结构体的名称代替变量的类型,如下所示:

my_super_struct super_struct_instance;
Enter fullscreen mode Exit fullscreen mode

指针类似于变量,但它们的值存储的是内存中的地址。引用变量的行为类似于解引用的指针,这意味着它们可以访问指针指向的值。我们来看一个例子:

// defining pointer `foo`, that will points to the variable with `int` type
int *foo;
// defining variable with `int` type
int bar = 3;

// taking reference to variable `bar` and assigning it to pointer.
// `foo` stores memory address, where `bar` stores
foo = &bar;

// with an asterisk we dereference the pointer (take the value at its address) and increment the value
(*foo)++;

// we increment the pointer itself, that means the pointer will refer at another value
foo++;
Enter fullscreen mode Exit fullscreen mode

处理指针

容器

容器是一个名为“Zend value” zval(简称“Zend value”)的结构体,它代表一个任意的 PHP 值,如下所示:

struct zval {
    zvalue_value value;
    zend_uchar type;
    zend_uchar is_ref;
    zend_ushort refcount;
};
Enter fullscreen mode Exit fullscreen mode

如我们所见,变量包含值、类型、标志和引用数量。PHPzval支持以下 8 种类型:

  • BOOL
  • LONG (有符号整数类型)
  • DOUBLE (用于存储浮点数)
  • STRING
  • ARRAY
  • OBJECT
  • RESOURCE
  • NULL

zvalue_value是一个联合体union。联合体是一种特殊类型,它可以包含多个不同类型的成员声明,​​但只会使用其中一个。这就是它的定义:

typedef union _zvalue_value {
    long lval; // integer
    double dval; // float
    struct {
        char *val;
        int len;
    } str; // string
    HashTable *ht; // array
    zend_object obj; // object
} zvalue_value;
Enter fullscreen mode Exit fullscreen mode

因此,当你创建这种类型的变量时,它将占用与联合体中最重元素占用的内存完全相同的内存。

PHP变量创建

我们为什么需要这一切?

首先,我们来弄清楚为什么需要引用计数。这很简单:当你将另一个变量的值赋给一个变量时,它们都引用同一个引用zval,并且refcount引用计数会递增。

两个变量指的是同一个内存位置

现在,如果您更改其中一个变量的值,PHP 检测到refcount该值大于 1 后,会复制该变量zval,并在副本中进行更改,这样您的变量就会指向新的值zval。它看起来会像这样:

PHP 引擎盖下
$foo = "baz";
$bar = $foo;
bar,foo: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 0
    refcount: 2
}
$bar .= "q";
foo: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 0
    refcount: 1
}
bar: {
    type: string,
    value:
        str:
            val: "bazq"
            len: 4
    is_ref: 0
    refcount: 1
}

这种技术称为“写时复制”,它可以很好地降低内存消耗。此外,垃圾回收器refcount也需要它,它会从内存中移除所有具有.zvalrefcount = 0

那么引用会发生什么情况?它is_ref是否有效?这很简单:如果您从一个变量创建一个引用,则 is_ref 标志变为 1,并且上述针对此 zval 的优化将不会应用。

PHP 引擎盖下
$foo = "baz";
$bar = $foo;
bar,foo: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 0
    refcount: 2
}
$baz = &$foo;
baz,foo: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 1
    refcount: 2
}
bar: { // variable `bar` was allocated to a separate `zval`
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 0
    refcount: 1
}
$qwe = $foo;
baz,foo: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 1
    refcount: 2
}
bar: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 0
    refcount: 1
}
// this variable was also allocated to a separate `zval`
qwe: {
    type: string,
    value:
        str:
            val: "baz"
            len: 3
    is_ref: 0
    refcount: 1
}

对 PHP 感兴趣,想了解更多相关内容吗?点击此链接查看更多文章 😉

文章来源:https://dev.to/larapulse/php-variables-under-the-hood-2onh