PHP_FUNCTION(set_time_limit)
{
long new_timeout;
char *new_timeout_str;
int new_timeout_strlen; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &new_timeout) == FAILURE) {
return;
} new_timeout_strlen = zend_spprintf(&new_timeout_str, 0, "%ld", new_timeout); if (zend_alter_ini_entry_ex("max_execution_time", sizeof("max_execution_time"), new_timeout_str, new_timeout_str len, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0 TSRMLS_CC) == SUCCESS) {
RETVAL_TRUE;
} else {
RETVAL_FALSE;
}
efree(new_timeout_str);
}

这是一个PHP函数  set_time_limit,zend_parse_parameters()函数的前几个参数我们直接用内核里宏来生成的

ZEND_NUM_ARGS() TSRMLS_CC 注意两者之间有个空格,但是没有逗号。ZEND_NUM_ARGS()代表着参数的个数。

紧接着需要传递给zend_parse_parameters()函数的参数是一个用于格式化的字符串,就像printf的第一个参数一样

type_spec是格式化字符串,其常见的含义如下:

 参数代表着的类型

b Boolean
l Integer 整型
d Floating point 浮点型
s String 字符串
r Resource 资源
a Array 数组
o Object instance 对象
O Object instance of a specified type 特定类型的对象
z Non-specific zval 任意类型~
Z zval**类型
f 表示函数、方法名称,PHP5.3之前没有的

这个函数就像printf()函数一样,后面的参数是与格式化字符串里的格式一一对应的。一些基础类型的数据会直接映射成C语言里的类型。

参数对应C里的数据类型
b   zend_bool
l long
d double
s char*, int 前者接收指针,后者接收长度
r zval*
a zval*
o zval*
O zval*, zend_class_entry*
z zval*
Z zval**

下面的一些字符在类型说明字符串(就是那个 char *type_spec)中具有特别的含义:

| - 表明剩下的参数都是可选参数。如果用户没有传进来这些参数值,那么这些值就会被初始化成默认值。

/ - 表明参数解析函数将会对剩下的参数以 SEPARATE_ZVAL_IF_NOT_REF() 的方式来提供这个参数的一份拷贝,除非这些参数是一个引用。

! - 表明剩下的参数允许被设定为 NULL(仅用在 a、o、O、r和z身上)。如果用户传进来了一个 NULL 值,则存储该参数的变量将会设置为 NULL。

当然啦,熟悉这个函数的最好的方法就是举个例子来说明。下面我们就来看一个例子:

/* 取得一个长整数,一个字符串和它的长度,再取得一个 zval 值 */
long l;
char *s;
int s_len;
zval *param;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"lsz", &l, &s, &s_len, &param) == FAILURE) {
return;
} /* 取得一个由 my_ce 所指定的类的一个对象,另外再取得一个可选的双精度的浮点数 */
zval *obj;
double d = 0.5;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"O|d", &obj, my_ce, &d) == FAILURE) {
return;
} /* 取得一个对象或空值,再取得一个数组 如果传递进来一个空对象,则 obj 将被设置为 NULL */
zval *obj;
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!a", &obj, &arr) == FAILURE) {
return;
} /* 取得一个分离过的数组 */
zval *arr;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a/", &arr) == FAILURE) {
return;
} /* 仅取得前 3 个参数(这对可变参数的函数很有用) */
zval *z;
zend_bool b;
zval *r;
if (zend_parse_parameters(3, "zbr!", &z, &b, &r) == FAILURE) {
return;
}


注意,在最后的一个例子中,我们直接用了数值 3 而不是 ZEND_NUM_ARGS() 来作为想要取得参数的个数。

这样如果我们的 PHP 函数具有可变参数的话我们就可以只接收最小数量的参数。当然,如果你想操作剩下的参数,你可以用 zend_get_parameters_array_ex() 来得到

这个参数解析函数还有一个带有附加标志的扩展版本,这个标志可以让你控制解析函数的某些动作。

int zend_parse_parameters_ex(int flags, int num_args TSRMLS_DC, char *type_spec, ...);

这个标志(flags)目前仅接受 ZEND_PARSE_PARAMS_QUIET 这一个值,它表示这个函数不输出任何错误信息。这对那些可以传入完全不同类型参数的函数非常有用,但这样你也就不得不自己输出错误信息

下面就是一个如何既可以接收 3 个长整形数又可以接收一个字符串的例子:

long l1, l2, l3;
char *s;
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS() TSRMLS_CC,
“lll”, &l1, &l2, &l3) == SUCCESS) {
/* manipulate longs */
} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS(), “s”, &s, &s_len) == SUCCESS) {
/* manipulate string */
} else {
php_error(E_WARNING, “%s() takes either three long values or a string as argument”,
get_active_function_name(TSRMLS_C));
return;
}

我想你通过上面的那些例子就可以基本掌握如何接收和处理参数了。如果你想看更多的例子,请翻阅 PHP 源码包中那些自带的扩展的源代码,那里面包含了你可能遇到的各种情况。

以前的老式的获取参数的的方法(不推荐)

获取函数参数这件事情我们还可以通过 zend_get_parameters_ex() 来完成(不推荐使用这些旧式的 API,我们推荐您使用前面所述的新式的参数解析函数):

zval **parameter;
if(zend_get_parameters_ex(1, &parameter) != SUCCESS)
WRONG_PARAM_COUNT;

所有的参数都存储在一个二次指向的 zval 容器里面(其实就是一个 zval* 数组,译者注)。上面的这段代码尝试接收 1 个参数并且将其保存在 parameter 所指向的位置。

zend_get_parameters_ex() 至少需要两个参数。第一个参数表示我们想要接收参数的个数(这个值通常是对应于 PHP 函数参数的个数,由此也可以看出事先对调用语法正确性的检查是多么重要)。第二个参数(包括剩下的所有参数)指向一个二次指向 zval 的指针。(即 ***zval,是不是有点糊涂了?^_^)这些指针是必须的,因为 Zend 内部是使用 **zval 进行工作的。为了能被在我们函数内部定义的 **zval 局部变量所访问,我们就必须在用一个指针来指向它。

zend_get_parameters_ex() 的返回值可以是 SUCCESSFAILURE,分别表示参数处理的成功或失败。如果处理失败,那最大的可能就是由于没有指定一个正确的参数个数。如果处理失败,则应该使用宏 WRONG_PARAM_COUNT 来退出函数。

如果想接收更多的的参数,可以用类似下面一段的代码来处理:

zval **param1, **param2, **param3, **param4;
if(zend_get_parameters_ex(4, &param1, &param2, &param3, &param4) != SUCCESS)
WRONG_PARAM_COUNT;

zend_get_parameters_ex() 仅检查你是否在试图访问过多的参数。如果函数有 5 个参数,而你仅仅接收了其中的 3 个,那么你将不会收到任何错误信息,zend_get_parameters_ex() 仅返回前三个参数的值。再次调用 zend_get_parameters_ex() 也不会获得剩下两个参数的值,而还是返回前三个参数的值。

接收可变(可选)参数

如果你想接收一些可变参数,那用前面我们刚刚讨论的方法就不太合适了,主要是因为我们将不得不为每个可能的参数个数来逐行调用 zend_get_parameters_ex(),显然这很不爽。

为了解决这个问题,我们可以借用一下 zend_get_parameters_array_ex() 这个函数。它可以帮助我们接收不定量的参数并将其保存在我们指定的地方:

zval **parameter_array[4];

/* 取得参数个数 */
argument_count = ZEND_NUM_ARGS(); /* 看一下参数个数是否满足我们的要求:最少 2 个,最多 4个。 */
if(argument_count < 2 || argument_count > 4)
WRONG_PARAM_COUNT;
/* 参数个数正确,开始接收。 */
if(zend_get_parameters_array_ex(argument_count, parameter_array) != SUCCESS)
WRONG_PARAM_COUNT;

让我们来看看这几行代码。首先代码检查了传入参数的个数,确保在我们可接受的范围内;然后就调用 zend_get_parameters_array_ex() 把所有有效参数值的指针填入 parameter_array

我们可以在 fsockopen() 函数(位于ext/standard/fsock.c )中找到一个更为漂亮的实现。代码大致如下,你也不用担心还没有弄懂全部的函数,因为我们很快就会谈到它们。

PHP中带有可变参数的 fsockopen() 函数的实现
pval **args[5];
int *sock=emalloc(sizeof(int));
int *sockp;
int arg_count=ARG_COUNT(ht);
int socketd = -1;
unsigned char udp = 0;
struct timeval timeout = { 60, 0 };
unsigned short portno;
unsigned long conv;
char *key = NULL;
FLS_FETCH();
if (arg_count > 5 || arg_count < 2 || zend_get_parameters_array_ex(arg_count,args)==FAILURE) {
CLOSE_SOCK(1);
WRONG_PARAM_COUNT;
}
switch(arg_count) {
case 5:
convert_to_double_ex(args[4]);
conv = (unsigned long) (Z_DVAL_PP(args[4]) * 1000000.0);
timeout.tv_sec = conv / 1000000;
timeout.tv_usec = conv % 1000000;
/* fall-through */
case 4:
if (!PZVAL_IS_REF(*args[3])) {
php_error(E_WARNING,”error string argument to fsockopen not passed by reference”);
}
pval_copy_constructor(*args[3]);
ZVAL_EMPTY_STRING(*args[3]);
/* fall-through */
case 3:
if (!PZVAL_IS_REF(*args[2])) {
php_error(E_WARNING,”error argument to fsockopen not passed by reference”);
return;
}
ZVAL_LONG(*args[2], 0);
break;
}
convert_to_string_ex(args[0]);
convert_to_long_ex(args[1]);
portno = (unsigned short) Z_LVAL_P(args[1]);
key = emalloc(Z_STRLEN_P(args[0]) + 10);

fsockopen() 可以接收 2-5 个参数。在必需的变量声明之后便开始检查参数的数量范围。然后在一个 switch 语句中使用了贯穿(fall-through)法来处理这些的参数。这个 switch 语句首先处理最大的参数个数(即 5),随后依次处理了参数个数为 4 和 3 的情况,最后用 break 关键字跳出 switch 来忽略对其他情况下参数(也就是只含有 2 个参数情况)的处理。这样在经过 switch 处理之后,就开始处理参数个数为最小时(即 2)的情况。

这种像楼梯一样的多级处理方法可以帮助我们很方便地处理一些可变参数。

存取参数

为 了存取一些参数,让每个参数都具有一个明确的(C)类型是很有必要的。但 PHP是一种动态语言,PHP 从不做任何类型检查方面的工作,因此不管你想不想,调用者都可能会把任何类型的数据传到你的函数里。比如说,如果你想接收一个整数,但调用者却可能会给你 传递个数组,反之亦然-PHP 可不管这些的。

为了避免这些问题,你就必须用一大套 API 函数来对传入的每一个参数都做一下强制性的类型转换。(见表3.4 参数类型转换函数)

注意: 所有的参数转换函数都以一个 **zval 来作为参数。

关于zend_parse_parameters函数的更多相关文章

  1. php调用C代码的方法详解和zend_parse_parameters函数详解

    php调用C代码的方法详解 在php程序中需要用到C代码,应该是下面两种情况: 1 已有C代码,在php程序中想直接用 2 由于php的性能问题,需要用C来实现部分功能   针对第一种情况,最合适的方 ...

  2. [PHP源码阅读]strlen函数

    文章来自:http://www.hoohack.me/2016/02/22/phps-source-analytics-strlen 我在github有对PHP源码更详细的注解.感兴趣的可以围观一下, ...

  3. c扩展调用php的函数(调用实现php函数的c函数)

    上一次是写的c扩展调用c的标准函数,但是只能调用头文件中申明的函数,今天来说下c扩展调用实现php函数的c函数,比方说,c扩展要用到php中ip2long这个函数,但是c不可能去php中调用,肯定是去 ...

  4. PHP源码阅读(一):str_split函数

    注:源码版本:php5.6.33. 函数简介 str_split 原型: array str_split ( string $string [, int $split_length = 1 ] ) 说 ...

  5. php扩展开发-函数

    我们首先找到快速上手文章里面关于函数定义的代码,以此说明然后开发PHP的函数 //php_myext.h PHP_FUNCTION(myext_hello);//函数申明,所有在myext.c文件定义 ...

  6. [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)

    文章来自:http://www.hoohack.me/2016/02/15/understanding-phps-internal-array-implementation-ch 原文:https:/ ...

  7. 【译】PHP的变量实现(给PHP开发者的PHP源码-第三部分)

    文章来自:http://www.aintnot.com/2016/02/12/phps-source-code-for-php-developers-part3-variables-ch 原文:htt ...

  8. [译] 理解PHP内部函数的定义(给PHP开发者的PHP源码-第二部分)

    文章来自:http://www.hoohack.me/2016/02/10/understanding-phps-internal-function-definitions-ch 原文:https:/ ...

  9. 用扩展开发一个PHP类

    原文:http://my.oschina.net/mickelfeng/blog/122519?p=1 假设我们要用PHP扩展实 现一个类Person,它有一个private的成员变量$_name和两 ...

随机推荐

  1. 使用autotools系列工具自动部署源代码编译安装

    在Linux系统下开发一个较大的项目,完全手动建立Makefile是一件费力而又容易出错的工作.autotools系列工具只需用户输入简单的目标文件.依赖文件.文件目录等就可以比较轻松地生成Makef ...

  2. Linux - Ubuntu下JDK配置

    系统版本: ubuntu 14.04 x64JDK版本: jdk-8u60-linux-x64 1.查看系统位数,输入以下命令即可 getconf LONG_BIT 2.下载对应的JDK文件,我这里下 ...

  3. JS 获取 本周、本月、本季度、本年、上月、上周、上季度、去年

    工具类定义: /** * 日期范围工具类 */ var dateRangeUtil = (function () { /*** * 获得当前时间 */ this.getCurrentDate = fu ...

  4. Winform开发框架之字典管理模块的更新,附上最新2013年全国最新县及县以上行政区划代码sql脚本

    在很多项目里面,字典管理是必备的项目模块,而这个又是比较通用的功能,因此可以单独做成一个通用字典管理,例如这个模块,可以通过集成的方式,使用在我的<Winform开发框架>.<WCF ...

  5. WPF中实现自定义虚拟容器(实现VirtualizingPanel)

    WPF中实现自定义虚拟容器(实现VirtualizingPanel) 在WPF应用程序开发过程中,大数据量的数据展现通常都要考虑性能问题.有下面一种常见的情况:原始数据源数据量很大,但是某一时刻数据容 ...

  6. AxWebBrowser与WebBrowserU盾登陆时的使用

    PS:上个月为财务小妹做了个自动上传报表的工具,财务妹子表示调戏我很开心T_T~~.   由于该小程序涉及到登陆,准备用WebBroswer,这一下撞墙上了,无法展示U盾密码框.   我在博问上的问题 ...

  7. C语言范例学习01

    编程语言的能力追求T型. 以前学过C语言,但是只学了理论. 从今天开始,我买了本<C语言程序开发范例宝典>.我要把它通关掉. 这应该可以极大地提升我的编程能力. 第一章 基础知识 这章没太 ...

  8. 四、MyBatis主配置文件

    //备注:该博客引自:http://limingnihao.iteye.com/blog/1060764 在定义sqlSessionFactory时需要指定MyBatis主配置文件: Xml代码 收藏 ...

  9. js中的浅拷贝和深拷贝

    说说最近所学:浅拷贝和深拷贝也叫做浅克隆和深克隆,深浅主要针对的是对象的"深度",常见的对象都是"浅"的,也就是对象里的属性就是单个的属性,而"深&q ...

  10. DOJO官方API翻译或解读-dojo/_base/lang --hitch()

    hitch() hitch() 是一个函数,会在给定的上下中执行给定一个执行函数.hitch允许你去控制一个函数如何执行,往往在异步操作中起作用. 我们常常会写出这样的代码:(博主:这个代码意图在&q ...