PHP7扩展开发(二):配置项与全局数值

PHP 2016-11-21

起步

Zend引擎提供了另种管理设置值(INI)的途径。现在弄个简单的,我们经常看到php.ini里有诸如 display_errors = On 这样的全局设置。假设我们需要为我们扩展定义一个值: hello.greeting 并用函数 hello_ini() 返回它的内容。

在php.ini:

[hello]
hello.greeting=1

为了避免命名空间的冲突,我们扩展的名字作为所有值的前缀。仅仅是一种约定,一个句点用来分隔扩展名和说明性的初始设定名字。

声明变量 php_hello.h

ext_skel 工具初始化的扩展有个好处就是它能帮我们在特定的位置写上注释。

/*
    Declare any global variables you may need between the BEGIN
    and END macros here:

ZEND_BEGIN_MODULE_GLOBALS(hello)
    zend_long  global_value;
    char *global_string;
ZEND_END_MODULE_GLOBALS(hello)
*/

这是说如果我们需要声明全局变量,需要将放置在 BEBIN与END之间。并提供了示例,因此在这边添加:

ZEND_BEGIN_MODULE_GLOBALS(hello)
    zend_long greeting;
ZEND_END_MODULE_GLOBALS(hello)

ZEND_BEGIN_MODULE_GLOBALS()ZEND_END_MODULE_GLOBALS()用来创建一个名为zend_hello_globals的结构,它包含一个long型的变量。然后有条件地将HELLO_G()定义为从线程池中取得数值,或者从全局作用域中得到-如果你编译的目标是非多线程环境。

工具还为我生成了:

/* Always refer to the globals in your function as HELLO_G(variable).
   You are encouraged to rename these macros something shorter, see
   examples in any other php module directory.
*/
#define HELLO_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(hello, v)

#if defined(ZTS) && defined(COMPILE_DL_HELLO)
ZEND_TSRMLS_CACHE_EXTERN()
#endif

#endif

这是一个简化变量获取操作的宏设置,可以使用 HELLO_G(greeting) 来获得全局设置的变量。

源码实现 hello.c

/* {{{ PHP_INI
 */
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("hello.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_hello_globals, hello_globals)
    STD_PHP_INI_ENTRY("hello.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_hello_globals, hello_globals)
PHP_INI_END()
*/
/* }}} */

注释说明可以自己看一下,在这下方添加:

ZEND_DECLARE_MODULE_GLOBALS(hello)
    PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("hello.greeting","0", PHP_INI_ALL, OnUpdateLong, greeting, zend_hello_globals, hello_globals)
PHP_INI_END()

ZEND_DECLARE_MODULE_GLOBALS()宏来例示zend_hello_globals结构.初始值 "0" 是在php.ini里没有对应实体的时候生效的。

全局初始函数:

static void php_hello_init_globals(zend_hello_globals *hello_globals)
{
    //hello_globals->global_value = 0;
    //hello_globals->global_string = NULL;
}

php_hello_init_globals()实际上什么也没做,却得多声明个RINIT将变量greeting初始化为0,为什么?

关键在于这两个函数何时调用。php_hello_init_globals()只是在一个新的进程或线程时被调用;然而,每个进程都能处理多个请求,所以这个函数将变量初始化为0将只在第一个页面请求时运行。

接下来就是hello_ini()函数的实现了:

PHP_FUNCTION(hello_ini)
{
    RETURN_LONG(HELLO_G(greeting));
}

const zend_function_entry hello_functions[] = {
    PHP_FE(hello, NULL)
    PHP_FE(hello_ini, NULL) /*添加到编译中去*/
    PHP_FE(confirm_hello_compiled,  NULL)
    PHP_FE_END
};

一些诸如PHP_MINIT_FUNCTION也要修改,这些函数目前不知道作用是什么:

PHP_MINIT_FUNCTION(hello)
{
    REGISTER_INI_ENTRIES();
    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(hello)
{
    UNREGISTER_INI_ENTRIES();
    return SUCCESS;
}

修改配置

写个hello_change_ini()来修改配置项:

PHP_FUNCTION(hello_change_ini)
{
    HELLO_G(greeting) ++;
}

同样要加到 hello_functions[] 中。

测试

<?php
echo hello_ini();    //1
hello_change_ini();
echo "<br>";
echo hello_ini();    //2

本文由 hongweipeng 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

赏个馒头吧