c语言编程笔录

首页 >   > 笔记大全

笔记大全

解析PHP底层内核源码之变量

更新时间:2023-10-25

PHP底层内核源码之变量

在PHP中,变量是程序中存储数据的基本单元,这些数据可以是数字、字符串、数组、对象或者资源等类型。PHP底层内核源码中对变量的定义、初始化、垃圾回收和传递等操作进行了详细的实现。下面将从这些方面介绍PHP底层内核源码中变量的实现。

变量的定义

在PHP中,定义变量时需要指定变量类型,以下是一些常见的变量类型定义:

  int $i;
  float $f;
  string $s;
  bool $b;
  array $a;
  object $obj;
  resource $res;
  

定义变量时需要为其分配内存空间,这个过程在Zend/zend_alloc.c文件的_zend_mm_alloc函数中完成,如下代码所示:

  ZEND_API void *_zend_mm_alloc(zend_mm_heap *heap, size_t size, int permanent)
  {
    void *ptr = zend_mm_alloc_heap(heap, size, permanent);

    if (ptr == NULL && !permanent) {
      ptr = zend_mm_alloc_heap(heap, size, 1);
      if (ptr != NULL) {
        permanent = 1;
      }
    }

    if (ptr == NULL) {
      zend_mm_alloc_failure(size, permanent);
    }

    ZEND_MM_CHECK_MAGIC((char*)ptr-sizeof(zend_mm_hdr),)
    return ptr;
  }
  

该函数根据指定大小size在内存池heap中分配内存空间,并返回指向该空间的指针。需要注意的是,指向变量的指针并不是变量本身,变量在内存中对应的位置可能比指针所指向的位置向前或向后偏移。

变量的初始化

在PHP中,变量可以先定义再初始化,也可以在定义变量时就进行初始化。对于已经定义的变量,可以通过赋值语句进行初始化,如下所示:

  $i = 10;
  $s = 'hello world';
  $a = array(1, 2, 3);
  

PHP中的变量默认值为NULL,如果一个变量未被初始化,那么它的值就是NULL。在PHP底层内核源码中,变量的初始化是由init_zval函数完成的,如下代码所示:

  ZEND_API void init_zval(zval *z)
  {
    Z_TYPE_INFO_P(z) = IS_NULL;
    Z_RES_P(z) = NULL;
    Z_ARRVAL_P(z) = NULL;
    Z_OBJ_P(z) = NULL;
    Z_STR_P(z) = NULL;
    Z_UNUSED_P(z) = 0;
    Z_LVAL_P(z) = 0;
    Z_DVAL_P(z) = 0;
  }
  

该函数将zval结构体中各个成员的值都初始化为0,即将变量的类型设置为IS_NULL,关联数组和对象销毁时需要用到的成员指针设置为NULL,字符串和资源的成员指针也都设置为NULL。虽然在PHP代码中,变量默认值为NULL,但是在实现上还是通过init_zval函数进行空间的初始化。

变量的垃圾回收

PHP底层内核源码中的垃圾回收主要是在zval结构体中完成的,zval结构体是PHP中变量的底层实现。在PHP中,垃圾回收是一种自动管理内存的方式,它可以自动检测不再使用的内存空间,并将其释放。PHP的垃圾回收机制主要有两种:

  • 引用计数
  • 标记清除

PHP底层内核源码中采用的是引用计数的方式来进行垃圾回收。在PHP代码中,变量赋值是按值传递的,因此多个变量可能指向同一块内存空间。通过增加和减少zval结构体的refcount计数器,可以实现对内存空间的引用计数统计和管理。当refcount为0时,代表该内存空间已经没有任何变量引用,此时就可以将该空间回收,如下是PHP内核源码中实现引用计数回收的核心代码片段:

  void _zval_dtor(zval *zval_ptr ZEND_FILE_LINE_DC)
  {
    switch (Z_TYPE_P(zval_ptr)) {
      case IS_RESOURCE: {
        ...
        break;
      }
      case IS_OBJECT: {
        ...
        break;
      }
      case IS_ARRAY: {
        ...
        FREE_HASHTABLE(Z_ARRVAL_P(zval_ptr));
        Z_ARRVAL_P(zval_ptr) = NULL;
        break;
      }
      case IS_STRING: {
        ...
        free_string(Z_STR_P(zval_ptr));
        Z_STR_P(zval_ptr) = NULL;
        break;
      }
      default: {
        break;
      }
    }

    Z_TYPE_P(zval_ptr) = IS_NULL;
    Z_COUNTED_P(zval_ptr) = 0;
  }
  

该函数用于销毁zval结构体,当其中某个变量的refcount为0时就执行实际的销毁操作。根据变量的类型,该函数分别调用不同的销毁函数。对于数组、字符串等,销毁函数会先释放其对应的空间,然后将zval结构体的成员指针设置为NULL,防止在回收悬空指针。销毁操作完成后,还要将zval结构体的类型设置为IS_NULL,同时将其refcount清零。

变量的传递

在PHP底层内核源码中,变量的传递是按值传递的,因此传递过程是将变量的值复制一份到新的内存空间中,传递新的内存空间指针。在PHP中,对于数组和对象等复杂类型,传递的是它们的引用地址,一旦传递过程中发生了修改,那么原变量的值也会受到影响。

总结:本文从PHP底层内核源码的角度详细介绍了变量的定义、初始化、垃圾回收和传递等操作。我们了解到变量是程序中存储数据的基本单元,定义变量时需要为其分配内存空间。变量的默认值为NULL,在初始化过程中会调用init_zval函数将其初始值设置为NULL。PHP底层内核源码中采用的是引用计数的方式进行垃圾回收,zval结构体的refcount计数器用于实现引用计数统计和管理,当refcount为0时,代表该内存空间已经没有任何变量引用,此时就可以将该空间回收。变量的传递是按值传递的,像数组和对象等复杂类型传递的是其引用地址。对于PHP开发人员来说,了解变量在底层是如何实现和管理的,对于我们写出高效稳定的代码有重要帮助。