[Traduction][Développement d'extensions php et embarqué] Chapitre 2 - L'intérieur et l'extérieur des variables

黄舟
Libérer: 2023-03-05 16:08:02
original
1375 Les gens l'ont consulté

Les tenants et les aboutissants des variables

Une fonctionnalité commune à chaque langage de programmation est le stockage et la récupération d'informations ; PHP ne fait pas exception, bien que de nombreux langages exigent que toutes les variables soient définies avant utilisation, ainsi que leurs informations de type. est corrigé, mais PHP permet aux programmeurs de créer des variables lorsqu'ils les utilisent et peut stocker des informations qui peuvent être exprimées dans n'importe quel type de langage. Il peut également convertir automatiquement les types de variables en cas de besoin.

Parce que vous avez utilisé user-. space PHP, il faut savoir que ce concept est du "typage faible". Dans ce chapitre, vous verrez cette information dans le langage parent de PHP-c (les types du C sont stricts) Comment est-il codé

Bien sûr, l'encodage des données ne représente que la moitié du travail. Pour garder une trace de toutes ces informations, chaque variable a également besoin d'une étiquette et d'un conteneur. Du point de vue de l'espace utilisateur, vous pouvez les considérer comme les concepts de noms et de portée de variables.

Type de données

L'unité de stockage des données en php est zval, également appelée Zend Value. C'est une structure avec seulement 4 membres Body, définie dans Zend/zend.h, a le format suivant :

typedef struct _zval_struct {  
    zval_value  value;  
    zend_uint   refcount;  
    zend_uchar  type;  
    zend_uchar  is_ref;  
} zval;
Copier après la connexion

Nous pouvons intuitivement deviner les types de stockage de base de la plupart de ces membres : refcount d'entier non signé, type et is_ref de caractère non signé. Le membre valeur est en fait une structure définie comme une union. est défini comme suit :

typedef union _zvalue_value {  
    long                    lval;  
    double              dval;  
    struct {  
        char        *val;  
        int     len;  
    }                   str;  
    HashTable           *ht;  
    zend_object_value       obj;  
} zvalue_value;
Copier après la connexion

Union permet à Zend d'utiliser une structure unique et unifiée pour stocker de nombreux types de données différents dans une variable php

zend définit actuellement 8 types de données répertoriés. dans le tableau suivant :


IS_RESOURCE


上表中的IS_*常量被存储在zval结构的type元素中, 用来确定在测试变量的值时应该查看value元素中的哪个部分.

最明显的检查一个数据的类型的方法如下代码:

void describe_zval(zval *foo)  
{  
    if (foo->type == IS_NULL) {  
        php_printf("The variable is NULL");  
    } else {  
        php_printf("The variable is of type %d", foo->type);  
    }  
}
Copier après la connexion

显而易见, 但是是错的.

好吧, 没有错, 但确实不是首选做法. Zend头文件包含了很多的zval访问宏, 它们是作者期望在测试zval数据时使用的方式. 这样做主要的原因是避免在引擎的api变更后产生不兼容问题, 不过从另一方面来看这样做还会使得代码更加易读. 下面是相同功能的代码段, 这一次使用了Z_TYPE_P()宏:

void describe_zval(zval *foo)  
{  
    if (Z_TYPE_P(foo) == IS_NULL) {  
        php_printf("The variable is NULL");  
    } else {  
        php_printf("The variable is of type %d",  
                            Z_TYPE_P(foo));  
    }  
}
Copier après la connexion

这个宏的_P后缀标识传递的参数应该是一级间访的指针. 还有另外两个宏Z_TYPE()和Z_TYPE_PP(), 它们期望的参数类型是zval(非指针)和zval **(两级间访指针).

注意

在这个例子中使用了一个特殊的输出函数php_printf(), 它被用于展示数据片. 这个函数语法上等同于stdio的printf函数; 不过它对webserver sapi有特殊的处理, 使用php的输出缓冲机制提升性能. 你将在第5章"你的第一个扩展"中更多的了解这个函数以及它的同族PHPWRITE().

数据值

和类型一样, zval的值也可以用3个一组的宏检查. 这些宏总是以Z_开始, 可选的以_P或_PP结尾, 具体依赖于它们的间访层级.

对于简单的标量类型, boolean, long, double, 宏简写为: BVAL, LVAL, DVAL.

void display_values(zval boolzv, zval *longpzv,  
                zval **doubleppzv)  
{  
    if (Z_TYPE(boolzv) == IS_BOOL) {  
        php_printf("The value of the boolean is: %s\n",  
            Z_BVAL(boolzv) ? "true" : "false");  
    }  
    if (Z_TYPE_P(longpzv) == IS_LONG) {  
        php_printf("The value of the long is: %ld\n",  
            Z_LVAL_P(longpzv));  
    }  
    if (Z_TYPE_PP(doubleppzv) == IS_DOUBLE) {  
        php_printf("The value of the double is: %f\n",  
            Z_DVAL_PP(doubleppzv));  
    }  
}
Copier après la connexion

由于字符串变量包含两个成员, 因此它有一对宏分别表示char *(STRVAL)和int(STRLEN)成员:

void display_string(zval *zstr)  
{  
    if (Z_TYPE_P(zstr) != IS_STRING) {  
        php_printf("The wrong datatype was passed!\n");  
        return;  
    }  
    PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));  
}
Copier après la connexion

数组数据类型内部以HashTable *存储, 可以使用: Z_ARRVAL(zv), Z_ARRVAL_P(pzv), Z_ARRVAL_PP(ppzv)访问. 在阅读旧的php内核和pecl模块的代码时, 你可能会碰到HASH_OF()宏, 它期望一个zval *参数. 这个宏等价于Z_ARRVAL_P()宏, 不过, 这个用法已经废弃, 在新的代码中应该不再被使用.

对象的内部表示结构比较复杂, 它有较多的访问宏: OBJ_HANDLE返回处理标识, OBJ_HT返回处理器表, OBJCE用于类定义, OBJPROP用于属性的HahsTable, OBJ_HANDLER用于维护OBJ_HT表中的一个特殊处理器方法. 现在不要被这么多的对象访问宏吓到, 在第10章"php4对象"和第11章"php5对象"中它们的细节都会介绍.

在一个zval中, 资源数据类型被存储为一个简单的整型, 它可以通过RESVAL这一组宏来访问. 这个整型将被传递给zend_fetch_resource()函数在已注册资源列表中查找资源对象. 我们将在第9章深入讨论资源数据类型.

数据的创建

现在你知道了怎样从一个zval中取出数据, 是时候创建一些自己的数据了. 虽然zval可以作为一个直接变量定义在函数的顶部, 这使得变量的数据存储在本地, 为了让它离开这个函数到达用户空间就需要对其进行拷贝.

因为你大多数时候都是希望自己创建的zval到达用户空间, 因此你就需要分配一个块内存给它, 并且将它赋值给一个zval *指针. 与之前的"显而易见"的方案一样, 使用malloc(sizeof(zval))并不是正确的答案. 取而代之的是你要用另外一个Zend宏: MAKE_STD_ZVAL(pzv). 这个宏将会以一种优化的方式在其他zval附近为其分配内存, 自动的处理超出内存错误(下一章将会解释), 并初始化新zval的refcount和is_ref属性.

除了MAKE_STD_ZVAL(), 你可能还经常会碰到其他的zval *创建宏, 比如ALLOC_INIT_ZVAL(). 这个宏和MAKE_STD_ZVAL唯一的区别是它会将zval *的数据类型初始化为IS_NULL.

一旦数据存储空间可用, 就可以向你的新zval中填充一些信息了. 在阅读了前面的数据存储部分后, 你可能准备使用Z_TYPE_P()和Z_SOMEVAL_P()宏去设置你的新变量. 我们来看看这个"显而易见"的方案是否正确?

同样, "显而易见"的并不正确!

Zend暴露了另外一组宏用来设置zval *的值. 下面就是这些新的宏和它们展开后你已经熟悉的格式:

ZVAL_NULL(pvz);                   Z_TYPE_P(pzv) = IS_NULL;
Copier après la connexion

虽然这些宏相比使用更加直接的版本并没有节省什么, 但它的出现体现了完整性.

ZVAL_BOOL(pzv, b);                Z_TYPE_P(pzv) = IS_BOOL;  
                                  Z_BVAL_P(pzv) = b ? 1 : 0;  
ZVAL_TRUE(pzv);                   ZVAL_BOOL(pzv, 1);  
ZVAL_FALSE(pzv);                  ZVAL_BOOL(pzv, 0);
Copier après la connexion

注意, 任何非0值提供给ZVAL_BOOL()都将产生一个真值. 当在内部代码中硬编码时, 使用1表示真值被认为是较好的实践. 宏ZVAL_TRUE()和ZVAL_FALSE()提供用来方便编码, 有时也会提升代码的可读性.

ZVAL_LONG(pzv, l);                Z_TYPE_P(pzv) = IS_LONG;  
                                  Z_LVAL_P(pzv) = l;  
ZVAL_DOUBLE(pzv, d);              Z_TYPE_P(pzv) = IS_DOUBLE;  
                                  Z_DVAL_P(pzv) = d;
Copier après la connexion

基础的标量宏和它们自己一样简单. 设置zval的类型, 并给它赋一个数值.

ZVAL_STRINGL(pzv,str,len,dup);    Z_TYPE_P(pzv) = IS_STRING;  
                                  Z_STRLEN_P(pzv) = len;  
                                  if (dup) {  
                                      Z_STRVAL_P(pzv) =  
                                            estrndup(str, len + 1);  
                                  } else {  
                                     Z_STRVAL_P(pzv) = str;  
                                  }  
ZVAL_STRING(pzv, str, dup);       ZVAL _STRINGL(pzv, str,  
                                                strlen(str), dup);
Copier après la connexion

这里, zval的创建就开始变得有趣了. 字符串就像数组, 对象, 资源一样, 需要分配额外的内存用于它们的数据存储. 在下一章你将继续探索内存管理的陷阱; 现在, 只需要注意, 当dup的值为1时, 将分配新的内存并拷贝字符串内容, 当dup的值为0时, 只是简单的将zval指向已经存在的字符串数据.

ZVAL_RESOURCE(pzv, res);          Z_TYPE_P(pzv) = IS_RESOURCE;  
                                  Z_RESVAL_P(pzv) = res;
Copier après la connexion

回顾前面, 资源在zval中只是存储了一个简单的整型, 它用于在Zend管理的资源表中查找. 因此ZVAL_RESOURCE()宏就很像ZVAL_LONG()宏, 但是, 使用不同的类型.

数据类型/值/创建回顾练习

static void eae_001_zval_dump_real(zval *z, int level) {  
    HashTable   *ht;  
    int         ret;  
    char        *key;  
    uint        index;  
    zval        **pData;  
  
    switch ( Z_TYPE_P(z) ) {  
        case IS_NULL:  
            php_printf("%*stype = null, refcount = %d%s\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", is_ref " : "");  
            break;  
        case IS_BOOL:  
            php_printf("%*stype = bool, refcount = %d%s, value = %s\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", 
            is_ref " : "", Z_BVAL_P(z) ? "true" : "false");  
            break;  
        case IS_LONG:  
            php_printf("%*stype = long, refcount = %d%s, value = %ld\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", 
            is_ref " : "", Z_LVAL_P(z));  
            break;  
        case IS_STRING:  
            php_printf("%*stype = string, refcount = %d%s, value = \"%s\", len = %d\n", level * 4, "", Z_REFCOUNT_P(z), 
            Z_ISREF_P(z) ? ", is_ref " : "", Z_STRVAL_P(z), Z_STRLEN_P(z));  
            break;  
        case IS_DOUBLE:  
            php_printf("%*stype = double, refcount = %d%s, value = %0.6f\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", 
            is_ref " : "", Z_DVAL_P(z));  
            break;  
        case IS_RESOURCE:  
            php_printf("%*stype = resource, refcount = %d%s, resource_id = %d\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", 
            is_ref " : "", Z_RESVAL_P(z));  
            break;  
        case IS_ARRAY:  
            ht      = Z_ARRVAL_P(z);  
  
            zend_hash_internal_pointer_reset(ht);  
            php_printf("%*stype = array, refcount = %d%s, value = %s\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", 
            is_ref " : "", HASH_KEY_NON_EXISTANT != zend_hash_has_more_elements(ht) ? "" : "empty");  
            while ( HASH_KEY_NON_EXISTANT != (ret = zend_hash_get_current_key(ht, &key, &index, 0)) ) {  
                if ( HASH_KEY_IS_STRING == ret ) {  
                    php_printf("%*skey is string \"%s\"", (level + 1) * 4, "", key);  
                } else if ( HASH_KEY_IS_LONG == ret ) {  
                    php_printf("%*skey is long %d", (level + 1) * 4, "", index);  
                }  
                ret = zend_hash_get_current_data(ht, &pData);  
                eae_001_zval_dump_real(*pData, level + 1);  
                zend_hash_move_forward(ht);  
            }  
            zend_hash_internal_pointer_end(Z_ARRVAL_P(z));  
            break;  
        case IS_OBJECT:  
            php_printf("%*stype = object, refcount = %d%s\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", is_ref " : "");  
            break;  
        default:  
            php_printf("%*sunknown type, refcount = %d%s\n", level * 4, "", Z_REFCOUNT_P(z), Z_ISREF_P(z) ? ", is_ref " : "");  
            break;  
    }  
}  
  
PHP_FUNCTION(eae_001_zval_dump)  
{  
    zval    *z;  
  
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &z) == FAILURE) {  
        return;  
    }  
  
    eae_001_zval_dump_real(z, 0);  
  
    RETURN_NULL();  
}  
  
PHP_FUNCTION(eae_001_zval_make)  
{  
    zval    *z;  
  
    MAKE_STD_ZVAL(z);  
  
    ZVAL_NULL(z);  
    eae_001_zval_dump_real(z, 0);  
  
    ZVAL_TRUE(z);  
    eae_001_zval_dump_real(z, 0);  
  
    ZVAL_FALSE(z);  
    eae_001_zval_dump_real(z, 0);  
  
    ZVAL_LONG(z, 100);  
    eae_001_zval_dump_real(z, 0);  
  
    ZVAL_DOUBLE(z, 100.0);  
    eae_001_zval_dump_real(z, 0);  
  
    ZVAL_STRING(z, "100", 0);  
    eae_001_zval_dump_real(z, 0);  
}
Copier après la connexion

数据存储

你已经在用户空间一侧使用过php了, 因此你应该已经比较熟悉数组了. 我们可以将任意数量的php变量(zval)放入到一个容器(array)中, 并可以为它们指派数字或字符串格式的名字(标签----key)

如果不出意外, php脚本中的每个变量都应该可以在一个数组中找到. 当你创建变量时, 为它赋一个值, Zend把这个值放到被称为符号表的一个内部数组中.

有一个符号表定义了全局作用域, 它在请求启动后, 扩展的RINIT方法被调用之前初始化, 接着在脚本执行完成后, 后续的RSHUTDOWN方法被执行之前销毁.

当一个用户空间的函数或对象方法被调用时, 则分配一个新的符号表用于函数或方法的生命周期, 它被定义为激活的符号表. 如果当前脚本的执行不在函数或方法中, 则全局符号表被认为是激活的.

我们来看看globals结构的实现(在Zend/zend_globals.h中定义), 你会看到下面的两个元素定义:

struct _zend_execution_globals {  
    ...  
    HashTable symbol_table;  
    HashTable *active_symbol_table;  
    ...  
};
Copier après la connexion

symbol_table, 使用EG(symbol_table)访问, 它永远都是全局变量作用域, 和用户空间的$GLOBALS变量相似, 用于对应于php脚本的全局作用域. 实际上, $GLOBALS变量的内部就是对EG(symbol_table)上的一层包装.

另外一个元素active_symbol_table, 它的访问方法类似: EG(active_symbol_table), 表示此刻激活的变量作用域.

这里有一个需要注意的关键点, EG(symbol_table), 它不像你在php和zend api下工作时将遇到的几乎所有其他HashTable, 它是一个直接变量. 几乎所有的函数在HashTable上操作时都期望一个间访的HashTable *作为参数. 因此, 你在使用时需要在EG(symbol_table)前加取地址符(&).

考虑下面的代码块, 它们的功能是等价的

/* php实现 */  
<?php $foo = &#39;bar&#39;; ?>  
  
/* C实现 */  
{  
    zval *fooval;  
  
    MAKE_STD_ZVAL(fooval);  
    ZVAL_STRING(fooval, "bar", 1);  
    ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval);  
}
Copier après la connexion

首先, 使用MAKE_STD_ZVAL()分配一个新的zval, 它的值被初始化为字符串"bar". 接着是一个新的宏调用, 它的作用是将fooval这个zval增加到当前激活的符号表中, 设置的变量名为"foo". 因为此刻并没有用户空间函数被激活, 因此EG(active_symbol_table) == &EG(symbol_table), 最终的含义就是这个变量被存储到了全局作用域中.

数据取回

为了从用户空间取回一个变量, 你需要在符号表的存储中查找. 下面的代码段展示了使用zend_hash_find()函数达成这个目的:

{  
    zval **fooval;  
  
    if (zend_hash_find(EG(active_symbol_table),  
                       "foo", sizeof("foo"),  
                       (void**)&fooval) == SUCCESS) {  
        php_printf("Got the value of $foo!");  
    } else {  
        php_printf("$foo is not defined.");  
    }  
}
Copier après la connexion

这个例子中有一点看起来有点奇怪. 为什么要把fooval定义为两级间访指针呢? 为什么sizeof()用于确定"foo"的长度呢? 为什么是&fooval? 哪一个被评估为zval ***, 转换为void **?如果你问了你自己所有上面3个问题, 请拍拍自己的后背.

首先, 要知道HashTable并不仅用于用户空间变量, 这一点很有价值. HashTable结构用途很广, 它被用在整个引擎中, 甚至它还能完美的存储非指针数据. HashTable的桶是定长的, 因此, 为了存储任意大小的数据, HashTable将分配一块内存用来放置被存储的数据. 对于变量而言, 被存储的是一个zval *, 因此HashTable的存储机制分配了一块足够保存一个指针的内存. HashTable的桶使用这个新的指针保存zval *的值, 因此在HashTable中被保存的是zval **. HashTable完全可以漂亮的存储一个完整的zval, 那为什么还要这样存储zval *呢? 具体原因我们将在下一章讨论.

在尝试取回数据的时候, HashTable仅知道有一个指针指向某个数据. 为了将指针弹出到调用函数的本地存储中, 调用函数自然就要取本地指针(变量)的地址, 结果就是一个未知类型的两级间访的指针变量(比如void **). 要知道你的未知类型在这里是zval *, 你可以看到把这种类型传递给zend_hash_find()时, 编译器会发现不同, 它知道是三级间访而不是两级. 这就是我们在前面加一个强制类型转换的目的, 用来抑制编译器的警告.

在前面的例子中使用sizeof()的原因是为了在"foo"常量用作变量的标签时包含它的终止NULL字节. 这里使用4的效果是等价的; 不过这比较危险, 因为对标签名的修改会影响它的长度, 现在这样做在标签名变更时比较容易查找需要修改的地方. (strlen("foo") + 1)也可以解决这个问题, 但是, 有些编译器并没有优化这一步, 结果产生的二进制文件最终执行时可能得到的是一个毫无意义的字符串长度, 拿它去循环可不是那么好玩的!

如果zend_hash_find()定位到了你要查找的项, 它就会将所请求数据第一次被增加到HashTable中时时分配的桶的指针地址弹出到所提供的指针(zend_hash_find()第4个参数)中, 同时返回一个SUCCESS整型常量. 如果zend_hash_find()不能定位到数据, 它就不会修改指针(zend_hash_find()第四个参数)而是返回整型常量FAILURE.

站在用户空间的角度看, 变量存储到符号表所返回的SUCCESS或FAILURE实际上就是变量是否已经设置(isset).

类型转换

现在你可以从符号表抓取变量, 那可能你就想对它们做些什么. 一种直接的事倍功半的方法是检查变量的类型, 并依赖类型执行特殊的动作. 就像下面代码中简单的switch语句就可以工作.

void display_zval(zval *value)  
{  
    switch (Z_TYPE_P(value)) {  
        case IS_NULL:  
            /* NULLs are echoed as nothing */  
            break;  
        case IS_BOOL:  
            if (Z_BVAL_P(value)) {  
                php_printf("1");  
            }  
            break;  
        case IS_LONG:  
            php_printf("%ld", Z_LVAL_P(value));  
            break;  
        case IS_DOUBLE:  
            php_printf("%f", Z_DVAL_P(value));  
            break;  
        case IS_STRING:  
            PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value));  
            break;  
        case IS_RESOURCE:  
            php_printf("Resource #%ld", Z_RESVAL_P(value));  
            break;  
        case IS_ARRAY:  
            php_printf("Array");  
            break;  
        case IS_OBJECT:  
            php_printf("Object");  
            break;  
        default:  
            /* Should never happen in practice, 
             * but it&#39;s dangerous to make assumptions 
             */  
             php_printf("Unknown");  
             break;  
    }  
}
Copier après la connexion

是的, 简单, 正确. 对比前面的例子, 不难猜想这种编码会使得代码不好管理. 幸运的是, 在脚本执行输出变量的行为时, 无论是扩展, 还是嵌入式环境, 引擎都使用了非常相似的里程. 使用Zend暴露的convert_to_*()函数族可以让这个例子变得很简单:

void display_zval(zval *value)  
{  
    convert_to_string(value);  
    PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value));  
}
Copier après la connexion

你可能会猜到, 有很多这样的函数用于转换到大多数数据类型. 值得注意的是convert_to_resource(), 它没有意义, 因为资源类型的定义旧是不能映射到真实用户空间表示的值.

如果你担心convert_to_string()调用对传递给函数的zval的值的修改不可逆, 那说明你很棒. 在真正的代码段中, 这是典型的坏主意, 当然, 引擎在输出变量时并不是这样做的. 下一章你将会看到安全的使用转换函数的方法, 它会安全的修改值的内容, 而不会破坏它已有的内容.

小结

本章中你看到了php变量的内部表示. 你学习了区别类型, 设置和取回值, 将变量增加到符号表中以及将它们取回. 下一章你将在这些知识的基础之上, 学习怎样拷贝一个zval, 怎样在不需要的时候销毁它们, 最重要的而是, 怎样避免在不需要的时候产生拷贝.

你还将看到Zend的单请求内存管理层的一角, 了解了持久化和非持久化分配. 在下一章的结尾, 你旧有实力可以去创建一个工作的扩展并在上面用自己的代码做实验了.

以上就是 [翻译][php扩展开发和嵌入式]第2章-变量的里里外外的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!
tapez ValeurObjectif
IS_NULLCe type est automatiquement attribué aux variables non initialisées jusqu'à sa première utilisation. Il peut également être utilisé par l'utilisateur. L'espace utilise la constante NULL intégrée pour une affectation explicite. data", qui est différent du booléen FALSE et de l'entier 0.
IS_BOOLLes variables booléennes peuvent avoir l'un des deux états possibles, TRUE/FALSE. Utilisateur Les structures de contrôle d'espace if/while/ternary/for et d'autres expressions conditionnelles intermédiaires seront évaluées lors de l'évaluation de la conversion implicite en type booléen.
IS_DOUBLEType de données à virgule flottante , en utilisant le type de données double signé du système hôte, les nombres à virgule flottante ne sont pas stockés avec une précision exacte, une formule est utilisée pour représenter la précision finie de la partie décimale de la valeur (Annotation : les nombres à virgule flottante sont représentés en 3 parties. : signe, mantisse - partie fractionnaire, exposant. La valeur d'un nombre à virgule flottante = signe * mantisse * 2^exposant - --Extrait du manuel des fonctions de la bibliothèque BSD : float(3)). plage de valeurs (positives ou négatives) : 2,225*10^(-308) à 1,798 peut être représentée sur 8 octets * Un nombre compris dans la plage *10^(308). Malheureusement, la décimale réelle du nombre auquel il est évalué. n'est pas toujours stockée aussi proprement qu'une fraction binaire. Par exemple, l'expression décimale 0,5 se convertit en la valeur binaire exacte de 0,1, alors que la conversion décimale de 0,8 en binaire est une boucle infinie de 0,1100110011..., et quand c'est le cas. reconvertis en décimal, les bits binaires supprimés ne peuvent pas être récupérés car ils ne peuvent pas être stockés. De même, vous pouvez penser à convertir 1/3 en décimal 0,333333, deux La valeur est très similaire, mais elle est imprécise car 3 * 0,333333 n'est pas égal. à 1,0. Cette imprécision déroute souvent les gens lors du traitement de nombres à virgule flottante sur des ordinateurs (ces limites de plage sont généralement basées sur des plates-formes 32 bits ; les plages peuvent varier selon les systèmes)
IS_STRING

Le type de données le plus courant en PHP est la chaîne, qui est stockée de la manière attendue par les programmeurs C expérimentés. Allouez une mémoire suffisamment grande pour contenir tous les octets/caractères de la chaîne et enregistrez le pointeur sur la chaîne dans l'hôte zval.

Il est à noter que la longueur de la chaîne PHP est toujours explicitement indiquée dans la structure zval. Cela permet à la chaîne de contenir des octets NULL sans être tronquée. aspect des chaînes PHP « binaires sûres » car cela permet de contenir en toute sécurité tout type de données binaires

Il convient de noter que la quantité totale de mémoire allouée pour une chaîne PHP est toujours minimisée : la longueur plus. 1. Le dernier octet stocke le caractère NULL de fin, donc peu importe que les fonctions binaires sécurisées puissent recevoir directement des pointeurs de chaîne.

IS_ARRAYUn tableau est une variable à usage spécial dont la seule fonction est d'organiser d'autres variables Contrairement au concept de tableau en C, le tableau de php n'est pas un vecteur de a. un seul type de données (comme zval arrayofzvals[];). En fait, le tableau de PHP est une collection complexe de buckets de données, et son intérieur est un HashTable. Chaque élément (bucket) de HashTable contient deux éléments d'information correspondants : des balises et des données. . Dans le scénario d'application du tableau PHP, la balise est la clé ou la valeur du tableau associé. La donnée est la variable (zval) pointée par la clé
IS_OBJECT<🎜. ><🎜 Les objets disposent de tableaux de stockage de données multi-éléments, en plus de l'ajout de méthodes, de modificateurs d'accès, de constantes de portée et de gestionnaires d'événements spéciaux. En tant que développeur d'extensions, c'est un grand défi de créer un code orienté objet équivalent en PHP 4. et PHP 5. Un gros défi, car il y a de très gros changements dans le modèle objet interne entre Zend Engine 1 (php4) et Zend Engine 2 (php5).
Certains types de données ne peuvent pas être simplement mappés à l'espace utilisateur. Par exemple, le pointeur FILE de stdio ou le handle de connexion de libmysqlclient, ils ne peuvent pas être simplement mappés à un tableau de valeurs scalaires, sinon ils perdront leur signification. protéger les utilisateurs Les rédacteurs de scripts spatiaux n'ont pas à gérer ces problèmes. PHP fournit un type de données de ressources pan-chinois. Nous aborderons les détails d'implémentation des types de ressources dans le chapitre 9 « Types de données de ressources ». c'est une telle chose.