前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PHP扩展开发(六)PHP扩展生命周期

PHP扩展开发(六)PHP扩展生命周期

作者头像
用户2131907
发布2019-02-27 13:02:51
1.2K0
发布2019-02-27 13:02:51
举报
文章被收录于专栏:程序猿成长计划

全局变量

在单线程和多线程环境中,扩展内部全局变量的创建和使用方式是不同的。

声明扩展全局变量

要声明一个全局变量,首先需要在扩展的头文件中声明:

代码语言:javascript
复制
ZEND_BEGIN_MODULE_GLOBALS(sample4)
    unsigned long counter;
ZEND_END_MODULE_GLOBALS(sample4)

上述代码经过宏替换之后,实际上是声明了一个名为zend_sample4_globals的结构体。

代码语言:javascript
复制
typedef struct _zend_sample4_globals {
    unsigned long counter;
} zend_sample4_globals;

接下来,在扩展的源文件(.c)中,使用ZEND_DELCARE_MODULE_GLOBALS定义一个全局变量。

代码语言:javascript
复制
ZEND_DECLARE_MODULE_GLOBALS(sample4);

这里需要注意的是,在单线程和多线程环境中,该宏展开后的内容是不一样的:

代码语言:javascript
复制
// 在单线程环境中,展开为定义了一个sample4_globals的结构体变量
zend_sample4_globals sample4_globals;

// 在多线程环境中,定义了一个int型的全局变量ID,Zend将会使用该ID检索线程相关的全局变量数据
int sample4_globals_id;

MINIT方法中,为多线程环境中的sample4_globals_id分配一个ID。

代码语言:javascript
复制
#ifdef ZTS // 注意一定要放在ifdef 中,否则单线程编译将报错,没有sample_globals_id
    ts_allocate_id(&sample4_globals_id,
                sizeof(zend_sample4_globals),
                NULL, NULL); // 这里两个NULL,一个是构造回调函数,一个是销毁回调函数
#endif

如果希望对全局变量进行初始化,可以为ts_allocate_id()函数提供最后两个参数指定的函数指针:

代码语言:javascript
复制
static void php_sample4_globals_ctor(
            zend_sample4_globals *sample4_globals TSRMLS_DC)
{
    // Initialize a new zend_sample4_globals struct During thread spin-up
    sample4_globals->counter = 0;
}
static void php_sample4_globals_dtor(
            zend_sample4_globals *sample4_globals TSRMLS_DC)
{
    // Any resources allocated during initialization May be freed here
}

修改后的初始化代码:

代码语言:javascript
复制
PHP_MINIT_FUNCTION(sample4)
{
    REGISTER_STRING_CONSTANT("SAMPLE4_VERSION",
            PHP_SAMPLE4_EXTVER, CONST_CS | CONST_PERSISTENT);
#ifdef ZTS // 线程安全,指定回调函数指针
    ts_allocate_id(&sample4_globals_id,
                sizeof(zend_sample4_globals),
                (ts_allocate_ctor)php_sample4_globals_ctor,
                (ts_allocate_dtor)php_sample4_globals_dtor);
#else // 非线程安全,也要对其进行初始化
    php_sample4_globals_ctor(&sample4_globals TSRMLS_CC);
#endif
    return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(sample4)
{
#ifndef ZTS
    php_sample4_globals_dtor(&sample4_globals TSRMLS_CC);
#endif
    return SUCCESS;
}

注意: 对全局变量初始化时,不要忘记非线程安全的全局变量也是要初始化的。

访问扩展全局变量

全局变量定义之后,在访问时,也要考虑到线程安全问题,因为在线程安全和非线程安全环境中全局变量的定义方式不同, 因此,访问方式也需要对ZTS进行判断,使用不同方式访问,为了提高代码的可读性和方便起见, 通常情况下会定义一个宏来对全局变量进行访问。

在头文件中加入以下代码:

代码语言:javascript
复制
#ifdef ZTS // 如果定义了ZTS,需要引入TSRM.h头文件
#include "TSRM.h"
// 多线程环境中,使用TSRMG宏,根据全局变量ID标识符,查找全局变量
#define SAMPLE4_G(v)    TSRMG(sample4_globals_id,
                         zend_sample4_globals*, v)
#else
// 单线程环境中,直接访问全局变量
#define SAMPLE4_G(v)    (sample4_globals.v)
#endif

Zend定义的全局变量访问宏

EG() 执行器全局变量,该变量在引擎内部主要用来跟踪当前请求的状态,常用的符号表、函数、类、 常量和资源表可以通过该宏查找。

CG() 核心全局变量,该宏主要是Zend引擎在脚本编译以及内核部分执行使用,在扩展开发中很少会用到。

PG() PHP全局变量,可用于访问php.ini中大部分核心指令。例如PG(register_globals)PG(safe_mode)PG(memory_limit)

FG() 文件全局变量。大部分与文件I/O和流相关的全局变量都使用该结构查询,该宏为标准扩展提供。

注册常量

在PHP中,我们通常会使用define()定义一些常量,但是在扩展中,我们如何定义常量,让PHP能够访问呢? 在扩展开发中,通常使用REGISTER_*_CONSTANT()系列宏定义常量。

在PHP扩展中定义常量的时候,一般会在MINITRINIT函数中注册常量。如果希望常量在所有的脚本中 都被初始化为同样的值的话,需要在MINIT函数中注册,如果是请求相关的常量,则在RINIT函数中注册。

代码语言:javascript
复制
REGISTER_LONG_CONSTANT(char *name, long lval, int flags)
REGISTER_DOUBLE_CONSTANT(char *name, double dval, int flags)
REGISTER_STRING_CONSTANT(char *name, char *value, int flags)
REGISTER_STRINGL_CONSTANT(char *name,
                     char *value, int value_len, int flags)

上述函数并非真实的函数,而是ZEND定义的宏。在使用上述这些宏的时候需要注意的是,对于name参数, 必须提供直接字面值,而不能使用变量。如果需要常量名称从变量中赋值的话,需要使用以下函数代替:

代码语言:javascript
复制
ZEND_API void zend_register_long_constant(const char *name, uint name_len, long lval, int flags, int module_number TSRMLS_DC);

ZEND_API void zend_register_double_constant(const char *name, uint name_len, double dval, int flags, int module_number TSRMLS_DC);

ZEND_API void zend_register_string_constant(const char *name, uint name_len, char *strval, int flags, int module_number TSRMLS_DC);

ZEND_API void zend_register_stringl_constant(const char *name, uint name_len, char *strval, uint strlen, int flags, int module_number TSRMLS_DC);

具体函数以及宏的定义在PHP源码的zend_constants.h头文件中有定义。下面对使用到的参数进行简要说明:

  • name/name_len 常量名称、名称长度,这里长度不需要-1
  • lval/dval/value/strval 常量值
  • flags 常来标识,多个用“|”分隔
  • module_number 该参数由引擎的MINITRINIT函数提供,只需要直接使用即可,

注意的是,该参数需要传递线程安全标识TSRMLS_CC。

需要特别指出的是flags参数,该参数可以取下面几个值:

  • CONST_CS 表明该常量名称为区分大小写的,如果不提供则为不区分大小写
  • CONST_PERSISTENT 表示该常量是否是持久化存储的,对于在MINIT中注册的常量使用该参数。

注册常量的示例:

代码语言:javascript
复制
PHP_MINIT_FUNCTION(ext_demo_1)
{
    // 注册扩展常量
  REGISTER_STRING_CONSTANT("EXT_DEMO_1_VERSION", "1.0.1", CONST_CS | CONST_PERSISTENT);

  // 直接使用函数
  register_string_constant("SAMPLE4_VERSION",
        sizeof("SAMPLE4_VERSION"),
        "1.0",
        CONST_CS | CONST_PERSISTENT,
        module_number TSRMLS_CC);

    return SUCCESS;
}

除了数组和对象,其它类型都可以注册为常量,但是因为Zend没有提供特殊的宏或者函数,因此需要手动注册。

代码语言:javascript
复制
void php_sample4_register_boolean_constant(char *name, uint len,
    zend_bool bval, int flags, int module_number TSRMLS_DC)
{
    zend_constant c;// 手动创建zend_constant结构体变量

    ZVAL_BOOL(&c.value, bval);
    c.flags = CONST_CS | CONST_PERSISTENT;
    c.name = zend_strndup(name, len - 1);
    c.name_len = len;
    c.module_number = module_number;
    zend_register_constant(&c TSRMLS_CC);// 使用zend_register_constant函数注册常量
}

其中,zend_constant常量的结构如下:

代码语言:javascript
复制
typedef struct _zend_constant {
    zval value;    // 常量值
    int flags;    // 常量标识
    char *name;    // 常量名称
    uint name_len;    // 名称长度
    int module_number;    // module_number
} zend_constant;

为phpinfo提供扩展信息

在加载扩展之后,我们可以在使用phpinfo()函数或者是执行php -i命令显示PHP环境配置信息, 我们自己写的扩展的信息也将在这里面展示出来。

在PHP扩展程序中,通过使用MINFO函数提供扩展的基本信息。

代码语言:javascript
复制
#include "ext/standard/info.h"

PHP_MINFO_FUNCTION(ext_demo_1)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "ext_demo_1 support", "enabled");
    php_info_print_table_end();
}

注意,在zend_module_entry结构体中info_func函数指针部分指定该函数。

代码语言:javascript
复制
zend_module_entry ext_demo_1_module_entry = {
  ...
    PHP_MINFO(ext_demo_1),
  ...
};

MINFO函数中,使用php_info_*()系列函数创建需要显示的信息,需要注意的是, 使用之前检查一下是否已经加载了ext/standard/info.h头文件。

php_info_*系列函数列表:

代码语言:javascript
复制
PHPAPI char *php_info_html_esc(char *string TSRMLS_DC);
PHPAPI void php_info_html_esc_write(char *string, int str_len TSRMLS_DC);
PHPAPI void php_print_info_htmlhead(TSRMLS_D);
PHPAPI void php_print_info(int flag TSRMLS_DC);
PHPAPI void php_print_style(void);
PHPAPI void php_info_print_style(TSRMLS_D);
PHPAPI void php_info_print_table_colspan_header(int num_cols, char *header);
PHPAPI void php_info_print_table_header(int num_cols, ...);
PHPAPI void php_info_print_table_row(int num_cols, ...);
PHPAPI void php_info_print_table_row_ex(int num_cols, const char *, ...);
PHPAPI void php_info_print_table_start(void);
PHPAPI void php_info_print_table_end(void);
PHPAPI void php_info_print_box_start(int bg);
PHPAPI void php_info_print_box_end(void);
PHPAPI void php_info_print_hr(void);
PHPAPI void php_info_print_module(zend_module_entry *module TSRMLS_DC);

MINFO函数中输出扩展的信息时,不仅可以使用上述的api函数,我们还可以使用PHPWRITE()php_printf()函数,不过需要注意的是,使用这两个函数的时候需要判断当前的SAPI环境, 以在WEB和CLI模式下输出正确的信息。要判断环境,使用全局变量sapi_module结构体的 phpinfo_as_text属性。

代码语言:javascript
复制
PHP_MINFO_FUNCTION(sample4)
{
    php_info_print_table_start();
    php_info_print_table_row(2, "Sample4 Module", "enabled");
    php_info_print_table_row(2, "version", PHP_SAMPLE4_EXTVER);
    if (sapi_module.phpinfo_as_text) {
        /* No HTML for you */
        php_info_print_table_row(2, "By",
            "Example Technologies\nhttp://www.example.com");
    } else {
        /* HTMLified version */
        php_printf("<tr>"
            "<td class=\"v\">By</td>"
            "<td class=\"v\">"
            "<a href=\"http://www.example.com\""
            " alt=\"Example Technologies\">"
            "<img src=\"http://www.example.com/logo.png\" />"

            "</a></td></tr>");
    }
    php_info_print_table_end();
}

~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-03-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 全局变量
    • 声明扩展全局变量
      • 访问扩展全局变量
        • Zend定义的全局变量访问宏
        • 注册常量
        • 为phpinfo提供扩展信息
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档