Heim >Backend-Entwicklung >PHP-Tutorial >[Übersetzung] [PHP-Erweiterungsentwicklung und eingebettet] Kapitel 7 – Akzeptieren von Parametern
Rechteerklärung
Diese Übersetzung kann ohne Einschränkung und ohne Gewinn frei verbreitet werden.
Mit Ausnahme einiger „Vorschau“-Ausnahmen sind die Erweiterungsfunktionen, mit denen Sie sich bisher befasst haben, sehr einfach. Geben Sie einfach zurück. Die meisten Funktionen haben jedoch nicht nur einen Zweck. Sie übergeben normalerweise einige Parameter und hoffen, eine nützliche Antwort basierend auf dem Wert und anderen zusätzlichen Verarbeitungen zu erhalten.
Automatische Typkonvertierung für zend_parse_parameters()
Wie bei den Rückgabewerten, die Sie im vorherigen Kapitel gesehen haben, handelt es sich auch bei den Parameterwerten um den indirekten Zugriff auf die zval-Referenz. Der einfachste Weg, die Werte dieser zval* zu erhalten, ist die Verwendung die Funktion zend_parse_parameters().
Der Aufruf von zend_parse_parameters() beginnt fast immer mit dem Makro ZEND_NUM_ARGS(), gefolgt vom allgegenwärtigen Makro TSRMLS_CC. Wie Sie anhand des Namens erraten können, gibt ZEND_NUM_ARGS() die tatsächliche Anzahl der übergebenen Parameter zurück int Typ. Da zend_parse_parameters() intern funktioniert, müssen Sie diesen Wert wahrscheinlich nicht direkt kennen, also müssen Sie ihn vorerst nur übergeben. Der nächste Parameter von zend_parse_parameters() ist Format-String-Parameter, der aus den von der Zend-Engine unterstützten grundlegenden Typbeschreibungszeichen besteht. Eine Folge von Zeichen, die zur Beschreibung der zu akzeptierenden Funktionsparameter verwendet werden. Die folgende Tabelle enthält die grundlegenden Typzeichen:
zend_parse_parameters() Die verbleibenden Parameter hängen von der angegebenen Typbeschreibung ab. Bei einfachen Typen wird der lange Datentyp beispielsweise wie folgt gelöst >
Obwohl der Speicherplatz von Integer und Long normalerweise gleich ist, sind sie nicht vollständig konsistent. Der Versuch, Daten vom Typ int auf einen Parameter vom Typ long * zu dereferenzieren, kann insbesondere auf 64-Bit-Plattformen zu unerwünschten Ergebnissen führen . Es ist fehleranfälliger. Bitte verwenden Sie daher ausschließlich die Spalten in der folgenden Tabelle außerhalb des Datentyps.
PHP_FUNCTION(sample_getlong) { long foo; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &foo) == FAILURE) { RETURN_NULL(); } php_printf("The integer value of the parameter you " "passed is: %ld\n", foo); RETURN_TRUE; }
Beachten Sie, dass alle anderen komplexen Typen tatsächlich in einfache aufgelöst werden Der Grund dafür und die Nichtverwendung von RETURN_*()-Makros zur Rückgabe komplexer Datentypen liegen in der Unfähigkeit, diese Strukturen im C-Raum wirklich zu emulieren. Was zend_parse_parameters() für Ihre Funktion tun kann, ist sicherzustellen, dass die zval * Sie erhalten den richtigen Typ. Bei Bedarf wird sogar eine implizite Typkonvertierung durchgeführt, z. B. die Konvertierung eines Arrays in ein Objekt der Typen stdClass und O, da diese separat erläutert werden müssen In Kapitel 10 „php4-Objekt“ und Kapitel 11 „php5-Objekt“ erfahren Sie mehr über O. Für den Typ s erweitern wir die Funktion „sample_hello_world()“ in Kapitel 5 „Ihre erste Erweiterung“. damit es mit dem angegebenen Namen verwendet werden kann.
Die Funktion zend_parse_parameters() schlägt möglicherweise fehl, weil die Funktion zu wenige Parameter übergibt, um die Formatzeichenfolge zu erfüllen, oder weil einer der Parameter dies nicht kann In diesem Fall wird eine Fehlermeldung automatisch ausgegeben, sodass Ihre Erweiterung dies nicht tun muss.
Um mehr als einen Parameter anzufordern, müssen Sie die Formatzeichenfolge erweitern um zusätzliche Zeichen einzuschließen und die anderen Parameter nach dem Aufruf von zend_parse_parameters() zu unterdrücken. Parameter verhalten sich genauso, wie sie in Benutzerraumfunktionen definiert sind, zusätzlich zu den Im Grundtyp werden 3 Metazeichen zum Ändern der Parameterverarbeitungsmethode verwendet:
function sample_hello_world($name) { echo "Hello $name!\n"; } /* 在C中, 你将使用zend_parse_parameters()函数接受一个字符串 */ PHP_FUNCTION(sample_hello_world) { char *name; int name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { RETURN_NULL(); } php_printf("Hello "); PHPWRITE(name, name_len); php_printf("!\n"); }
Optionale Parameter
function sample_hello_world($name, $greeting) { echo "Hello $greeting $name!\n"; } sample_hello_world('John Smith', 'Mr.'); Or: PHP_FUNCTION(sample_hello_world) { char *name; int name_len; char *greeting; int greeting_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &name, &name_len, &greeting, &greeting_len) == FAILURE) { RETURN_NULL(); } php_printf("Hello "); PHPWRITE(greeting, greeting_len); php_printf(" "); PHPWRITE(name, name_len); php_printf("!\n"); }Schauen wir uns das an Das überarbeitete Beispiel „sample_hello_world()“ besteht darin, einen optionalen $greeting-Parameter hinzuzufügen:
sample_hello_world() kann jetzt nur mit dem Parameter $name oder mit beiden Parametern aufgerufen werden
Wenn der zweite Parameter nicht übergeben wird, wird der Standardwert verwendet. In der C-Sprachimplementierung werden auch optionale Parameter auf ähnliche Weise angegeben Um diese Funktion abzuschließen Verwenden Sie das Pipe-Zeichen (|) in der Formatzeichenfolge zend_parse_parameters(). Die Parameter auf der linken Seite des Pipe-Zeichens werden aus dem Aufrufstack analysiert. Wenn die Parameter rechts von allen Pipe-Zeichen nicht im Aufrufstack bereitgestellt werden nicht geändert werden (die entsprechenden Parameter nach der Formatzeichenfolge zend_parse_parameters()). Daher ist es sehr wichtig, einen anfänglichen Standardwert für den optionalen Parameter festzulegen. In den meisten Fällen ist der anfängliche Standardwert NULL/0, aber manchmal, beispielsweise im obigen Beispiel, sind andere sinnvolle Werte der Standardwert . IS_NULL vs. NULLfunction sample_hello_world($name, $greeting='Mr./Ms.') { echo "Hello $greeting $name!\n"; }
Jeder zval, selbst ein sehr einfacher IS_NULL-Typ, benötigt einige (CPU-)Taktzyklen, um Speicherplatz zuzuweisen und zu initialisieren den Wert und geben Sie ihn schließlich frei, wenn er nicht mehr benötigt wird. Für viele Funktionen ist die Verwendung von NULL-Parametern im Aufrufbereich nicht wichtig, daher ist dieser Vorgang glücklicherweise bedeutungslos. Ermöglicht die Markierung von Parametern als „NULL zulassen“. Wenn nach einem Formatbeschreibungszeichen ein Ausrufezeichen (!) hinzugefügt wird, bedeutet dies, dass, wenn der entsprechende Parameter NULL übergeben wird, die entsprechenden Parameter beim Aufruf von zend_parse_parameters() auf echten NULL gesetzt werden Betrachten Sie die folgenden zwei Codeteile, einen mit diesem Modifikator und einen ohne:
PHP_FUNCTION(sample_arg_fullnull) { zval *val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &val) == FAILURE) { RETURN_NULL(); } if (Z_TYPE_P(val) == IS_NULL) { val = php_sample_make_defaultval(TSRMLS_C); } ... PHP_FUNCTION(sample_arg_nullok) { zval *val; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z!", &val) == FAILURE) { RETURN_NULL(); } if (!val) { val = php_sample_make_defaultval(TSRMLS_C); } ...
这两个版本的代码其实没什么不同, 前者表面上看起来需要更多的处理时间. 通常, 这个特性并不是很有用, 但最好还是知道有这么回事.
强制隔离
当一个变量传递到一个函数中后, 无论是否是引用传值, 它的refcount至少是2; 一个是变量自身, 另外一个是传递给函数的拷贝. 因此在修改zval之前(如果直接在参数传递进来的zval上), 将它从它所属的非引用集合中分离出来非常重要.
如果没有"/"格式修饰符, 这将是一个单调重复的工作. 这个格式修饰符自动的隔离所有写时拷贝的引用传值参数, 这样, 你的函数中就可以为所欲为了. 和NULL标记一样, 这个修饰符放在它要修饰的格式描述字符后面. 同样和NULL标记一样, 直到你真的有使用它的地方, 你可能才知道你需要这个特性.
zend_get_arguments()
如果你正在设计的代码计划在非常旧的php版本上工作, 或者你有一个函数, 它只需要zval *, 就可以考虑使用zend_get_parameters()的API调用.
zend_get_parameters()调用与它对应的新版本有一点不同. 首先, 它不会自动执行类型转换; 而是所有期望的参数都是zval *数据类型. 下面是zend_get_parameters()的最简单用法:
PHP_FUNCTION(sample_onearg) { zval *firstarg; if (zend_get_parameters(ZEND_NUM_ARGS(), 1, &firstarg) == FAILURE) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected at least 1 parameter."); RETURN_NULL(); } /* Do something with firstarg... */ }
其次, 你可能已经注意到, 它需要手动处理错误消息, zend_get_parameters()并不会在失败的时候输出错误文本. 它对可选参数的处理也很落后. 如果你让它抓取4个参数, 最好至少给它提供4个参数, 否则可能返回FAILURE.
最后, 它不像parse, 这个get变种会自动的隔离所有写时复制的引用集合. 如果你想要跳过自动隔离, 就要使用它的兄弟接口: zend_get_parameters_ex().
除了不隔离写时复制集合, zend_get_parameters_ex()还有一个不同点是返回zval **指针而不是直接的zval *. 它们的差别同样可能直到你使用的时候才知道需要它们, 不过它们最终的使用非常相似:
PHP_FUNCTION(sample_onearg) { zval **firstarg; if (zend_get_parameters_ex(1, &firstarg) == FAILURE) { WRONG_PARAM_COUNT; } /* Do something with firstarg... */ }
要注意的是_ex版本不需要ZEND_NUM_ARGS()参数. 这是因为增加_ex的版本时已经比较晚了, 当时Zend引擎已经不需要这个参数了.
在这个例子中, 你还使用了WRONG_PARAM_COUNT宏, 它用来处理E_WARNING错误消息的显示已经自动的离开函数.
处理任意数目的参数
还有两个zend_get_parameters()族的函数, 用于解出zval *和zval **指针集合, 它们适用于有很多参数或运行时才知道参数个数的引用场景.
考虑var_dump()函数, 它用来展示传递给它的任意数量的变量的内容:
PHP_FUNCTION(var_dump) { int i, argc = ZEND_NUM_ARGS(); zval ***args; args = (zval ***)safe_emalloc(argc, sizeof(zval **), 0); if (ZEND_NUM_ARGS() == 0 || zend_get_parameters_array_ex(argc, args) == FAILURE) { efree(args); WRONG_PARAM_COUNT; } for (i=0; i<argc; i++) { php_var_dump(args[i], 1 TSRMLS_CC); } efree(args); }
这里, var_dump()预分配了一个传给函数的参数个数大小的zval **指针向量. 接着使用zend_get_parameters_array_ex()一次性将参数摊入到该向量中. 你可能会猜到, 存在这个函数的另外一个版本: zend_get_parameters_array(), 它们仅有一点不同: 自动隔离, 返回zval *而不是zval **, 在第一个参数上要求传递ZEND_NUM_ARGS().
参数信息和类型暗示
在上一章已经简短的向你介绍了使用Zend引擎2的 参数信息结构进行类型暗示的概念. 我们应该记得, 这个特性是针对ZE2(Zend引擎2)的, 对于php4的ZE1(Zend引擎1)不可用.
我们重新从ZE2的参数信息结构开始. 每个参数信息都使用ZEND_BEGIN_ARG_INFO()或ZEND_BEGIN_ARG_INFO_EX()宏开始, 接着是0个或多个ZEND_ARG_*INFO()行, 最后以ZEND_END_ARG_INFO()调用结束.
这些宏的定义和基本使用可以在刚刚结束的第6章"返回值"的编译期引用传值一节中找到.
假设你想要重新实现count()函数, 你可能需要创建下面这样一个函数:
PHP_FUNCTION(sample_count_array) { zval *arr; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) { RETURN_NULL(); } RETURN_LONG(zend_hash_num_elements(Z_ARRVAL_P(arr))); }
zend_parse_parameters()会做很多工作, 以保证传递给你的函数是一个真实的数组. 但是, 如果你使用zend_get_parameters()函数或某个它的兄弟函数, 你就需要自己在函数中做类型检查. 当然, 你也可以使用类型暗示! 通过定义下面这样一个arg_info结构:
static ZEND_BEGIN_ARG_INFO(php_sample_array_arginfo, 0) ZEND_ARG_ARRAY_INFO(0, "arr", 0) ZEND_END_ARG_INFO()
并在你的php_sample_function结构中声明该函数时使用它:
PHP_FE(sample_count_array, php_sample_array_arginfo)
这样就将类型检查的工作交给了Zend引擎. 同时你给了你的参数一个名字(arr), 它可以在产生错误消息时被使用, 这样在使用你API的脚本发生错误时, 更加容易跟踪问题.
在第6章第一次介绍参数信息结构时, 你可能注意到了对象, 也可以使用ARG_INFO宏进行类型暗示. 只需要在参数名后增加一个额外的参数来说明类型名(类名)
static ZEND_BEGIN_ARG_INFO(php_sample_class_arginfo, 0) ZEND_ARG_OBJECT_INFO(1, "obj", "stdClass", 0) ZEND_END_ARG_INFO()
要注意到这里的第一个参数(by_ref)被设置为1. 通常来说这个参数对对象来说并不是很重要, 因为ZE2中所有的对象默认都是引用方式的, 对它们的拷贝必须显式的通过clone来实现. 在函数调用内部强制clone可以做到, 但是它和强制引用完全不同.
Denn wenn das Flag zend.ze1_compatiblity_mode gesetzt ist, ist Ihnen möglicherweise die by_ref-Einstellung der ZEND_ARG_OBJECT_INFO-Zeile wichtig. In diesem speziellen Fall wird dem Objekt möglicherweise immer noch eine Kopie anstelle einer Referenz übergeben Das Objekt, das Sie möglicherweise benötigen, ist eine echte Referenz, Sie müssen sich also keine Gedanken über die Auswirkungen machen, wenn Sie dieses Flag setzen.
Vergessen Sie nicht, dass es im Parameterinformationsmakro eine Option „allow_null“ gibt Einzelheiten zum Zulassen von NULL finden Sie im Abschnitt „Übergabe von Werten zur Kompilierungszeit“
Natürlich wird die Verwendung von Parameterinformationen für Typhinweise nur in ZE2 unterstützt Wenn Sie möchten, dass die von Ihnen betrachtete Erweiterung mit PHP4 kompatibel ist, müssen Sie zend_get_parameters() verwenden. Auf diese Weise kann die Typüberprüfung nur innerhalb der Funktion platziert werden und kann manuell durch Testen von Z_TYPE_P(Wert) oder mithilfe von abgeschlossen werden die in Kapitel 2 gezeigte Methode „convert_to_type()“ für die automatische Typkonvertierung.
Zusammenfassung
Jetzt sind Ihre Hände möglicherweise etwas chaotisch. Der Funktionscode für die Kommunikation mit dem Benutzerbereich wird durch einfache Eingaben implementiert. Ausgabefunktionen. Sie haben ein tieferes Verständnis des Referenzzählsystems von zval und haben gelernt, wie Sie Steuervariablen an Ihre internen Funktionsmethoden und das Timing übergeben.
Im nächsten Kapitel lernen Sie den Array-Datentyp kennen und verstehen, wie der Die Darstellung des User-Space-Arrays ist der internen HashTable-Implementierung zugeordnet. Darüber hinaus werden Sie eine große Anzahl optionaler Zend- und PHP-API-Funktionen sehen, die zum Bearbeiten dieser komplexen Strukturen verwendet werden. [Entwicklung und Einbettung von PHP-Erweiterungen] Kapitel 7 – Akzeptieren von Parametern Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!