# 5.4 编写函数
# 5.4 编写函数
前面我们已经生成好了一份扩展框架,但它是没有什么实际作用的。一个扩展的作用可大了去了,既可以操作PHP中的变量、常量,还可以定义函数、类、方法、资源等。先让我们从函数说起吧!
### ZEND\_FUNCTION()宏函数
ZEND\_FUNCTION()宏函数也可以写成PHP\_FUNCTION(),但ZEND\_FUNCTION()更前卫、标准一些,但两者是完全相同的。
```
#define PHP_FUNCTION ZEND_FUNCTION
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FN(name) zif_##name
```
其中,zif是zend internal function的意思,zif\_前缀是可供PHP语言调用的函数在C语言中的函数名称前缀。
```
ZEND_FUNCTION(walu_hello)
{
php_printf("Hello World!\n");
}
```
上面定义了一个函数,在C语言中展开后应该是这样的:
```
void zif_walu_hello(INTERNAL_FUNCTION_PARAMETERS)
{
php_printf("Hello World!\n");
}
```
上面的展开式仅供参考,绝不推荐在编程时使用,我们应该采用宏的形式,来提高程序的兼容性与可读性。 上面的代码定义了一个可供用户在PHP语言中调用的函数实现,但现在用户还不能在程序中调用,因为这个函数还没有与用户端建立联系,也就是说虽然我们在C中完成了它的实现,但用户端PHP语言还根本不知道它的存在呢。 现在我们回头看一下5.1节中我们为扩展定义的zend\_module\_entry walu\_module\_entry(它是联系C扩展与PHP语言的重要纽带)中的“NULL, / *Functions* /”,当时我们为它赋予了NULL,是因为还没有函数,现在我们已经为它编写了函数了,便可以给它赋予一个新值了,这个值需要是zend\_function\_entry\[\]类型的,首先让我们来构造这个重要数据。
```
static zend_function_entry walu_functions[] = {
ZEND_FE(walu_hello, NULL)
{ NULL, NULL, NULL }
};
/*
下面是ZEND_FE的定义
#define ZEND_FE(name, arg_info) ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)
#define ZEND_FENTRY(zend_name, name, arg_info, flags) { #zend_name, name, arg_info, (zend_uint) (sizeof(arg_info)/sizeof(struct _zend_arg_info)-1), flags },
ZEND_FE(walu_hello, NULL)展开后便是:
{"walu_hello",zif_walu_hello,NULL, (zend_uint) (sizeof(NULL)/sizeof(struct _zend_arg_info)-1), 0 },
*/
```
其中最后的{NULL,NULL,NULL}是固定不变的。ZEND\_FE()宏函数是对我们walu\_hello函数的一个声明,如果我们有多个函数,可以直接以类似的形式添加到{NULL,NULL,NULL}之前,注意每个之间不需要加逗号。 其中的arg\_info我们现在先赋予NULL就行了,我们将在第7章讨论这个参数。确保一切无误后,我们替换掉zend\_module\_entry里的原有成员,现在应该是这样的:
```
ZEND_FUNCTION(walu_hello)
{
php_printf("Hello World!\n");
}
static zend_function_entry walu_functions[] = {
ZEND_FE(walu_hello, NULL)
{ NULL, NULL, NULL }
};
zend_module_entry walu_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"walu", //这个地方是扩展名称,往往我们会在这个地方使用一个宏。
walu_functions, /* Functions */
NULL, /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
"2.1", //这个地方是我们扩展的版本
#endif
STANDARD_MODULE_PROPERTIES
};
```
现在configure、make、make test,复制到extension dir。用下面这个命令来测试下,应该会输出hello world了,如果没有输出,说明你哪个地方做错了,查不出来的话可以给我发mail,看看是不是特例:-)
```
$ php -r 'walu_hello();'
```
## Zend Internal Functions
zif*前缀在前面我们已经说过了,代表着"Zend Internal Function",主要用来避免命名冲突,比如PHP语言中有个strlen()函数,而C语言中也有strlen()函数,所以PHP中的strlen在C中的实现不能是strlen,而应改是一个不同的名字。 但是有些时候尽管我们加了zif*前缀,还会出现一些冲突问题。比如函数名称本身是一个宏名称从而被编译器替换掉了。在这种情况下,我们需要手动来为我们扩展中的函数命名,这一步操作通过ZEND\_NAMED\_FUNCTION(diy\_walu\_hello)来代替ZEND\_FUNCTION(hello\_hello)。前者由我们指定名称,后者自己加上前缀。 如果我们在定义函数时使用了ZEND\_NAMED\_FUNCTION(),那么在walu\_functions\[\]里,我们需要用ZEND\_NAMED\_FE()宏来代替ZEND\_FE()宏。即:ZEND\_NAMED\_FE(walu\_hello,diy\_walu\_hello,NULL) 上面的技术在ext/standard/file.c用到了,我们可以看fopen()函数的定义:PHP\_NAMED\_FUNCTION(php\_if\_fopen)。但是用户端不会感觉到任何变化,还是用fopen函数来使用,因为zend\_function\_entry中每一项的第一个值代表这此函数在PHP语言中的名称。Internally, however, the function is protected from being mangled by preprocessor macros and over-helpful compilers.(原作者说的这个理由我也没看明白,请知者指点)
## Function Aliases
去PHP手册里查一下pos()函数,会得到这么一条信息:"This function is an alias of: current()";也就是说,它只是current的一个软链接而已,类似linux中的ln -s命令,理解成win下的快捷方式也成。运行pos函数,其实就是在运行current函数,转接了一下而已。这往往是因为版本升级引起的,新版本中的程序提供了某个功能的新的实现,先为原来的函数改个名,但还需要保留原来的函数名,所以这就用到了alias。这个功能可以在内核中通过ZEND\_NAMED\_FE宏来实现。
```
static zend_function_entry walu_functions[] = {
ZEND_FE(walu_hello, NULL)
ZEND_NAMED_FE(walu_hi, ZEND_FN(walu_hello), NULL)
{ NULL, NULL, NULL }
};
/*
ZEND_NAMED_FE也可以写成PHP_NAMED_FE,但推荐用前者
#define ZEND_NAMED_FE(zend_name, name, arg_info) ZEND_FENTRY(zend_name, name, arg_info, 0)
*/
```
通过ZEND\_NAMED\_FE的展开式我们了解到,它只是把PHP语言中的两个函数的名字对应到同一个C语言函数而已。 其实还有另外一种写法:
```
static zend_function_entry walu_functions[] = {
ZEND_FE(walu_hello, NULL)
ZEND_FALIAS(walu_hi,walu_hello, NULL)
{ NULL, NULL, NULL }
};
/*
#define ZEND_FALIAS(name, alias, arg_info) ZEND_FENTRY(name, ZEND_FN(alias), arg_info, 0)
*/
```
展开式是一样的,真不清楚官方鼓捣这么多同样的宏干啥。
```
<?php
walu_hi();
walu_hello();
```
## links
- 5.3 [静态编译](5.3.html)
- 5.5 [第五章小结](5.5.html)
- 介绍
- 1 PHP的生命周期
- 1.1 让我们从SAPI开始
- 1.2 PHP的启动与终止
- 1.3 PHP的生命周期
- 1.4 线程安全
- 1.5 PHP的生命周期
- 2 PHP变量在内核中的实现
- 2.1 变量的类型
- 2.2 变量的值
- 2.3 创建PHP变量
- 2.4 变量的存储方式
- 2.5 变量的检索
- 2.6 类型转换
- 2.7 小结
- 3 内存管理
- 3.1 内存管理
- 3.2 引用计数
- 3.3 内存管理
- 4 动手编译PHP
- 4.1 动手编译PHP
- 4.2 动手编译PHP
- 4.3 Unix/Linux平台下的编译
- 4.4 在Win32平台上编译PHP
- 4.5 动手编译PHP
- 5 Your First Extension
- 5.1 Your First Extension
- 5.2 编译我们的扩展
- 5.3 静态编译
- 5.4 编写函数
- 5.5 Your First Extension
- 6 函数返回值
- 6.1 函数返回值
- 6.2 引用与函数的执行结果
- 6.3 函数返回值
- 7 函数的参数
- 7.1 函数的参数
- 7.2 函数的参数
- 7.3 函数的参数
- 8 使用HashTable与{数组}
- 8.1 使用HashTable与{数组}
- 8.2 使用HashTable与{数组}
- 8.3 使用HashTable与{数组}
- 8.4 使用HashTable与{数组}
- 9 PHP中的资源类型
- 9.1 PHP中的资源类型
- 9.2 PHP中的资源类型
- 9.3 PHP中的资源类型
- 9.4 PHP中的资源类型
- 10 PHP中的面向对象(一)
- 10.1 PHP中的面向对象(一)
- 10.2 PHP中的面向对象(一)
- 10.3 PHP中的面向对象(一)
- 10.4 PHP中的面向对象(一)
- 10.5 PHP中的面向对象(一)
- 11 PHP中的面向对象(二)
- 11.1 PHP中的面向对象(二)
- 11.2 PHP中的面向对象(二)
- 11.3 PHP中的面向对象(二)
- 12 启动与终止的那点事
- 12.1 关于生命周期
- 12.2 MINFO与phpinfo
- 12.3 常量
- 12.4 PHP扩展中的全局变量
- 12.5 PHP语言中的超级全局变量(Superglobals)
- 12.6 小结
- 13 INI设置
- 13.1 声明和访问INI设置
- 13.2 小结
- 14 流式访问
- 14.1 流的概览
- 14.2 访问流
- 14.3 静态资源操作
- 14.4 links
- 15 流的实现
- 15.1 php流的表象之下
- 15.2 包装器操作
- 15.3 实现一个包装器
- 15.4 操纵
- 15.5 检查
- 15.6 小结
- 16 有趣的流
- 16.1 上下文
- 16.2 过滤器
- 16.3 小结
- 17 配置和链接
- 17.1 autoconf
- 17.2 库的查找
- 17.3 强制模块依赖
- 17.4 Windows方言
- 17.5 小结
- 18 扩展生成
- 18.1 ext_skel
- 18.2 PECL_Gen
- 18.3 小结
- 19 设置宿主环境
- 19.1 嵌入式SAPI
- 19.2 构建并编译一个宿主应用
- 19.3 通过嵌入包装重新创建cli
- 19.4 老技术新用
- 19.5 小结
- 20 高级嵌入式
- 20.1 回调到php中
- 20.2 错误处理
- 20.3 初始化php
- 20.4 覆写INI_SYSTEM和INI_PERDIR选项
- 20.5 捕获输出
- 20.6 同时扩展和嵌入
- 20.7 小结