• php中弱语言类型的底层实现


    PHP是弱语言类型,主要分为三类:

    1、标量类型:integer、string、float、boolean

    2、复合类型:array、object

    3、特殊类型:resource、null

    php是通过c语言进行实现,但是c语言为强类型,那php的弱语言类型是如何实现的呢。

    1. 变量存储结构

    变量的值存储到以下所示zval结构体中。 zval结构体定义在Zend/zend.h文件,其结构如下:

    typedef struct _zval_struct zval;
    ...
    struct _zval_struct {
        /* Variable information */
        zvalue_value value;     /* value */
        zend_uint refcount__gc;
        zend_uchar type;    /* active type */
        zend_uchar is_ref__gc;
    };

    PHP使用这个结构来存储变量的所有数据。和其他编译性静态语言不同, PHP在存储变量时将PHP用户空间的变量类型也保存在同一个结构体中。这样我们就能通过这些信息获取到变量的类型。

    zval结构体中有四个字段,其含义分别为:

    属性名含义默认值
    refcount__gc 表示引用计数 1
    is_ref__gc 表示是否为引用 0
    value 存储变量的值  
    type 变量具体的类型  

    2.变量类型:

    zval结构体的type字段就是实现弱类型最关键的字段了,type的值可以为: IS_NULL、IS_BOOL、IS_LONG、IS_DOUBLE、IS_STRING、IS_ARRAY、IS_OBJECT和IS_RESOURCE 之一。 从字面上就很好理解,他们只是类型的唯一标示,根据类型的不同将不同的值存储到value字段。 除此之外,和他们定义在一起的类型还有IS_CONSTANT和IS_CONSTANT_ARRAY。

    这和我们设计数据库时的做法类似,为了避免重复设计类似的表,使用一个标示字段来记录不同类型的数据。

    二.变量的值存储

    前面提到变量的值存储在zvalue_value联合体中,结构体定义如下:

    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;

    这里使用联合体而不是用结构体是出于空间利用率的考虑,因为一个变量同时只能属于一种类型。 如果使用结构体的话将会不必要的浪费空间,而PHP中的所有逻辑都围绕变量来进行的,这样的话, 内存浪费将是十分大的。这种做法成本小但收益非常大。

    各种类型的数据会使用不同的方法来进行变量值的存储,其对应赋值方式如下:

    • 一般类型
    变量类型 
    boolean ZVAL_BOOL 布尔型/整型的变量值存储于(zval).value.lval中,其类型也会以相应的IS_*进行存储。
     Z_TYPE_P(z)=IS_BOOL/LONG;  Z_LVAL_P(z)=((b)!=0); 
    integer ZVAL_LONG
    float ZVAL_DOUBLE
    null ZVAL_NULL NULL值的变量值不需要存储,只需要把(zval).type标为IS_NULL。
     Z_TYPE_P(z)=IS_NULL; 
    resource ZVAL_RESOURCE 资源类型的存储与其他一般变量无异,但其初始化及存取实现则不同。
     Z_TYPE_P(z) = IS_RESOURCE;  Z_LVAL_P(z) = l; 
    • 字符串String

    字符串的类型标示和其他数据类型一样,不过在存储字符串时多了一个字符串长度的字段。

    struct {
        char *val;
        int len;
    } str;

    C中字符串是以结尾的字符数组,这里多存储了字符串的长度,这和我们在设计数据库时增加的冗余字段异曲同工。 因为要实时获取到字符串的长度的时间复杂度是O(n),而字符串的操作在PHP中是非常频繁的,这样能避免重复计算字符串的长度, 这能节省大量的时间,是空间换时间的做法。 
    这么看在PHP中strlen()函数可以在常数时间内获取到字符串的长度。 计算机语言中字符串的操作都非常之多,所以大部分高级语言中都会存储字符串的长度。

    • 数组Array

    数组是PHP中最常用,也是最强大变量类型,它可以存储其他类型的数据,而且提供各种内置操作函数。数组的存储相对于其他变量要复杂一些, 数组的值存储在zvalue_value.ht字段中,它是一个HashTable类型的数据。 PHP的数组使用哈希表来存储关联数据。哈希表是一种高效的键值对存储结构。PHP的哈希表实现中使用了两个数据结构HashTable和Bucket。 PHP所有的工作都由哈希表实现,在下节HashTable中将进行哈希表基本概念的介绍以及PHP的哈希表实现。

    • 对象Object

    在面向对象语言中,我们能自己定义自己需要的数据类型,包括类的属性,方法等数据。而对象则是类的一个具体实现。 对象有自身的状态和所能完成的操作。

    PHP的对象是一种复合型的数据,使用一种zend_object_value的结构体来存放。其定义如下:

    typedef struct _zend_object_value {
        zend_object_handle handle;  //  unsigned int类型,EG(objects_store).object_buckets的索引
        zend_object_handlers *handlers;
    } zend_object_value;

    PHP的对象只有在运行时才会被创建,前面的章节介绍了EG宏,这是一个全局结构体用于保存在运行时的数据。 其中就包括了用来保存所有被创建的对象的对象池,EG(objects_store),而object对象值内容的zend_object_handle域就是当前 对象在对象池中所在的索引,handlers字段则是将对象进行操作时的处理函数保存起来。 这个结构体及对象相关的类的结构_zend_class_entry,将在第五章作详细介绍。

    PHP的弱变量容器的实现方式是兼容并包的形式体现,针对每种类型的变量都有其对应的标记和存储空间。 使用强类型的语言在效率上通常会比弱类型高,因为很多信息能在运行之前就能确定,这也能帮助排除程序错误。 而这带来的问题是编写代码相对会受制约。

  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/tangchuanyang/p/6185655.html
Copyright © 2020-2023  润新知