PHP扩展开发 - 创建第一个PHP扩展函数

###创建第一个PHP扩展函数

在PHP扩展中,创建一个函数主要需要经过三步:

  1. 在源文件(.c)中使用PHP_FUNCTION宏创建函数实现,并头文件中声明该函数
  2. 使用PHP_FE告诉zend_function_entry结构体新创建的函数的地址
  3. zend_function_entry结构体注册到zend_module_entry扩展入口结构体上,只有 创建第一个函数的时候需要这样做。

接下来,我们对这三个步骤展开,并且辅以一个名为demo_array()的函数作为例子,该函数返回一个 我们在扩展函数中创建的数组作为返回值。

在讲解如何创建一个扩展函数之前,我们需要创建一个扩展的基本骨架,创建扩展的基本骨架请参考 PHP扩展开发 - 构建第一个PHP扩展

PHP扩展开发 - 构建第一个PHP扩展中,我们创建了一个名为ext_demo_1的扩展程序,进入扩展目录, 我们将看到如下文件:

/vagrant/ext/ext_demo_1$ ls
config.m4   CREDITS       ext_demo_1.c    php_ext_demo_1.h
config.w32  EXPERIMENTAL  ext_demo_1.php  tests

首先,我们需要在ext_demo_1.c文件中,创建函数的实现:

PHP_FUNCTION(demo_array)
{
	zval *subarray;/* 子数组 */

	array_init(return_value); /* 将函数返回值初始化为数组类型 */

  /* 返回数组中添加三个值:life=>42, 123=>1, 124=>3.1415926 */
	add_assoc_long(return_value, "life", 42);
	add_index_bool(return_value, 123, 1);
	add_next_index_double(return_value, 3.1415926);

  /* 添加两个字符串值: 125=> Foo, 126=> Bar */
	add_next_index_string(return_value, "Foo", 1);
	add_next_index_string(return_value, estrdup("Bar"), 0);

  /* 初始化zval结构体,分配内存空间 */
	MAKE_STD_ZVAL(subarray);
	array_init(subarray);

	add_next_index_long(subarray, 1);
	add_next_index_long(subarray, 20);
	add_next_index_long(subarray, 132);

  /* 将subarray添加到返回值 */
	add_index_zval(return_value, 444, subarray);
}

创建函数体之后,我们需要在头文件php_ext_demo_1.h中声明该函数。

PHP_FUNCTION(demo_array);

第二步是告诉zend_function_entry结构体函数的地址。在ext_demo_1.c文件的第 41 行左右, 我们可以看到zend_function_entry结构体变量,将函数通过PHP_FE宏添加到该变量数组中。

const zend_function_entry ext_demo_1_functions[] = {
	PHP_FE(confirm_ext_demo_1_compiled,	NULL)		/* For testing, remove later. */
	PHP_FE(demo_array, NULL) /* 在这里添加demo_array函数 */
	PHP_FE_END	/* Must be the last line in ext_demo_1_functions[] */
};

一般来说,如果使用的是ext_skel创建的扩展骨架的话,一个函数就算是添加完成了,因为第三步在生成扩展骨架的时候已经自动的完成了, 这里的第三步就是将该ext_demo_1_functions添加到zend_module_entry结构体上。

zend_module_entry ext_demo_1_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
	STANDARD_MODULE_HEADER,
#endif
	"ext_demo_1",
	ext_demo_1_functions,  /* 注意这里,添加了 ext_demo_1_functions 变量 */
	PHP_MINIT(ext_demo_1),
	PHP_MSHUTDOWN(ext_demo_1),
	PHP_RINIT(ext_demo_1),		/* Replace with NULL if there's nothing to do at request start */
	PHP_RSHUTDOWN(ext_demo_1),	/* Replace with NULL if there's nothing to do at request end */
	PHP_MINFO(ext_demo_1),
#if ZEND_MODULE_API_NO >= 20010901
	"0.1", /* Replace with version number for your extension */
#endif
	STANDARD_MODULE_PROPERTIES
};

为了验证我们的函数创建是否成功,我们编译一下这个扩展:

# phpize
# ./configure
# make
# make install

注意: 如果之前编译过该扩展,需要先make clean一下,清理掉上次编译产生的中间文件。

安装完成之后,验证一下是否成功:

$ php -r "print_r(demo_array());"
Array
(
    [life] => 42
    [123] => 1
    [124] => 3.1415926
    [125] => Foo
    [126] => Bar
    [444] => Array
        (
            [0] => 1
            [1] => 20
            [2] => 132
        )

)

好了,一个函数就已经创建完成了,在php文件中,我们就可以直接调用刚才创建的函数了:

<?php
print_r(demo_array());

###函数结构解析

为了对该函数的创建过程有个直观的了解,我们对刚才用到的宏进行简单的剖析。

这里的PHP_FUNCTION实际上是Zend定义的一个宏,展开后如下:

#define PHP_FUNCTION(name) \
    void zif_##name(INTERNAL_FUNCTION_PARAMETERS)

也就是说,如果有函数定义如下:

PHP_FUNCTION(sample_hello_world)
{
  php_printf("Hello World!\n");
}

在编译的时候将会被替换为:

void zif_sample_hello_world(
  int ht,
  zval *return_value,         /* 函数返回值 */
  zval **return_value_ptr,
  zval *this_ptr,
  char return_value_used TSRMLS_DC     /* 标识返回值是否被使用了 */
){
  ...
}

这里的 zif_Zend Internal Functions 缩写,为了避免定义的函数与C内部函数名冲突。

对于函数demo_array,内部实现如下:

ZEND_FUNCTION(demo_array)
{
  ...
}

部分宏替换之后如下:


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

当然,这样还没有结束,Zend引擎并不知道该函数的地址,因此,需要告诉引擎函数地址:

const zend_function_entry ext_demo_1_functions[] = {
	PHP_FE(confirm_ext_demo_1_compiled,	NULL)		/* For testing, remove later. */
	PHP_FE(demo_array, NULL)
  PHP_FALIAS(demo_array_alias, demo_array, NULL) /* 函数别名 */
	PHP_FE_END	/* Must be the last line in ext_demo_1_functions[] */
};

注意,zend_function_entry结构体最后一组值为PHP_FE_END ({ NULL, NULL, NULL, 0, 0 }),如果需要添加新的函数,则 在上面添加PHP_FE宏即可。

这里的PHP_FALIAS宏为函数demo_array提供了一个别名demo_array_alias。

使用zif前缀仍然可能与内部函数名称产生冲突,可以使用PHP_NAMED_FUNCTIONPHP_NAMED_FE 配合使用(与PHP_FUNCTION和PHP_FE一样)

这里的PHP_FE定义如下:

#define PHP_FE			ZEND_FE   /* php.h:341 */

#define ZEND_FE(name, arg_info)						ZEND_FENTRY(name, ZEND_FN(name), arg_info, 0)  /* zend_API.h:77 */

#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 },

该宏需要两个参数,第一个参数为函数名,第二个为参数,用于提供参数提示信息。

我的博客:http://aicode.cc/,在这里可以看到更多相关文章。

转载于:https://my.oschina.net/agiledev/blog/343165

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值