Heim > Artikel > Backend-Entwicklung > PHP7-Parameter, Arrays und Zvals
Erste Schritte
Jetzt können Sie einfache Funktionen deklarieren und statische oder dynamische Werte zurückgeben. Definieren Sie INI-Optionen und deklarieren Sie interne oder globale Werte. In diesem Kapitel erfahren Sie, wie Sie den Wert des vom aufrufenden Skript (PHP-Datei) übergebenen Parameters empfangen und wie der PHP-Kernel und die Zend-Engine interne Variablen bedienen.
Empfangsparameter
Im Gegensatz zum Benutzersteuerungscode werden die Parameter interner Funktionen nicht im Funktionsheader deklariert. Funktionsdeklarationen haben die Form: PHP_FUNCTION(func_name), Parameter Die Anweisung lautet nicht darunter. Parameter werden über die Adresse der Parameterliste übergeben und an jede Funktion übergeben, unabhängig davon, ob Parameter vorhanden sind.
Werfen wir einen Blick darauf, indem wir die Funktion hello_str() definieren, die einen Parameter empfängt und ihn zusammen mit dem Text der Begrüßung ausgibt.
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); }
Die meisten zend_parse_parameters()-Blöcke sehen ähnlich aus. ZEND_NUM_ARGS() teilt der Zend-Engine die Informationen über die zu verwendenden Parameter mit, um die Thread-Sicherheit sicherzustellen. Der Rückgabewert erkennt, ob es sich um SUCCESS oder FAILURE handelt. Normalerweise ist die Rückgabe ERFOLGREICH. Sofern nicht zu wenige oder zu viele Parameter übergeben werden oder die Parameter nicht in den entsprechenden Typ konvertiert werden können, gibt Zend automatisch eine Fehlermeldung aus und gibt die Kontrolle an das aufrufende Skript zurück.
Die Angabe von „s“ bedeutet, dass diese Funktion nur die Übergabe eines Parameters erwartet, der Parameter in den String-Datentyp konvertiert und die Adresse an die Variable char * übergeben wird.
Darüber hinaus gibt es eine int-Variable, die per Adresse an zend_parse_parameters() übergeben wird. Dadurch kann die Zend-Engine die Bytelänge des Strings bereitstellen, sodass binärsichere Funktionen nicht mehr auf strlen(name) angewiesen sind, um die Länge des Strings zu bestimmen. Denn die tatsächliche Verwendung von strlen(name) führt nicht einmal zum richtigen Ergebnis, da name möglicherweise NULL-Zeichen vor dem Ende der Zeichenfolge enthält.
In PHP7 wird eine weitere Möglichkeit zum Abrufen von Parametern, FAST_ZPP, bereitgestellt, um die Leistung der Parameteranalyse zu verbessern.
#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
Parametertyptabelle
Die letzten vier Typen sind alle zvals * Dies liegt daran, dass in der tatsächlichen Verwendung von PHP der zval-Datentyp verwendet wird speichert alle Benutzerbereichsvariablen. Drei „komplexe“ Datentypen: Ressourcen, Arrays und Objekte. Wenn ihre Datentypcodes in zend_parse_parameters() verwendet werden, führt die Zend-Engine eine Typprüfung durch, aber da es für sie keinen entsprechenden Datentyp in C gibt, wird keine Typkonvertierung durchgeführt.
Zval
Im Allgemeinen sind Zval- und PHP-Benutzerraumvariablen sehr problematisch und die Konzepte sind schwer zu verstehen. Bei PHP7 ist seine Struktur in Zend/zend_types.h definiert:
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; };
Wie Sie sehen können, werden Variablen über die Struktur _zval_struct gespeichert und der Wert der Variablen ist vom Typ 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;
Obwohl die Struktur groß aussieht, handelt es sich bei genauem Hinsehen tatsächlich um eine Union, eine Werterweiterung, u1 ist type_info und u2 sind verschiedene andere Hilfsfelder.
Zval-Typ
变量存储的数据是有数据类型的,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的数组,最简单的方法:
做一个测试:
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); }
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.'\n'; } }
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中很重要的一个内容,有时间再好好研究一下。