[翻译][php扩展和嵌入式]第7章-接受参数
全部翻译内容pdf文档下载地址: http://download.gaodaima.com/detail/lgg201/5107012
本书目前在github上由laruence(http://www.laruence.com)和walu(http://www.walu.cc)两位大牛组织翻译. 该翻译项目地址为: https://github.com/walu/phpbook
本书在github上的地址: https://github.com/goosman-lei/php-eae
未来本书将可能部分合并到phpbook项目中, 同时保留一份独立版本.
原书名:
原作者: Sara Golemon
译者: goosman.lei(雷果国)
译者Email: [email protected]
译者Blog: http://blog.gaodaima.com/lgg201
权利声明
此译本在不获利的情况下, 可以无限制自由传播.
除了几个”预览”的例外, 你迄今处理的扩展函数都很简单, 只有返回. 然而, 多数函数并非只有一个目的. 你通常会传递一些参数, 并希望接收到基于值和其他附加处理的有用的响应.
zend_parse_parameters()的自动类型转换
和上一章你看到的返回值一样, 参数的值也是围绕着对zval引用的间访展开的. 获取这些zval*的值最简单的方法就是使用zend_parse_parameters()函数.
调用zend_parse_parameters()几乎总是以ZEND_NUM_ARGS()宏接着是无所不在的TSRMLS_CC. ZEND_NUM_ARGS()从名字上可以猜到, 它返回int型的实际传递的参数个数. 由于zend_parse_parameters()内部工作的方法, 你可能不需要直接了解这个值, 因此现在只需要传递它.
zend_parse_parameters()的下一个参数是格式串参数, 它是由Zend引擎支持的基础类型描述字符组成的字符序列, 用来描述要接受的函数参数. 下表是基础的类型字符:
类型字符 |
用户空间数据类型 |
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 |
Dereferenced non-specific zval |
zend_parse_parameters()剩下的参数依赖于你的格式串中所指定的类型描述. 对于简单类型, 直接解引用为C语言的基础类型. 例如, long数据类型如下解出:
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;}
尽管integer和long的存储空间通常是一样大的, 但它们并不完全一致. 尝试将一个int类型的数据解引用到long *参数可能会带来不期望的结果, 尤其是在64位平台上更加容易出错. 因此, 请严格使用下表中列出的数据类型.
类型 |
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 * |
注意, 所有其他的复杂类型实际上都解析为简单的zval. 这样做的原因和不使用RETURN_*()宏返回复杂数据类型一样, 都是受限于无法真正的模拟C空间中的这些结构. zend_parse_parameters()能为你的函数所做的, 是确保你接收到的zval *是正确的类型. 如果需要, 它甚至会执行隐式的类型转换, 比如将数组转换为stdClass的对象.
s和O类型需要单独说明, 因为它们一次调用需要两个参数. 在第10章”php4对象”和第11章”php5对象”中你将更进一部的了解O. 对于s这个类型, 我们对第5章”你的第一个扩展”的sample_hello_world()函数进行一次扩展, 让它可以跟指定的人名打招呼.
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");}
zend_parse_parameters()函数可能由于函数传递的参数太少不能满足格式串, 或者因为其中某个参数不能转换为请求的类型而失败. 这种情况下, 它将自动的输出错误消息, 因此你的扩展不需要这样做.
要请求超过1个参数, 就需要扩充格式串, 包括其他字符, 并将zend_parse_parameters()调用的后面其他参数压栈. 参数和它们在用户空间函数定义的行为一致, 从左向右解析.
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");}
除了基础类型, 还有3个元字符用于修改参数处理方式. 如下表所示:
类型修改符 |
含义 |
| |
接下来是可选参数了.当指定它时,所有之前的参数都被认为是必须的,所有后续的参数都被认为是可选的. |
! |
!之前的一个修饰符对应的参数如果是NULL,提供的内部变量将被设置为真实的NULL指针. |
/ |
/之前的一个修饰符对应的参数指定为写时拷贝,它将自动的隔离到新的zval(is_6来源gaodaimacom搞#^代%!码网搞gaodaima代码ref = 0, refcount = 1) |
可选参数
我们再来看一看修订版的sample_hello_world()示例, 下一步是增加一个可选的$greeting参数:
function sample_hello_world($name, $greeting='Mr./Ms.') { echo "Hello $greeting $name!\n";}
sample_hello_world()现在可以只用$name参数调用, 也可以同时使用两个参数调用.
sample_hello_world('Ginger Rogers','Ms.');sample_hello_world('Fred Astaire');
当不传递第二个参数时, 使用默认值. 在C语言实现中, 可选参数也是以类似的方式指定.
要完成这个功能, 我们需要使用zend_parse_parameters()格式串中的管道符(|). 管道符左侧的参数从调用栈解析, 所有管道符右边的参数如果在调用栈中没有提供, 则不会被修改(zend_parse_parameters()格式串后面对应的参数). 例如: