ホームページ  >  記事  >  バックエンド開発  >  PHP7 パラメータ、配列、Zval

PHP7 パラメータ、配列、Zval

高洛峰
高洛峰オリジナル
2016-11-23 15:45:421898ブラウズ

はじめに

これで、簡単な関数を宣言し、静的または動的値を返すことができます。 INI オプションを定義し、内部値またはグローバル値を宣言します。この章では、呼び出し元のスクリプト(phpファイル)から渡されるパラメータの値を受け取る方法と、PHPカーネルとZendエンジンが内部変数を操作する方法を紹介します。

パラメータを受け取る

ユーザー制御コードとは異なり、内部関数のパラメータは実際には関数ヘッダーで宣言されません。関数宣言は PHP_FUNCTION(func_name) の形式であり、パラメータ宣言は含まれません。パラメータはパラメータ リストのアドレスを通じて渡され、パラメータの有無に関係なくすべての関数に渡されます。

関数 hello_str() を定義して見てみましょう。この関数はパラメーターを受け取り、挨拶テキストとともに出力します。

PHP_FUNCTION(hello_greetme)
{    char *name = NULL;
    size_t name_len;
    zend_string *strg;    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
        RETURN_NULL();
    }

    strg = strpprintf(0, "你好: %s", name);
    RETURN_STR(strg);
}

ほとんどの zend_parse_parameters() ブロックは似ています。 ZEND_NUM_ARGS() は、取得するパラメータに関する情報を Zend エンジンに伝えます。TSRMLS_CC は、戻り値が SUCCESS か FAILURE かを検出します。通常、戻り値は SUCCESS です。渡されるパラメーターが少なすぎるか多すぎるか、パラメーターを適切な型に変換できない場合を除き、Zend は自動的にエラー メッセージを出力し、呼び出し元のスクリプトに制御を返します。

「s」を指定すると、この関数は 1 つのパラメーターのみが渡されることを予期し、パラメーターは文字列データ型に変換され、アドレスが char * 変数に渡されます。

さらに、アドレスによって zend_parse_parameters() に渡される int 変数があります。これにより、Zend エンジンが文字列のバイト長を提供できるようになり、バイナリ セーフ関数が文字列の長さを決定するために strlen(name) に依存しなくなります。 name には文字列の末尾の前に NULL 文字が含まれる可能性があるため、実際に strlen(name) を使用しても正しい結果さえ得られないからです。

php7 では、パラメータ解析のパフォーマンスを向上させるために、パラメータを取得する別の方法 FAST_ZPP が提供されています。

#ifdef FAST_ZPPZEND_PARSE_PARAMETERS_START(1, 2)
    Z_PARAM_STR(type)    Z_PARAM_OPTIONAL    Z_PARAM_ZVAL_EX(value, 0, 1)
    ZEND_PARSE_PARAMETERS_END();#endif

パラメータ型テーブル

PHP7 パラメータ、配列、Zval

最後の 4 つの型はすべて zvals * です。これは、実際の PHP の使用では、zval データ型にすべてのユーザー空間変数が格納されるためです。 3 つの「複雑な」データ型: リソース、配列、オブジェクト。それらのデータ型コードが zend_parse_parameters() で使用される場合、Zend エンジンは型チェックを実行しますが、C にはそれらに対応するデータ型がないため、型変換は実行されません。

Zval

一般的に、zvalとphpのユーザー空間変数は非常に面倒で、概念を理解するのが難しいです。 PHP7 では、その構造は Zend/zend_types.h で定義されています:

struct _zval_struct {
    zend_value        value;            /* value */
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    type,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
        uint32_t     access_flags;         /* class constant access flags */
        uint32_t     property_guard;       /* single property guard */
    } u2;
};

変数は _zval_struct 構造を通じて格納されており、変数の値は zend_value 型であることがわかります:

typedef union _zend_value {
    zend_long         lval;             /* long value */
    double            dval;             /* double value */
    zend_refcounted  *counted;
    zend_string      *str;
    zend_array       *arr;
    zend_object      *obj;
    zend_resource    *res;
    zend_reference   *ref;
    zend_ast_ref     *ast;
    zval             *zv;
    void             *ptr;
    zend_class_entry *ce;
    zend_function    *func;
    struct {
        uint32_t w1;
        uint32_t w2;
    } ww;
} zend_value;

構造は次のように見えますが、非常に大きいですが、よく見ると、これは実際には共用体であり、u1 は type_info であり、u2 はその他のさまざまな補助フィールドです。

zvalタイプ

变量存储的数据是有数据类型的,php7中总体有以下类型,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                 11
#define IS_CONSTANT_AST             12

/* fake types */
#define _IS_BOOL                    13
#define IS_CALLABLE                 14
#define IS_ITERABLE                 19
#define IS_VOID                     18

/* internal types */
#define IS_INDIRECT                 15
#define IS_PTR                      17
#define _IS_ERROR                   20

测试

书写一个类似gettype()来取得变量的类型的hello_typeof():

PHP_FUNCTION(hello_typeof)
{
    zval *userval = NULL;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &userval) == FAILURE) {
        RETURN_NULL();
    }
    switch (Z_TYPE_P(userval)) {
        case IS_NULL:
            RETVAL_STRING("NULL");
            break;

        case IS_TRUE:
            RETVAL_STRING("true");
            break;

        case IS_FALSE:
            RETVAL_STRING("false");
            break;

        case IS_LONG:
            RETVAL_STRING("integer");
            break;

        case IS_DOUBLE:
            RETVAL_STRING("double");
            break;

        case IS_STRING:
            RETVAL_STRING("string");
            break;

        case IS_ARRAY:
            RETVAL_STRING("array");
            break;

        case IS_OBJECT:
            RETVAL_STRING("object");
            break;

        case IS_RESOURCE:
            RETVAL_STRING("resource");
            break;

        default:
            RETVAL_STRING("unknown type");
    }
}

这里使用RETVAL_STRING()与之前的RETURN_STRING()差别并不大,它们都是宏。只不过RETURN_STRING中包含了RETVAL_STRING的宏代替,详细在 Zend/zend_API.h 中有定义:

#define RETVAL_STRING(s)                ZVAL_STRING(return_value, s)
#define RETVAL_STRINGL(s, l)            ZVAL_STRINGL(return_value, s, l)

#define RETURN_STRING(s)                { RETVAL_STRING(s); return; }
#define RETURN_STRINGL(s, l)            { RETVAL_STRINGL(s, l); return; }

创建zval

前面用到的zval是由Zend引擎分配空间,也通过同样的途径释放。然而有时候需要创建自己的zval,可以参考如下代码:

{
    zval temp;
    ZVAL_LONG(&temp, 1234);
}

数组

数组作为运载其他变量的变量。内部实现上使用了众所周知的 HashTable .要创建将被返回PPHP的数组,最简单的方法:

PHP7 パラメータ、配列、Zval

做一个测试:

PHP_FUNCTION(hello_get_arr)
{
    array_init(return_value);
    add_next_index_null(return_value);
    add_next_index_long(return_value, 42);
    add_next_index_bool(return_value, 1);
    add_next_index_double(return_value, 3.14);
    add_next_index_string(return_value, "foo");
    add_assoc_string(return_value, "mno", "baz");
    add_assoc_bool(return_value, "ghi", 1);
}

PHP7 パラメータ、配列、Zval

add_*_string()函数参数从四个改为了三个。

数组遍历

假设我们需要一个取代以下功能的拓展:

<?php
function hello_array_strings($arr) {
    if (!is_array($arr)) {
        return NULL;
    }

    printf("The array passed contains %d elements\n", count($arr));

    foreach ($arr as $data) {
        if (is_string($data))
            echo $data.&#39;\n&#39;;
    }
}

php7的遍历数组和php5差很多,7提供了一些专门的宏来遍历元素(或keys)。宏的第一个参数是HashTable,其他的变量被分配到每一步迭代:

ZEND_HASH_FOREACH_VAL(ht, val)
ZEND_HASH_FOREACH_KEY(ht, h, key) 
ZEND_HASH_FOREACH_PTR(ht, ptr)
ZEND_HASH_FOREACH_NUM_KEY(ht, h) 
ZEND_HASH_FOREACH_STR_KEY(ht, key)
ZEND_HASH_FOREACH_STR_KEY_VAL(ht, key, val)
ZEND_HASH_FOREACH_KEY_VAL(ht, h, key, val)

因此它的对应函数实现如下:

PHP_FUNCTION(hello_array_strings)
{
    ulong num_key;
    zend_string *key;
    zval *val, *arr;
    HashTable *arr_hash;
    int array_count;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
        RETURN_NULL();
    }

    arr_hash = Z_ARRVAL_P(arr);
    array_count = zend_hash_num_elements(arr_hash);
    php_printf("The array passed contains %d elements\n", array_count);
    ZEND_HASH_FOREACH_KEY_VAL(arr_hash, num_key, key, val) {
        //if (key) { //HASH_KEY_IS_STRING
        //}
        PHPWRITE(Z_STRVAL_P(val), Z_STRLEN_P(val));
        php_printf("\n");
    }ZEND_HASH_FOREACH_END();
}

因为这是新的遍历方法,而我看的还是php5的处理方式,调试出上面的代码花了不少功夫,总的来说,用宏的方式遍历大大减少了编码体积。哈希表是php中很重要的一个内容,有时间再好好研究一下。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。