女主宣言
今天小编为大家分享编写PHP扩展的系列文章,文章从环境构建到扩展编写,会对使用到的相关PHP内核数据结构及API进行介绍,希望能对大家有所帮助。
PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!
我们先回顾一下扩展骨架文件的内容。
“config.m4”是扩展配置脚本,在通过“phpize”或“buildconf”命令生成“配置”脚本期间使用。它是用M4宏处理语言编写的。对于PHP扩展配置,非常基础的知识就足够了。可以从本教程或其他扩展配置文件中复制粘贴代码块。
PHP_ARG_ENABLE([test],
[whether to enable test support],
[AS_HELP_STRING([--enable-test],
[Enable test support])],
[no])
if test "$PHP_TEST" != "no"; then
AC_DEFINE(HAVE_TEST, 1, [ Have test support ])
PHP_NEW_EXTENSION(test, test.c, $ext_shared)
fi
PHP_ARG_ENABLE(...) 宏添加配置选项“--enable-test”。它可能会获得三个值“yes”,“no”和“shared”。
当运行“phpize”时,默认值为“shared”,这意味着我们将构建一个可动态加载的PHP扩展。但是,可以将“test”扩展目录复制到主PHP发行版(“ext/test”)中,然后重新运行“./buildconf”和“./configure…–enable-test”以重新构建整个PHP,扩展名为“test”,静态链接。
默认情况下可以启用扩展,在第5行将“no”替换为“yes”。在这种情况下,可以通过“./configure --disable-test”禁用“test”扩展。
在“if”之后的只是一个常规的UNIX shell代码,它测试由“--enable-test”,“--disable-test”或“--enable-test=shared”定义的值。
AC_DEFINE(HAVE_TEST)将C宏HAVE_TEST添加到“config.h”中,因此如有必要,可以使用条件编译指令(#ifdef,#ifndef)跳过无用的代码。
最后,PHP_NEW_EXTENSION(test, test.c, $ext_shared)宏指出,将从“test.c”文件构建扩展“test”。可以指定几个文件。根据$ ext_shared变量的值,扩展名可以构建为共享对象或静态链接。(它取自相同的“--enable-test”选项。)
如果添加新的源文件或需要链接某些外部库,则可能需要扩展此文件。稍后将说明如何链接库。只是,不要忘记在此文件中进行任何更改后重新运行“phpize” / “buildconf” +“configure”。
Windows PHP使用不同的构建系统。对于Windows,文件“config.w32”代替了“config.m4”。两者几乎相同。他们使用相似的宏,只是一种不同的语言:在Windows PHP构建系统上,使用JavaScript而不是M4和Shell。将不再重复对宏的解释。
ARG_ENABLE('test', 'test support', 'no');
if (PHP_TEST != 'no') {
AC_DEFINE('HAVE_TEST', 1, 'test support enabled');
EXTENSION('test', 'test.c', null, '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
}
“php_test.h”是具有通用定义的C头文件。在我们最基本的情况下,它定义:
test_module_entry — 扩展描述结构。(扩展程序的入口)
PHP_TEST_VERSION — 扩展的版本。
ZEND_TSRMLS_CACHE_EXTERN — 如果扩展是为线程安全的版本(ZTS)构建并编译为共享对象(COMPILE_DL_TEST),则为线程本地存储缓存条目。
/* test extension for PHP */
#ifndef PHP_TEST_H
# define PHP_TEST_H
extern zend_module_entry test_module_entry;
# define phpext_test_ptr &test_module_entry
# define PHP_TEST_VERSION "0.1.0"
# if defined(ZTS) && defined(COMPILE_DL_TEST)
ZEND_TSRMLS_CACHE_EXTERN()
# endif
#endif /* PHP_TEST_H */
“test.c”是主要扩展源文件。我们将其分成小部分来分别解释。
/* test extension for PHP */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "php_test.h"
包括必要的C头文件。如有必要,可以添加其他“#include”指令。
/* For compatibility with older PHP versions */
#ifndef ZEND_PARSE_PARAMETERS_NONE
#define ZEND_PARSE_PARAMETERS_NONE() \
ZEND_PARSCE_PARAMETERS_START(0, 0) \
ZEND_PARSE_PARAMETERS_END()
#endif
一些向前兼容性宏,以便可以为较旧的PHP-7版本编译扩展。
/* {{{ void test_test1()
*/
PHP_FUNCTION(test_test1)
{
ZEND_PARSE_PARAMETERS_NONE();
php_printf("The extension %s is loaded and working!\r\n", "test");
}
/* }}} */
我们的PHP扩展提供的功能test_test1()的C代码。PHP_FUNCTION()宏的参数是函数名称。ZEND_PARSE_PARAMETERS_NONE()表示此函数不需要任何参数。php_printf(...)只是一个C函数调用,该函数将字符串打印到输出流中,类似于PHP printf()函数。
/* {{{ string test_test2( [ string $var ] )
*/
PHP_FUNCTION(test_test2)
{
char *var = "World";
size_t var_len = sizeof("World") – 1;
zend_string *retval;
ZEND_PARSE_PARAMETERS_START(0, 1)
Z_PARAM_OPTIONAL
Z_PARAM_STRING(var, var_len)
ZEND_PARSE_PARAMETERS_END();
retval = strpprintf(0, "Hello %s", var);
RETURN_STR(retval);
}
/* }}}*/
另一个更复杂的函数使用“快速参数解析API”来描述其参数。
ZEND_PARSE_PARAMETERS_START(0, 1)启动参数描述部分。它的第一个参数(0)定义所需参数的数量。
第二个自变量(1)定义最大自变量数。因此,我们的函数可以不带参数调用,也可以带单个参数调用。
在本节中,我们应该定义所有参数,它们的类型以及将它们复制到的位置。对于我们的情况:
Z_PARAM_OPTIONAL 将必需的参数与可选的参数分开。
Z_PARAM_STRING() 定义一个字符串参数,该值将被复制到变量“var”,将长度复制到变量“var_len”。
请注意,我们的参数是可选的,因此可以省略。在这种情况下,将使用默认值“world”。参见ZEND_PARSE_PARAMETERS_START上方的变量“var”和“var_len”的初始化程序。
该代码创建一个“zend_string”值,并通过类似于PHP sprintf() 函数的宏RETURN_STR()返回该值:
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(test)
{
#if defined(ZTS) && defined(COMPILE_DL_TEST)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
return SUCCESS;
}
/* }}} */
PHP_RINIT_FUNCTION()定义了一个回调函数,它将在每次请求启动时被调用。在我们的情况下,它仅初始化线程本地存储缓存。尽早进行此操作(在MINIT或GINIT回调中)会更好。预计这将在PHP 8扩展框架中得到解决。
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(test)
{
php_info_print_table_start();
php_info_print_table_header(2, "test support", "enabled");
php_info_print_table_end();
}
/* }}} */
PHP_MINFO_FUNCTION()定义了一个回调函数,该函数将从PHP phpinfo()函数中调用,以打印有关扩展的信息。
/* {{{ arginfo
*/
ZEND_BEGIN_ARG_INFO(arginfo_test_test1, 0)
ZEND_END_ARG_INFO()
有关第一个函数的参数的信息。没有参数。
ZEND_BEGIN_ARG_INFO(arginfo_test_test2, 0)
ZEND_ARG_INFO(0, str)
ZEND_END_ARG_INFO()
/* }}} */
有关第二个函数的参数的信息。名称为“str”的单个可选参数按值传递。
/* {{{ test_functions[]
*/
static const zend_function_entry test_functions[] = {
PHP_FE(test_test1, arginfo_test_test1)
PHP_FE(test_test2, arginfo_test_test2)
PHP_FE_END
};
/* }}} */
“test_functions”是所有扩展功能的列表,以及有关其参数的信息。该列表由PHP_FE_END宏终止。
/* {{{ test_module_entry
*/
zend_module_entry test_module_entry = {
STANDARD_MODULE_HEADER,
"test", /* Extension name */
test_functions, /* zend_function_entry */
NULL, /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(test), /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(test), /* PHP_MINFO - Module info */
PHP_TEST_VERSION, /* Version */
STANDARD_MODULE_PROPERTIES
};
/* }}} */
test_module_entry 是主要的扩展条目结构。PHP核心从此类结构获取有关扩展的所有信息。它定义:
扩展名(“test”)。
声明的PHP函数列表(“test_functions”)。
一些回调函数和扩展版本(PHP_TEST_VERSION-在头文件中定义)。
回调发生在以下情况:当PHP启动(MINIT),PHP终止(MSHUTDOWN),每个请求处理的开始(RINIT),每个请求处理的结束(RSHUTDOWN)以及来自phpinfo()(MINFO)的回调。
#ifdef COMPILE_DL_TEST
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(test)
#endif
最后,为动态链接的两个定义。
如果大家有什么建议或疑问,可以在下方留言交流。
360云计算
由360云平台团队打造的技术分享公众号,内容涉及数据库、大数据、微服务、容器、AIOps、IoT等众多技术领域,通过夯实的技术积累和丰富的一线实战经验,为你带来最有料的技术分享