ホームページ > バックエンド開発 > PHPチュートリアル > PHP7 パラメータ、配列、Zval

PHP7 パラメータ、配列、Zval

高洛峰
リリース: 2016-11-23 15:45:42
オリジナル
2078 人が閲覧しました

はじめに

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

パラメータを受け取る

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

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

1

2

3

4

5

6

7

8

9

10

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 が提供されています。

1

2

3

#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 で定義されています:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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 型であることがわかります:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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中有定义:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/* 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():

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

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 中有定义:

1

2

3

4

5

#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,可以参考如下代码:

1

2

3

4

{

    zval temp;

    ZVAL_LONG(&temp, 1234);

}

ログイン後にコピー

数组

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

PHP7 パラメータ、配列、Zval

做一个测试:

1

2

3

4

5

6

7

8

9

10

11

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()函数参数从四个改为了三个。

数组遍历

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

1

2

3

4

5

6

7

8

9

10

11

12

13

<?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)

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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中很重要的一个内容,有时间再好好研究一下。

関連ラベル:
php
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
関連するチュートリアル
人気のおすすめ
最新のコース
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート