函数的编写

输出到 PHP 用户层的函数是扩展的一个核心元素。甚至在打算编写面向对象扩展时,也建议阅读此章节,其中大部分信息对于编写方法也是有效的。

给一个扩展增加函数时,例如在使用ext_skel脚本创建初始结构后,可创建一个函数由 C 函数来实现,然后在扩展的函数表中提供一个入口。函数入口可含有一个对参数信息结构的指针。 不是必须要提供此信息,除非打算接受参数引用或返回一个引用,及提供由 PHP 的反射 API访问的信息。在下面例子中,参数不是作为直接函数参数传递的,而是通过一个堆栈(stack),由函数实现——不能直接作为信息源——进行核对。

Example #1 最小的仅有一个函数的 PHP 扩展

PHP_FUNCTION(hello_world){}ZEND_BEGIN_ARG_INFO(arginfo_hello_world, 0)ZEND_END_ARG_INFO()function_entry demo_functions[] ={PHP_FE(hello_world, arginfo_hello_world){NULL, NULL, NULL}}zend_module_entry demo_module_entry ={#if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "demo", demo_functions, NULL, NULL, NULL, NULL, NULL, #if ZEND_MODULE_API_NO >= 20010901 "1.0.0", #en STANDARD_MODULE_PROPERTIES}

在此例程中,可以你看到上面讨论的元素和模块结构。模块结构可参见The zend_module structure

此扩展的第一部分是实际的实现内容。 按照惯例,在每一个输出的函数之前用两行注释及函数后的一行短注释来描述函数的用户层模型。

为了源代码对不同版本 PHP 提供兼容性,函数声明被包装在PHP_FUNCTION宏里。编译器的预处理器会使用 PHP 5.3 将此转换为如下函数:

淘宝 adsense 商品推荐

官方正版 Google AdSense实战宝典 用谷歌广告联盟出海赚美元 第2版 祁劲松 网站运营书籍 seo搜索引擎优化 AdSense从入门到精通

价格:51.35元

void zif_hello_world(int ht, zval *return_value, zval **return_value_ptr,
                     zval *this_ptr, int return_value_used TSRMLS_DC)
{
}

为预防输出至用户层的函数与其他的同名函数间的命名冲突,输出函数的 C 符号用zif_作为前缀。你也可看到,此模型没有引用参数堆栈。如果存取 PHP 传递的参数,在下面进行描述。下方表格中列举了在INTERNAL_FUNCTION_PARAMETERS宏中定义了的 C 语言级别的参数。使用所提供的宏时,请注意这些参数可能在 PHP 版本间会发生变化。

INTERNAL_FUNCTION_PARAMETERS
名称和类型描述访问宏
int ht用户实际传递参数的数量ZEND_NUM_ARGS()
zval *return_valuePHP 变量的指针,可填充返回值传递给用户。默认值是IS_NULLRETVAL_*,RETURN_*
zval **return_value_ptr当返回引用时,PHP 将其设为变量的指针。不建议返回引用。 
zval *this_ptr假如这是一个方法调用,其指向存放$this对象的 PHP 变量。getThis()
int return_value_used指示返回值是否会被调用者使用的标志。 caller. 

淘宝 云主机 商品推荐

远程电脑云电脑出租单机单IP抖音直播游戏工作室四核物理真实主机

价格:80元

上面所述的函数什么事都不做,只是简单地返回 NULL 给用户。从 PHP 用户层可以带任意数量的参数调用此函数。更有用的函数通常由四部分组成:

  1. 局部变量定义。在 c 语言中必须在函数的开头定义局部变量。
  2. 解析的参数。php 传递参数到一个特殊的堆栈上,从那里对参数进行读取、校验类型,有问题时根据需要可进行转换。
  3. 实际逻辑。做所需做的。
  4. 设定返回值,清理,返回。

在某些情况下,这些步骤的实际顺序可能发生变化。尤其是最后两步经常颠倒,但最好保持那个顺序。

Example #2 一个简单的函数

PHP_FUNCTION(hello_world){char *name; int name_len; if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC, "s", &name, &name_len)== FAILURE){return;}php_printf("Hello %s!", name); RETURN_TRUE;}

上面的函数展示了那些部分。让我们开始分析最后几行:php_printf可以很容易的被猜到是标准的 C 语言的printf针对 PHP 的版本。与printf不同,它不是打印到进程的STDOUT通道,而是到当前的输出流,可被用户进行缓冲。要注意的是,php_printf不象大部分 PHP 的 API,它不是二进制安全的。想要二进制安全输出应使用PHPWRITE

通常来说,要直接向输出流打印数据,建议改为返回字符串数据给用户,由用户决定做什么。 此规则的例外情况可能是处理二进制数据(如图像)的函数,API 要求就在函数里提供方法来存取数据。

淘宝 SEO书 商品推荐

网店推广 中级 北京鸿科经纬科技有限公司 高等教育出版社 职业技能证书1+X证书配套理论教材SEO优化SEM推广信息流推广图书籍

价格:46.5元

最后一行的RETURN_TRUE宏做了三件事:将return_value指针变量的类型设为IS_BOOLEAN,并将其值设为true值。然后从 C 函数中返回。所以当使用此宏时,你就是内存和要结束的其他资源的管家,此函数不会执行其后的代码。

zend_parse_parameters()函数负责读取用户从参数堆栈传递来参数,并将其适当地转换后放入局部 C 语言变量。如果用户传递的参数个数有误或类型不可被转换, 函数会发出一个冗长的错误信息,并返回FAILURE。 当从函数简单返回的情况下——不修改return_value,将默认的返回值NULL返回给用户。

请注意,FAILURE表示为-1SUCCESS表示为0。为了使代码清晰,应总是使用已命名的常量而不是其值。

传递给zend_parse_parameters()的第一个参数是用户实际传递到函数的参数数量。此数值做为ht参数传给函数,但就像上面讨论的那样,应使用做为实现细节的ZEND_NUM_ARGS()

为了与 PHP 的线程隔离、线程安全资源管理器兼容,还要用TSRMLS_CC传递线程上下文。与其他函数不同,它不能是最后的参数,因为在zend_parse_parameters内要求有不定数量的参数——依赖于要读取的用户参数的数量。

在线程上下文之后定义所要求的参数。每个参数都由字符串中的一个字符表示其类型。 如果希望一个字符串参数,则在此类型说明只不过是个"s"

淘宝 建站 书 商品推荐

正版 从0到1 HTML+CSS+JavaScript快速上手 网页制作与设计教程 web html5 dw开发建站网站建设 web前端开发书籍 前端静态页面制作

价格:46.8元

最后一个是传递一个或多个指针给要填充变量值的 C 变量,或提供更多细节。比如字符串,事实上的字符串,总是以 NULL 结尾,以char*,且其长度是除 NULL 字节外的int型值。

相关所有类型说明符和对应的附加的 C 语言类型的文档可在源代码发布包中的文件README.PARAMETER_PARSING_API中找到。大多数重要类型可见下表。

zend_parse_parameters()类型说明符
修饰符附加参数的类型描述
bzend_boolBoolean 值
llonginteger(long)值
ddoublefloat(double)值
schar*, int二进制的安全串
hHashTable*数组的哈希表