목차
PHP扩展开发相关总结,php扩展总结
1、线程安全宏定义
2、PHP的生命周期
比如PHP_GINIT_FUNCTION
这里有一段代码,可以测试一下
3、段错误调试
4、常见的变量操作宏
  1、SG  针对SAPI信息 在main/SAPI.h文件中
 2、EG  Executor Globals
5、获取变量的类型和值
6、常量的实例化
7、全局变量
8、包装第三方库
9、用于返回的宏
10、hashTable的遍历函数
백엔드 개발 PHP 튜토리얼 PHP扩展开发相关总结,php扩展总结_PHP教程

PHP扩展开发相关总结,php扩展总结_PHP教程

Jul 12, 2016 am 09:05 AM
PHP 언어

PHP扩展开发相关总结,php扩展总结

1、线程安全宏定义

在TSRM/TSRM.h文件中有如下定义

#define TSRMLS_FETCH()       void ***tsrm_ls = (void ***) ts_resource_ex(0, NULL)
#define TSRMLS_FETCH_FROM_CTX(ctx) void ***tsrm_ls = (void ***) ctx
#define TSRMLS_SET_CTX(ctx)   ctx = (void ***) tsrm_ls
#define TSRMG(id, type, element)   (((type) (*((void ***) tsrm_ls))[TSRM_UNSHUFFLE_RSRC_ID(id)])->element)
#define TSRMLS_D   void ***tsrm_ls
#define TSRMLS_DC  , TSRMLS_D
#define TSRMLS_C   tsrm_ls
#define TSRMLS_CC  , TSRMLS_C
로그인 후 복사

在ext/xsl/php_xsl.h有这么一段话

/* In every utility function you add that needs to use variables.                                                                     
   in php_xsl_globals, call TSRM_FETCH(); after declaring other.
   variables used by that function, or better yet, pass in TSRMLS_CC
   after the last function argument and declare your utility function
   with TSRMLS_DC after the last declared argument.  Always refer to
   the globals in your function as XSL_G(variable).  You are.
   encouraged to rename these macros something shorter, see
   examples in any other php module directory.
*/
로그인 후 복사

1.在方法定义时加上TSRMLS_D(如果方法没有参数用这个)或者TSRMLS_DC(有1个以上的参数)
2.在方法调用时用TSRMLS_C(如果方法没有参数用这个)或者TSRMLS_CC(有1个以上的参数)

应该可以这样理解

第一个后缀字母D表示定义,即D=Define,第一个后缀字母C表示调用,即C=Call,而第二个后缀字母C是不是表示逗号呢? C=Comma (逗号)

TSRMLS_D就是定义了,所以是  void ***tsrm_ls

TSRMLS_DC是带逗号的定义,所以是 , void ***tsrm_ls

TSRMLS_C是调用,即tsrm_ls

TSRMLS_CC是调用并带逗号,即 ,tsrm_ls
로그인 후 복사

所以一个是形参、一个是实参

可以这样使用

int php_myext_action(int action_id, char *message TSRMLS_DC);
php_myext_action(42, "The meaning of life" TSRMLS_CC);
로그인 후 복사

一般推荐使用tsrm_ls指针定义的方式来保证线程安全

TSRMLS_FETCH调用需要一定的处理时间。这在单次迭代中并不明显,但是随着你的线程数增多,随着你调用TSRMLS_FETCH()的点的增多,你的扩展就会显现出这个瓶颈。因此,请谨慎的使用它。 注意:为了和c++编译器兼容,请确保将TSRMLS_FETCH()和所有变量定义放在给定块作用域的顶部(任何其他语句之前)。因为TSRMLS_FETCH()宏自身有多种不同的解析方式,因此最好将它作为变量定义的最后一行

2、PHP的生命周期

PHP的最多的两种运行模式是WEB模式、CLI模式,无论哪种模式,PHP工作原理都是一样的,作为一种SAPI运行。

1、当我们在终端敲入php这个命令的时候,它使用的是CLI。
它就像一个web服务器一样来支持php完成这个请求,请求完成后再重新把控制权交给终端。
2、当使用Apache作为宿主时,当一个请求到来时,PHP会来支持完成这个请求

PHP_MINIT_FUNCTION  初始化module时运行  
PHP_MSHUTDOWN_FUNCTION  当module被卸载时运行  
PHP_RINIT_FUNCTION  当一个REQUEST请求初始化时运行  
PHP_RSHUTDOWN_FUNCTION  当一个REQUEST请求结束时运行  
PHP_MINFO_FUNCTION  这个是设置phpinfo中这个模块的信息  
PHP_GINIT_FUNCTION  初始化全局变量时  
PHP_GSHUTDOWN_FUNCTION  释放全局变量时  
로그인 후 복사

比如PHP_GINIT_FUNCTION

比如PHP_GINIT_FUNCTION

PHP_GINIT_FUNCTION(test)
{
    /** 初始化全局变量 */
}
//对应的C代码
void zm_globals_ctor_test (zend_test_globals *test_globals TSRMLS_DC)
{
    /** 初始化全局变量 */
}
//在线程退出时,需要将之前自己申请的资源释放时,可以使用 PHP_GSHUTDOWN_FUNCTION来注册析构函数。
PHP_GSHUTDOWN_FUNCTION(test)
{
    /** 清除全局变量 */
}

//对应的C代码
void zm_globals_dtor_test (zend_test_globals *test_globals TSRMLS_DC)
{
    /** 清除全局变量 */
}
로그인 후 복사

这里有一段代码,可以测试一下

int minit_time;
PHP_MINIT_FUNCTION(test)
{
    minit_time = time(NULL);
    return SUCCESS;
}

PHP_MSHUTDOWN_FUNCTION(test)
{
    FILE *fp=fopen("mshutdown.txt","a+");
    fprintf(fp,"%ld\n",time(NULL));
    fclose(fp);
    return SUCCESS;
}

int rinit_time;
PHP_RINIT_FUNCTION(test)
{
    rinit_time = time(NULL);
    return SUCCESS;
}


PHP_RSHUTDOWN_FUNCTION(test)
{
    FILE *fp=fopen("rshutdown.txt","a+");
    fprintf(fp,"%ld\n",time(NULL));
    fclose(fp);
    return SUCCESS;
}

PHP_MINFO_FUNCTION(test)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "module info", "enabled");
    php_info_print_table_end();

    /* Remove comments if you have entries in php.ini
    DISPLAY_INI_ENTRIES();
    */
}


PHP_FUNCTION(test)
{
    php_printf("%d",time_of_minit);
    php_printf("%d",time_of_rinit);
    return;
}  
로그인 후 복사

3、段错误调试

Linux下的C程序常常会因为内存访问错误等原因造成segment fault(段错误)此时如果系统core dump功能是打开的,那么将会有内存映像转储到硬盘上来,之后可以用gdb对core文件进行分析,还原系统发生段错误时刻的堆栈情况。这对于我们发现程序bug很有帮助。
使用ulimit -a可以查看系统core文件的大小限制;使用ulimit -c [kbytes]可以设置系统允许生成的core文件大小。

ulimit -c 0 不产生core文件
ulimit -c 100 设置core文件最大为100k
ulimit -c unlimited 不限制core文件大小

步骤:

1、当发生段错误时,我们查看ulimit -a (core file size (blocks, -c) 0)并没有文件,
2、设置 :ulimit -c unlimited 不限制core文件大小
3、运行程序 ,发生段错误时会自动记录在core中 (php -f WorkWithArray.php)
4、ls -al core.* 在那个文件下(-rw------- 1 leconte leconte 139264 01-06 22:3 1 core.2065)
5、使用gdb 运行程序和段错误记录的文件。(gdb ./test core.2065)
6、会提哪行有错。


很多系统默认的core文件大小都是0,我们可以通过在shell的启动脚本/etc/bashrc或者~/.bashrc等地方来加入 ulimit -c 命令来指定core文件大小,从而确保core文件能够生成。
除此之外,还可以在/proc/sys/kernel/core_pattern里设置core文件的文件名模板,详情请看core的官方man手册。

4、常见的变量操作宏

CG    -> Complier Global      编译时信息,包括函数表等(zend_globals_macros.h:32)
EG    -> Executor Global      执行时信息(zend_globals_macros.h:43)
PG    -> PHP Core Global      主要存储php.ini中的信息
SG    -> SAPI Global          SAPI信息
로그인 후 복사

1、SG 针对SAPI信息 在main/SAPI.h文件中

typedef struct _sapi_globals_struct {
    void *server_context;
    sapi_request_info request_info;
    sapi_headers_struct sapi_headers;
    int read_post_bytes;
    unsigned char headers_sent;
    struct stat global_stat;
    char *default_mimetype;
    char *default_charset;
    HashTable *rfc1867_uploaded_files;
    long post_max_size;
    int options;
    zend_bool sapi_started;
    double global_request_time;
    HashTable known_post_content_types;
    zval *callback_func;
    zend_fcall_info_cache fci_cache;
    zend_bool callback_run;
} sapi_globals_struct;
로그인 후 복사

看一下SG的定义

BEGIN_EXTERN_C()
#ifdef ZTS
# define SG(v) TSRMG(sapi_globals_id, sapi_globals_struct *, v)
SAPI_API extern int sapi_globals_id;
#else
# define SG(v) (sapi_globals.v)
extern SAPI_API sapi_globals_struct sapi_globals;
#endif

SAPI_API void sapi_startup(sapi_module_struct *sf);
SAPI_API void sapi_shutdown(void);
SAPI_API void sapi_activate(TSRMLS_D);
SAPI_API void sapi_deactivate(TSRMLS_D);
SAPI_API void sapi_initialize_empty_request(TSRMLS_D);
END_EXTERN_C()
로그인 후 복사

成员都在sapi_globals_struct这里了

那么我么可以这样调用

SG(default_mimetype)
SG(request_info).request_uri
로그인 후 복사

可以感受一下这么一段代码

static int sapi_cgi_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC)
{
    char buf[SAPI_CGI_MAX_HEADER_LENGTH];
    sapi_header_struct *h;
    zend_llist_position pos;
    long rfc2616_headers = 0;

    if(SG(request_info).no_headers == 1) {
        return  SAPI_HEADER_SENT_SUCCESSFULLY;
    }

    if (SG(sapi_headers).http_response_code != 200) {
        int len;
        len = sprintf(buf, "Status: %d\r\n", SG(sapi_headers).http_response_code);
        PHPWRITE_H(buf, len);
    }
    if (SG(sapi_headers).send_default_content_type) {
        char *hd;
        hd = sapi_get_default_content_type(TSRMLS_C);
        PHPWRITE_H("Content-type:", sizeof("Content-type: ")-1);
        PHPWRITE_H(hd, strlen(hd));
        PHPWRITE_H("\r\n", 2);
        efree(hd);
    }
    h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
    while (h) {
        PHPWRITE_H(h->header, h->header_len);
        PHPWRITE_H("\r\n", 2);
        h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
    }
    PHPWRITE_H("\r\n", 2);
    return SAPI_HEADER_SENT_SUCCESSFULLY;
}
로그인 후 복사

2、EG Executor Globals

EG获取的是struct _zend_execution_globals结构体中的数据

struct _zend_execution_globals {
  ...
  HashTable symbol_table;   /* 全局作用域,如果没有进入函数内部,全局=活动 */
  HashTable *active_symbol_table; /* 活动作用域,当前作用域 */
  ...
}
로그인 후 복사

通常,使用EG(symbol_table)获取的是全局作用域中的符号表,使用EG(active_symbol_table)获取的是当前作用域下的符号表

例如 来定义$foo = 'bar'

zval *fooval;

MAKE_STD_ZVAL(fooval);
ZVAL_STRING(fooval, "bar", 1);
ZEND_SET_SYMBOL(EG(active_symbol_table), "foo", fooval);
로그인 후 복사

或者从符号表中查找$foo

zval **fooval;
if(zend_hash_find(&EG(symbol_table), "foo", sizeof("foo"), (void **)&fooval) == SUCCESS) {
	RETURN_STRINGL(Z_STRVAL_PP(fooval), Z_STRLEN_PP(fooval));
} else {
	RETURN_FALSE;
}
로그인 후 복사

上面的代码中,EG(active_symbol_table) == &EG(symbol_table)

3、CG() 用来访问核心全局变量。(zend/zend_globals_macros.h)

4、PG() PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。(main/php_globals.h)

5、FG() 文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构。(ext/standard/file.h)

5、获取变量的类型和值

#define Z_TYPE(zval)        (zval).type
#define Z_TYPE_P(zval_p)    Z_TYPE(*zval_p)
#define Z_TYPE_PP(zval_pp)  Z_TYPE(**zval_pp)
로그인 후 복사

比如获取一个变量的类型

void describe_zval(zval *foo)
{
    if ( Z_TYPE_P(foo) == IS_NULL )
    {
        php_printf("这个变量的数据类型是: NULL");
    }
    else
    {
        php_printf("这个变量的数据类型不是NULL,这种数据类型对应的数字是: %d", Z_TYPE_P(foo));
    }
}
로그인 후 복사

有这么几种类型

#define IS_NULL     0
#define IS_LONG     1
#define IS_DOUBLE   2
#define IS_BOOL     3
#define IS_ARRAY    4
#define IS_OBJECT   5
#define IS_STRING   6
#define IS_RESOURCE 7
#define IS_CONSTANT 8
#define IS_CONSTANT_ARRAY   9
#define IS_CALLABLE 10
로그인 후 복사

php_printf()函数是内核对printf()函数的一层封装,我们可以像使用printf()函数那样使用它,以一个P结尾的宏的参数大多是*zval型变量。 此外获取变量类型的宏还有两个,分别是Z_TYPE和Z_TYPE_PP,前者的参数是zval型,而后者的参数则是**zval

比如gettype函数的实现

//开始定义php语言中的函数gettype
PHP_FUNCTION(gettype)
{
    //arg间接指向调用gettype函数时所传递的参数。是一个zval**结构
    //所以我们要对他使用__PP后缀的宏。
    zval **arg;

    //这个if的操作主要是让arg指向参数~
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
        return;
    }

    //调用Z_TYPE_PP宏来获取arg指向zval的类型。
    //然后是一个switch结构,RETVAL_STRING宏代表这gettype函数返回的字符串类型的值
    switch (Z_TYPE_PP(arg)) {
        case IS_NULL:
            RETVAL_STRING("NULL", 1);
            break;

        case IS_BOOL:
            RETVAL_STRING("boolean", 1);
            break;

        case IS_LONG:
            RETVAL_STRING("integer", 1);
            break;

        case IS_DOUBLE:
            RETVAL_STRING("double", 1);
            break;

        case IS_STRING:
            RETVAL_STRING("string", 1);
            break;

        case IS_ARRAY:
            RETVAL_STRING("array", 1);
            break;

        case IS_OBJECT:
            RETVAL_STRING("object", 1);
            break;

        case IS_RESOURCE:
            {
                char *type_name;
                type_name = zend_rsrc_list_get_rsrc_type(Z_LVAL_PP(arg) TSRMLS_CC);
                if (type_name) {
                    RETVAL_STRING("resource", 1);
                    break;
                }
            }

        default:
            RETVAL_STRING("unknown type", 1);
    }
}   
로그인 후 복사

获取变量的值,有这么多宏来获取

Long

Boolean

Double

String value

String length

<pre class="code">Z_LVAL( )
로그인 후 복사
<pre class="code">Z_BVAL( )
로그인 후 복사
<pre class="code">Z_DVAL( )
로그인 후 복사
<pre class="code">Z_STRVAL( )
로그인 후 복사
<pre class="code">Z_STRLEN( )
로그인 후 복사
<pre class="code">Z_LVAL_P( )
로그인 후 복사
<pre class="code">Z_BVAL_P( )
로그인 후 복사
<pre class="code">Z_DVAL_P( )
로그인 후 복사
<pre class="code">Z_STRVAL_P( )
로그인 후 복사
<pre class="code">Z_STRLEN_P( )
로그인 후 복사
<pre class="code">Z_LVAL_PP( )
로그인 후 복사
<pre class="code">Z_BVAL_PP( )
로그인 후 복사
<pre class="code">Z_DVAL_PP( )
로그인 후 복사
<pre class="code">Z_STRVAL_PP( )
로그인 후 복사
<pre class="code">Z_STRLEN_PP( )
로그인 후 복사

HashTable

Object

Object properties

Object class entry

Resource value

<pre class="code">Z_ARRVAL( )
로그인 후 복사
<pre class="code">Z_OBJ( )
로그인 후 복사
<pre class="code">Z_OBJPROP( )
로그인 후 복사
<pre class="code">Z_OBJCE( )
로그인 후 복사
<pre class="code">Z_RESVAL( )
로그인 후 복사
<pre class="code">Z_ARRVAL_P( )
로그인 후 복사
<pre class="code">Z_OBJ_P( )
로그인 후 복사
<pre class="code">Z_OBJPROP_P( )
로그인 후 복사
<pre class="code">Z_OBJCE_P( )
로그인 후 복사
<pre class="code">Z_RESVAL_P( )
로그인 후 복사
<pre class="code">Z_ARRVAL_PP( )
로그인 후 복사
<pre class="code">Z_OBJ_PP( )
로그인 후 복사
<pre class="code">Z_OBJPROP_PP( )
로그인 후 복사
<pre class="code">Z_OBJCE_PP( )
로그인 후 복사
<pre class="code">Z_RESVAL_PP( )
로그인 후 복사

rot13函数的实现

PHP_FUNCTION(rot13)
{
  zval **arg;
  char *ch, cap;
  int i;
  
  if (ZEND_NUM_ARGS( ) != 1 || zend_get_parameters_ex(1, &arg) == FAILURE) {
      WRONG_PARAM_COUNT;
  }
  *return_value = **arg;
  zval_copy_ctor(return_value);
  convert_to_string(return_value);
  
  for(i=0, ch=return_value->value.str.val;
      i<return_value->value.str.len; i++, ch++) {
        cap = *ch & 32;
        *ch &= ~cap;
        *ch = ((*ch>='A') && (*ch<='Z') ? ((*ch-'A'+13) % 26 + 'A') : *ch) | cap;
    }
}
로그인 후 복사

要获取变量的值,也应该使用Zend定义的宏进行访问。对于简单的标量数据类型、Boolean,long,double, 使用Z_BVAL, Z_LVAL, Z_DVAL

void display_values(zval boolzv, zval *longpzv, zval **doubleppzv)
{
  if (Z_TYPE(boolzv) == IS_BOOL) {
    php_printf("The value of the boolean is : %s\n", Z_BVAL(boolzv) ? "true" : "false");
  }
  if(Z_TYPE_P(longpzv) == IS_LONG) {
    php_printf("The value of the long is: %ld\n", Z_LVAL_P(longpzv));
  }
  if(Z_TYPE_PP(doubleppzv) == IS_DOUBLE) {
    php_printf("The value of the double is : %f\n", Z_DVAL_PP(doubleppzv));
  }
}
로그인 후 복사

对于字符串类型,因为它含有两个字段char * (Z_STRVAL) 和 int (Z_STRLEN),因此需要用两个宏来进行取值,因为需要二进制安全的输出这个字符串

void display_string(zval *zstr)
{
  if (Z_TYPE_P(zstr) != IS_STRING) {
    php_printf("The wronng datatype was passed!\n");
    return ;
  }
  PHPWRITE(Z_STRVAL_P(zstr), Z_STRLEN_P(zstr));
}
로그인 후 복사

因为数组在zval中是以HashTable形式存在的,因此使用Z_ARRVAL()进行访问

void display_zval(zval *value)
{
    switch (Z_TYPE_P(value)) {
        case IS_NULL:
            /* 如果是NULL,则不输出任何东西 */
            break;

        case IS_BOOL:
            /* 如果是bool类型,并且true,则输出1,否则什么也不干 */
            if (Z_BVAL_P(value)) {
                php_printf("1");
            }
            break;
        case IS_LONG:
            /* 如果是long整型,则输出数字形式 */
            php_printf("%ld", Z_LVAL_P(value));
            break;
        case IS_DOUBLE:
            /* 如果是double型,则输出浮点数 */
            php_printf("%f", Z_DVAL_P(value));
            break;
        case IS_STRING:
            /* 如果是string型,则二进制安全的输出这个字符串 */
            PHPWRITE(Z_STRVAL_P(value), Z_STRLEN_P(value));
            break;
        case IS_RESOURCE:
            /* 如果是资源,则输出Resource #10 格式的东东 */
            php_printf("Resource #%ld", Z_RESVAL_P(value));
            break;
        case IS_ARRAY:
            /* 如果是Array,则输出Array5个字母! */
            php_printf("Array");
            break;
        case IS_OBJECT:
            php_printf("Object");
            break;
        default:
            /* Should never happen in practice,
             * but it's dangerous to make assumptions
             */
             php_printf("Unknown");
             break;
    }
}    
로그인 후 복사

一些类型转换函数

ZEND_API void convert_to_long(zval *op);
ZEND_API void convert_to_double(zval *op);
ZEND_API void convert_to_null(zval *op);
ZEND_API void convert_to_boolean(zval *op);
ZEND_API void convert_to_array(zval *op);
ZEND_API void convert_to_object(zval *op);
ZEND_API void _convert_to_string(zval *op ZEND_FILE_LINE_DC);
로그인 후 복사

6、常量的实例化

我们可以这样实例化

PHP_MINIT_FUNCTION(consts) //模块初始化时定义常量
{
    REGISTER_LONG_CONSTANT("CONSTS_MEANING_OF_LIFE", 42, CONST_CS | CONST_PERSISTENT);
    REGISTER_DOUBLE_CONSTANT("CONSTS_PI", 3.1415926, CONST_PERSISTENT);
    REGISTER_STRING_CONSTANT("CONSTS_NAME", "leon", CONST_CS|CONST_PERSISTENT);
}

PHP_RINIT_FUNCTION(consts) //每次请求时定义常量
{
    char buffer[40];
    srand((int)time(NULL));
    snprintf(buffer, sizeof(buffer), "%d", rand());
    REGISTER_STRING_CONSTANT("CONSTS_RAND", estrdup(buffer), CONST_CS);
    return SUCCESS;
}
로그인 후 복사

常见的宏

/*注册LONG类型常量*/
#define REGISTER_LONG_CONSTANT(name, lval, flags)  zend_register_long_constant((name), sizeof(name), (lval), (flags), module_number TSRMLS_CC)

/*注册double类型常量*/
#define REGISTER_DOUBLE_CONSTANT(name, dval, flags)  zend_register_double_constant((name), sizeof(name), (dval), (flags), module_number TSRMLS_CC)

/*注册STRING类型常量*/
#define REGISTER_STRING_CONSTANT(name, str, flags)  zend_register_string_constant((name), sizeof(name), (str), (flags), module_number TSRMLS_CC)

/*注册STRING类型常量*/
#define REGISTER_STRINGL_CONSTANT(name, str, len, flags)  zend_register_stringl_constant((name), sizeof(name), (str), (len), (flags), module_number TSRMLS_CC)
로그인 후 복사

7、全局变量

#php-fpm 生成 POST|GET|COOKIE|SERVER|ENV|REQUEST|FILES全局变量的流程
php_cgi_startup() -> php_module_startup() -> php_startup_auto_globals() -> 保存变量到symbol_table符号表

php_cgi_startup()在 fpm/fpm/fpm_main.c中定义
php_module_startup() 在main/main.c中定义
php_startup_auto_globals() 在main/php_variables.h中定义
zend_hash_update(&EG(symbol_table), "_GET", sizeof("_GET") + 1, &vars, sizeof(zval *), NULL);

/* 读取$_SERVER变量 */
static PHP_FUNCTION(print_server_vars) {
    zval **val;
    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) {
        RETURN_ZVAL(*val, 1, 0);
    }else{
     RETURN_FALSE;
    }
}


/* 读取$_SERVER[$name] */

ZEND_BEGIN_ARG_INFO(print_server_var_arginfo, 0)
    ZEND_ARG_INFO(0, "name")
ZEND_END_ARG_INFO()

static PHP_FUNCTION(print_server_var) {
    char *name;
    int name_len;
    zval **val;
    HashTable *ht_vars = NULL;
    HashPosition pos;
    zval **ret_val;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!", &name, &name_len) == FAILURE) {
        RETURN_NULL();
    }

    if (zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **)&val) == SUCCESS) {
        ht_vars = Z_ARRVAL_PP(val);

        //此处需传入大于name长度+1的值,因为字符串值后面需要'\0'

        if (zend_hash_find(ht_vars, name, name_len+1, (void **)&ret_val) == SUCCESS) {              RETURN_STRING(Z_STRVAL_PP(ret_val), 0);
        }else{
            RETURN_NULL();
        }
    }else{
        RETURN_NULL();
    }
}
로그인 후 복사

8、包装第三方库

配置(config.m4)

SEARCH_PATH="/usr/local /usr"     #lib搜索的目录
SEARCH_FOR="/include/curl/curl.h"  #lib头文件的路径
if test -r $PHP_LIBS/$SEARCH_FOR; then 
    LIBS_DIR=$PHP_LIBS
else # search default path list
    AC_MSG_CHECKING([for libs files in default path])
    for i in $SEARCH_PATH ; do
        if test -r $i/$SEARCH_FOR; then
            LIBS_DIR=$i                #搜索到的lib的路径
            AC_MSG_RESULT(found in $i)
        fi
    done
fi

/*验证lib是否存在*/
if test -z "$LIBS_DIR"; then
    AC_MSG_RESULT([not found])
    AC_MSG_ERROR([Please reinstall the libs distribution])
fi


/*编译的时候添加lib的include目录,  -I/usr/include*/
PHP_ADD_INCLUDE($LIBS_DIR/include) 

LIBNAME=curl            #lib名称  
LIBSYMBOL=curl_version  #lib的一个函数,用来PHP_CHECK_LIBRARY验证lib

/*验证lib*/
PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,  
[
    PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $LIBS_DIR/$PHP_LIBDIR, LIBS_SHARED_LIBADD) #编译的时候链接lib, -llibcurl
    AC_DEFINE(HAVE_LIBSLIB,1,[ ])
],[
    AC_MSG_ERROR([wrong libs lib version or lib not found])
],[
    -L$LIBS_DIR/$PHP_LIBDIR -lm
])  


PHP_SUBST(LIBS_SHARED_LIBADD)
로그인 후 복사

9、用于返回的宏

//这些宏都定义在Zend/zend_API.h文件里
#define RETVAL_RESOURCE(l)              ZVAL_RESOURCE(return_value, l)
#define RETVAL_BOOL(b)                  ZVAL_BOOL(return_value, b)
#define RETVAL_NULL()                   ZVAL_NULL(return_value)
#define RETVAL_LONG(l)                  ZVAL_LONG(return_value, l)
#define RETVAL_DOUBLE(d)                ZVAL_DOUBLE(return_value, d)
#define RETVAL_STRING(s, duplicate)         ZVAL_STRING(return_value, s, duplicate)
#define RETVAL_STRINGL(s, l, duplicate)     ZVAL_STRINGL(return_value, s, l, duplicate)
#define RETVAL_EMPTY_STRING()           ZVAL_EMPTY_STRING(return_value)
#define RETVAL_ZVAL(zv, copy, dtor)     ZVAL_ZVAL(return_value, zv, copy, dtor)
#define RETVAL_FALSE                    ZVAL_BOOL(return_value, 0)
#define RETVAL_TRUE                     ZVAL_BOOL(return_value, 1)

#define RETURN_RESOURCE(l)              { RETVAL_RESOURCE(l); return; }
#define RETURN_BOOL(b)                  { RETVAL_BOOL(b); return; }
#define RETURN_NULL()                   { RETVAL_NULL(); return;}
#define RETURN_LONG(l)                  { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d)                { RETVAL_DOUBLE(d); return; }
#define RETURN_STRING(s, duplicate)     { RETVAL_STRING(s, duplicate); return; }
#define RETURN_STRINGL(s, l, duplicate) { RETVAL_STRINGL(s, l, duplicate); return; }
#define RETURN_EMPTY_STRING()           { RETVAL_EMPTY_STRING(); return; }
#define RETURN_ZVAL(zv, copy, dtor)     { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE                    { RETVAL_FALSE; return; }
#define RETURN_TRUE                     { RETVAL_TRUE; return; }
로그인 후 복사

其实,除了这些标量类型,还有很多php语言中的复合类型我们需要在函数中返回,如数组和对象,我们可以通过RETVAL_ZVAL与RETURN_ZVAL来操作它们

10、hashTable的遍历函数

//基于long key的操作函数

zval *v3;
MAKE_STD_ZVAL(v3);
ZVAL_STRING(v3, "value3", 1);
zend_hash_index_update(names, 0, &v3, sizeof(zval *), NULL);//按数字索引键更新HashTable元素的值

zval **v4;
zend_hash_index_find(names, 1, &v4); //按数字索引获取HashTable元素的值
php_printf("v4 : ");
PHPWRITE(Z_STRVAL_PP(v4), Z_STRLEN_PP(v4));
php_printf("\n");

ulong idx;
idx = zend_hash_index_exists(names, 10);//按数字索引查找HashTable,如果找到返回 1, 反之则返回 0

zend_hash_index_del(names, 2);    //按数字索引删除HashTable元素


//hashTable的遍历函数

zend_hash_internal_pointer_reset(names); //初始化hash指针
zend_hash_internal_pointer_reset_ex(names, &pos);//初始化hash指针,并付值给pos

zend_hash_get_current_data(names, (void**) &val); //获取当前hash存储值,data should be cast to void**, ie: (void**) &data
zend_hash_get_current_data_ex(names, (void**) &val, &pos) == SUCCESS; //获取当前hash存储值

zend_hash_get_current_key(names, &key, &klen, &index, 0) == HASH_KEY_IS_LONG
zend_hash_get_current_key_ex(names, &key, &klen, &index, 0, &pos) == HASH_KEY_IS_LONG; //读取hashtable当前的KEY,返回值会有两种 HASH_KEY_IS_LONG | HASH_KEY_IS_STRING ,分别对应array("value"),array("key"=>"value")两种hashtable

zend_hash_move_forward(names); 
zend_hash_move_forward_ex(names, &pos); //hash指针移至下一位

//HashTable长度
php_printf("%*carray(%d) {\n", depth * 2, ' ', zend_hash_num_elements(Z_ARRVAL_P(zv))
로그인 후 복사

一个简单的函数

function hello_array_strings($arr) {
    if (!is_array($arr)) return NULL;
    printf("The array passed contains %d elements ", count($arr));

    foreach($arr as $data) {
        if (is_string($data)) echo "$data ";
    }
}
로그인 후 복사

PHP内核实现

PHP_FUNCTION(hello_array_strings)
{
    zval *arr, **data;
    HashTable *arr_hash;
    HashPosition pointer;
    int array_count;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arr) == FAILURE) {
        RETURN_NULL();
    }

    arr_hash = Z_ARRVAL_P(arr);
    array_count = zend_hash_num_elements(arr_hash);
    php_printf("The array passed contains %d elements ", array_count);

    for(zend_hash_internal_pointer_reset_ex(arr_hash, &pointer); zend_hash_get_current_data_ex(arr_hash, (void**) &data, &pointer) == SUCCESS; zend_hash_move_forward_ex(arr_hash, &pointer)) {
        if (Z_TYPE_PP(data) == IS_STRING) {

            PHPWRITE(Z_STRVAL_PP(data), Z_STRLEN_PP(data));

            php_printf(" ");

        }
    }

    RETURN_TRUE;
}
로그인 후 복사

 

参考文章 

http://php.find-info.ru/php/016/ch23lev1sec1.html

http://docstore.mik.ua/orelly/webprog/php/ch14_10.htm

http://wiki.jikexueyuan.com/project/extending-embedding-php/2.4.html

http://www.php-internals.com/book/?p=chapt02/02-03-02-opcode

http://blog.csdn.net/phpkernel/article/details/5721134

https://github.com/chenpingzhao/php-ext-trie-filter/blob/master/trie_filter.c

http://aicode.cc/tags/php%E6%89%A9%E5%B1%95/

http://www.phpboy.net/2013-12/28-php-stream-wrapper.html

http://thiniki.sinaapp.com/?p=163

http://top.jobbole.com/26400/

https://github.com/walu/phpbook/blob/master/9.1.md

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/1067638.htmlTechArticlePHP扩展开发相关总结,php扩展总结 1、线程安全宏定义 在TSRM/TSRM.h文件中有如下定义 #define TSRMLS_FETCH() void ***tsrm_ls = (void ***) ts_resource_ex(0,...
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

PHP 언어 개발 시 요청 헤더 오류를 처리하는 방법은 무엇입니까? PHP 언어 개발 시 요청 헤더 오류를 처리하는 방법은 무엇입니까? Jun 10, 2023 pm 05:24 PM

PHP 언어 개발에서 요청 헤더 오류는 일반적으로 HTTP 요청의 일부 문제로 인해 발생합니다. 이러한 문제에는 잘못된 요청 헤더, 요청 본문 누락, 인식할 수 없는 인코딩 형식이 포함될 수 있습니다. 이러한 요청 헤더 오류를 올바르게 처리하는 것이 애플리케이션 안정성과 보안을 보장하는 열쇠입니다. 이 기사에서는 보다 안정적이고 안전한 애플리케이션을 구축하는 데 도움이 되도록 PHP 요청 헤더 오류를 처리하는 몇 가지 모범 사례에 대해 설명합니다. 요청 방법 확인 HTTP 프로토콜은 사용 가능한 요청 방법 세트를 지정합니다(예: GET, POS

PHP의 Ctype 확장을 사용하는 방법은 무엇입니까? PHP의 Ctype 확장을 사용하는 방법은 무엇입니까? Jun 03, 2023 pm 10:40 PM

PHP는 개발자가 다양한 응용 프로그램을 만들 수 있도록 하는 매우 인기 있는 프로그래밍 언어입니다. 그러나 PHP 코드를 작성할 때 문자를 처리하고 유효성을 검사해야 하는 경우가 있습니다. 이것이 PHP의 Ctype 확장이 유용한 곳입니다. 이 기사에서는 PHP의 Ctype 확장을 사용하는 방법을 소개합니다. Ctype 확장이란 무엇입니까? PHP용 Ctype 확장은 문자열의 문자 유형을 확인하는 다양한 기능을 제공하는 매우 유용한 도구입니다. 이러한 함수에는 isalnum이 포함됩니다.

PHP 언어 개발에서 경로 탐색 취약점 보안 문제를 방지하는 방법 PHP 언어 개발에서 경로 탐색 취약점 보안 문제를 방지하는 방법 Jun 10, 2023 am 09:43 AM

인터넷 기술의 발전으로 점점 더 많은 웹사이트와 애플리케이션이 PHP 언어를 사용하여 개발되고 있습니다. 그러나 보안 문제도 발생합니다. 일반적인 보안 문제 중 하나는 경로 탐색 취약성입니다. 이 기사에서는 애플리케이션 보안을 보장하기 위해 PHP 언어 개발에서 경로 탐색 취약점을 방지하는 방법을 살펴보겠습니다. 경로 순회 취약점이란 무엇입니까? 경로 탐색 취약점(PathTraversal)은 공격자가 인증 없이 웹 서버에 접근할 수 있도록 허용하는 일반적인 웹 취약점입니다.

PHP 프로그래밍에서 Behat을 사용하는 방법은 무엇입니까? PHP 프로그래밍에서 Behat을 사용하는 방법은 무엇입니까? Jun 12, 2023 am 08:39 AM

PHP 프로그래밍에서 Behat은 프로그래머가 개발 프로세스 중 비즈니스 요구 사항을 더 잘 이해하고 코드 품질을 보장하는 데 도움이 되는 매우 유용한 도구입니다. 이 기사에서는 PHP 프로그래밍에서 Behat을 사용하는 방법을 소개합니다. 1. 베하트란 무엇인가요? Behat은 언어 설명(Gherkin 언어로 작성된 사용 사례)을 통해 PHP 코드를 결합하여 코드와 비즈니스 요구 사항이 함께 작동할 수 있도록 하는 BDD(행동 중심 개발) 프레임워크입니다. Behat을 사용하여 수행할 작업

PHP에서 단위 테스트를 위해 Phpt를 사용하는 방법 PHP에서 단위 테스트를 위해 Phpt를 사용하는 방법 Jun 27, 2023 am 08:35 AM

현대 개발에서는 단위 테스트가 필수 단계가 되었습니다. 코드가 예상대로 작동하고 언제든지 버그를 수정할 수 있는지 확인하는 데 사용할 수 있습니다. PHP 개발에서 Phpt는 단위 테스트를 작성하고 실행하는 데 매우 편리한 매우 인기 있는 단위 테스트 도구입니다. 이번 글에서는 단위 테스트를 위해 Phpt를 사용하는 방법을 살펴보겠습니다. 1. Phpt란 무엇입니까Phpt는 PHP 테스트의 일부인 간단하지만 강력한 단위 테스트 도구입니다. Phpt 테스트 케이스는 일련의 PHP 소스 코드 조각입니다.

PHP 언어는 다양한 주석 스타일을 지원합니다. PHP 언어는 다양한 주석 스타일을 지원합니다. Feb 15, 2022 pm 02:05 PM

PHP 언어는 3가지 주석 스타일을 지원합니다. 1. "//" 기호와 "//comment content" 구문을 사용하는 C++ 스타일 2. "/* */" 기호와 "/* 구문을 사용하는 C 언어 스타일; comment content*" /"; 3. "#" 기호와 "#comment content" 구문을 사용하는 쉘 스타일(Perl 스타일).

PHP 언어 개발에서 JSON을 구문 분석할 때 발생하는 일반적인 오류 및 해결 방법 PHP 언어 개발에서 JSON을 구문 분석할 때 발생하는 일반적인 오류 및 해결 방법 Jun 10, 2023 pm 12:00 PM

PHP 언어 개발에서는 후속 데이터 처리 및 작업을 위해 JSON 데이터를 구문 분석해야 하는 경우가 많습니다. 하지만 JSON을 파싱할 때 다양한 오류와 문제가 발생하기 쉽습니다. 이 기사에서는 PHP 개발자가 JSON 데이터를 더 잘 처리하는 데 도움이 되는 일반적인 오류와 처리 방법을 소개합니다. 1. JSON 형식 오류 가장 일반적인 오류는 JSON 형식이 올바르지 않다는 것입니다. JSON 데이터는 JSON 사양을 준수해야 합니다. 즉, 데이터는 키-값 쌍의 컬렉션이어야 하며 중괄호({})와 대괄호([])를 사용하여 데이터를 포함해야 합니다.

PHP 언어 개발에서 LDAP 관련 취약점을 방지하는 방법은 무엇입니까? PHP 언어 개발에서 LDAP 관련 취약점을 방지하는 방법은 무엇입니까? Jun 10, 2023 pm 09:18 PM

LDAP(Lightweight Directory Access Protocol)는 디렉터리 서비스에 액세스하고 관리하는 데 사용되는 일반적인 네트워크 프로토콜입니다. PHP 언어 개발에서 LDAP는 ID 인증 및 사용자 인증과 같은 외부 LDAP 디렉토리 서비스와 상호 작용하는 데 자주 사용됩니다. 그러나 LDAP의 특성상 LDAP 주입, LDAP 재정의 문제 등 일부 보안 취약점도 존재합니다. 이 기사에서는 PHP 언어 개발에서 LDAP 관련 취약점을 방지하는 방법을 살펴보겠습니다. LDAP 주입 방지 LDAP 주입은 다음과 같은 일반적인 보안 취약점입니다.

See all articles