之前的文章中,函数在接收的参数和返回的类型上都比较简单,但是往往实际中所遇到的都更加复杂一些。这篇文章主要说一下如何在php扩展开发中接收来自于用户空间的参数,并且对这些参数的类型、个数等信息进行相应的检查。
1. 使用zend_parse_parameters()进行自动的类型转换
在php的扩展中,最容易的得到输入参数的方法就是使用zend_parse_parameters()函数。
立即学习“PHP免费学习笔记(深入)”;
对这个函数的调用的第一个参数总是:ZEND_NUM_ARGS() TSRMLS_CC. 这个参数返回一个int型的输入参数的数目。
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;
}function sample_hello_world($name) {
echo "Hello $name!\n";
}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");
}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");
}
function sample_hello_world($name, $greeting='Mr./Ms.') {
echo "Hello $greeting $name!\n";
}sample_hello_world('Ginger Rogers','Ms.');
sample_hello_world('Fred Astaire');PHP_FUNCTION(sample_hello_world)
{
char *name;
int name_len;
char *greeting = "Mr./Mrs.";
int greeting_len = sizeof("Mr./Mrs.") - 1;//给定默认值,找出默认的长度
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s",
&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");
}立即学习“PHP免费学习笔记(深入)”;
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) {//使用zval检查为空的方式
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) {// c语言风格的检查为空的方式
val = php_sample_make_defaultval(TSRMLS_C);
}
...当一个变量传入函数的时候,不管是不是用传引用的方式,refcount总是至少为2.一个是本身,一个是传进函数的拷贝。在对这个zval进行更改之前,把它从一个非引用的集合中分离出来是很必要的。
使用/会很方便,它会自动的把任何copy-on-write引用(也就是假引用的)的变量分离出来。
这个特性跟NULL标志位一样,需要的时候才用到。
zend_get_parameters():
如果想要兼容老版本的php或只想以zval作为载体来接收参数,那么可以考虑使用zend_get_parameters()函数来接收参数
它与zend_parse_parameters()相比,直接获取,不做解析。不会自动进行类型转换,所有参数在扩展实现中的载体都是用zval的.
ZEND_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_FUNCTION(sample_onearg) {
zval **firstarg;
if (zend_get_parameters_ex(1, &firstarg) == FAILURE) {
WRONG_PARAM_COUNT;抛出一个E_WARNING级别的错误信息,并自动return。 }
/*
还有两种zend_get_parameters_**函数,专门用来解决很多或者无法提前知道参数数目的情况。php语言中的var_dump()函数,可以输入任意数量的参数。
ZEND_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<br>
程序首先获取参数数量,然后通过safe_emalloc函数申请相应大小的内存来存储这些zval**的参数。这里使用zend_get_parameters_array_ex()函数来把传递给函数的参数填充到args中。提醒一下,还存在一个zend_get_parameters_array()函数,唯一不同是它将zval*类型的参数填充到args中,并且需要ZEND_NUM_ARGS()作为参数。<br>
</p>
<p><br>
</p>
<p><br>
</p>
<p><strong>2. Arg info参数和类型的绑定</strong></p>
<div class="aritcle_card">
<a class="aritcle_card_img" href="/ai/732">
<img src="https://img.php.cn/upload/ai_manual/000/000/000/175679952967256.jpg" alt="豆包AI编程">
</a>
<div class="aritcle_card_info">
<a href="/ai/732">豆包AI编程</a>
<p>豆包推出的AI编程助手</p>
<div class="">
<img src="/static/images/card_xiazai.png" alt="豆包AI编程">
<span>483</span>
</div>
</div>
<a href="/ai/732" class="aritcle_card_btn">
<span>查看详情</span>
<img src="/static/images/cardxiayige-3.png" alt="豆包AI编程">
</a>
</div>
<p>这个arg info结构是ZE2才有的。每一个arg info声明都由一个ZEND_BEGIN_ARG_INFO()或ZEND_BEGIN_ARG_INFO_EX()宏组成,后面跟着0个或多个ZEND_ARG_*INFO(), 然后最后以ZEND_END_ARG_INFO()作为结尾。<br>
假定要重写count()函数:<br>
</p>
<p><pre class="code">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_BEGIN_ARG_INFO(php_sample_array_arginfo, 0)
ZEND_ARG_ARRAY_INFO(0, "arr", 0)
ZEND_END_ARG_INFO()
。。。 PHP_FE(sample_count_array, php_sample_array_arginfo) 。。。
而对于对象来说,也可以通过arg info进行限定:
ZEND_BEGIN_ARG_INFO(php_sample_class_arginfo, 0)
ZEND_ARG_OBJECT_INFO(1, "obj", "stdClass", 0)
ZEND_END_ARG_INFO()这里第一个参数被设为1,表示是引用方式传递,但是对象其实在ZE2中都是引用传递的。不要忘记了array和object的allow_null选项。
如果使用的是php4的话,只能用PHP_TYPE_P()进行检查,或使用convert_to_type()方法进行类型转换。
PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号