Skip to content

PHP Extension Develop and Test

yourlovemyall edited this page Oct 26, 2017 · 7 revisions

PHP扩展说明

PHP扩展是php扩展的功能。常见的扩展有哪些呢?

1,mysql

2,redis

3,DES加密等

一般扩展的安装步奏

Linux 编译安装

$ /$path/phpize
$ ./configure + php-cofnig
$ make && make install
$ 添加生成的 ext.so 到 php.ini

不同扩展部署测试

  1. config.m4为配置定义文件,用来读取编译命令。
  2. myext.so为编译后生成的二进制文件。
  3. 代码在myext.c中,编译完成并且加入到php.ini后。
  4. 运行 $ php test.php

那么扩展开发包含哪些部分呢

以下是ext_skel可以帮你生成的基本框架,本节课主要是以ext_skel生成的目录结构解析。

  1. config.m4
  2. php_{extname}.h
  3. {extname}.c

config.m4 主要包含哪些内容?

扩展没有引用外部组件, 则使用enable,否则用with。以下说明都是采用enable的模式,使用with的扩展比如mysql等等

dnl $Id$
dnl config.m4 for extension myext

dnl Comments in this file start with the string 'dnl'.
dnl Remove where necessary. This file will not work
dnl without editing.

dnl If your extension references something external, use with:

dnl PHP_ARG_WITH(myext, for myext support,
dnl Make sure that the comment is aligned:
dnl [  --with-myext             Include myext support])

dnl Otherwise use enable:

PHP_ARG_ENABLE(myext, whether to enable myext support, 
Make sure that the comment is aligned:
[  --enable-myext           Enable myext support])

if test "$PHP_MYEXT" != "no"; then
  dnl Write more examples of tests here...

  dnl # --with-myext -> check with-path
  dnl SEARCH_PATH="/usr/local /usr"     # you might want to change this
  dnl SEARCH_FOR="/include/myext.h"  # you most likely want to change this
  dnl if test -r $PHP_MYEXT/$SEARCH_FOR; then # path given as parameter
  dnl   MYEXT_DIR=$PHP_MYEXT
  dnl else # search default path list
  dnl   AC_MSG_CHECKING([for myext files in default path])
  dnl   for i in $SEARCH_PATH ; do
  dnl     if test -r $i/$SEARCH_FOR; then
  dnl       MYEXT_DIR=$i
  dnl       AC_MSG_RESULT(found in $i)
  dnl     fi
  dnl   done
  dnl fi
  dnl
  dnl if test -z "$MYEXT_DIR"; then
  dnl   AC_MSG_RESULT([not found])
  dnl   AC_MSG_ERROR([Please reinstall the myext distribution])
  dnl fi

  dnl # --with-myext -> add include path
  dnl PHP_ADD_INCLUDE($MYEXT_DIR/include)

  dnl # --with-myext -> check for lib and symbol presence
  dnl LIBNAME=myext # you may want to change this
  dnl LIBSYMBOL=myext # you most likely want to change this 

  dnl PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL,
  dnl [
  dnl   PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $MYEXT_DIR/lib, MYEXT_SHARED_LIBADD)
  dnl   AC_DEFINE(HAVE_MYEXTLIB,1,[ ])
  dnl ],[
  dnl   AC_MSG_ERROR([wrong myext lib version or lib not found])
  dnl ],[
  dnl   -L$MYEXT_DIR/lib -lm
  dnl ])
  dnl
  dnl PHP_SUBST(MYEXT_SHARED_LIBADD)

  PHP_NEW_EXTENSION(myext, myext.c, $ext_shared)
fi

已上文件内容包含了那些语义?

AC_MSG_CHECKING(message)
在执行 configure 命令时输出“checking <message>”等信息。

AC_MSG_RESULT(value)
取得 AC_MSG_CHECKING 的执行结果,一般情况下 value 应为 yes 或 no。

AC_MSG_ERROR(message)
在执行 configure 命令时输出一条错误消息 message 并中止脚本的执行。

AC_DEFINE(name,value,description)
向 php_config.h 添加一行定义:#define name value // description (这对模块的条件编译很有用。)

AC_ADD_INCLUDE(path)
添加一条编译器的包含路径,比如用于模块需要为头文件添加搜索路径。

AC_ADD_LIBRARY_WITH_PATH(libraryname,librarypath)
指定一个库的连接路径。

AC_ARG_WITH(modulename,description,unconditionaltest,conditionaltest)
这是一款比较强大的宏,用于将模块的描述 description 添加到“configure –help”命令的输出里面。PHP 会检查当前执行的 configure 脚本里面有没有–with-<modulename> 这个选项。 如果有则执行 unconditionaltest 语句(比如 –with-myext=yes 等), 此时,选项的值会被包含在 $withval 变量里面。否则就执行 conditionaltest 语句。

PHP_NEW_EXTENSION(myredis, myredis.c library.c, $ext_shared)

{extname}.c核心代码文件部分

zend_module_entry

zend_module_entry myext_module_entry = {
 #if ZEND_MODULE_API_NO >= 20010901
         STANDARD_MODULE_HEADER,
 #endif
         "myext",
         NULL,   /*const struct _zend_function_entry *functions*/
         PHP_MINIT(myext),
         PHP_MSHUTDOWN(myext),
         PHP_RINIT(myext),               /* Replace with NULL if there's nothing to do at request start */
         PHP_RSHUTDOWN(myext),   /* Replace with NULL if there's nothing to do at request end */
         PHP_MINFO(myext),
 #if ZEND_MODULE_API_NO >= 20010901
         PHP_MYEXT_VERSION,
 #endif
         STANDARD_MODULE_PROPERTIES
};

打开“php_myext.h”,会看到里面有怎么一行:

extern zend_module_entry myext_module_entry;

那么该PHP Extension的源码结构是如何的呢?(Zend/zend_modules.h)

typedef struct _zend_module_entry zend_module_entry;
 
struct _zend_module_entry {
    unsigned short size;
    unsigned int zend_api;
    unsigned char zend_debug;
    unsigned char zts;
    const struct _zend_ini_entry *ini_entry;
    const struct _zend_module_dep *deps;
    const char *name;
    const struct _zend_function_entry *functions;
    int (*module_startup_func)(INIT_FUNC_ARGS);
    int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
    int (*request_startup_func)(INIT_FUNC_ARGS);
    int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
    void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
    const char *version;
    size_t globals_size;
#ifdef ZTS
    ts_rsrc_id* globals_id_ptr;
#else
    void* globals_ptr;
#endif
    void (*globals_ctor)(void *global TSRMLS_DC);
    void (*globals_dtor)(void *global TSRMLS_DC);
    int (*post_deactivate_func)(void);
    int module_started;
    unsigned char type;
    void *handle;
    int module_number;
    char *build_id;
};

上面生成的结构体中和已上源码结构体是匹配上的。其中有几个指针函数(module_startup_func,module_shutdown_func,request_startup_func,request_shutdown_func),这四个函数会在相应时机被调用,分别是“扩展模块加载时”、“扩展模块卸载时”、“每个请求开始时”和“每个请求结束时”。这四个函数可以看成是一种拦截机制,主要用于相应时机的资源分配、释放等相关操作。 (后面再说)

独立函数demo (自行尝试)

ZEND_FUNCTION(say_hello)
{
    php_printf("Hello World!");
}

static zend_function_entry say_hello_functions[] = {
    ZEND_FE(say_hello, NULL)
    {
        NULL,
        NULL,
        NULL
    }
};

zend_module_entry gglinux_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
     STANDARD_MODULE_HEADER,
#endif
    "say_hello", 
    say_hello_functions, /* Functions */
    NULL, /* MINIT */
    NULL, /* MSHUTDOWN */
    NULL, /* RINIT */
    NULL, /* RSHUTDOWN */
    NULL, /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
    "2.1",
#endif
    STANDARD_MODULE_PROPERTIES
};

上面我们了解了如何注册一个函数到自定义模块中,那么下面我们该如何传入参数和获取返回值呢?

返回值如何返回

PHP内核定义

#define INTERNAL_FUNCTION_PARAMETERS int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC

以上定义说明

int ht
zval *return_value,我们在函数内部修改这个指针,函数执行完成后,内核将把这个指针指向的zval返回给用户端的函数调用者。
zval **return_value_ptr,
zval *this_ptr,如果此函数是一个类的方法,那么这个指针的含义和PHP语言中$this变量差不多。
int return_value_used,代表用户端在调用此函数时有没有使用到它的返回值。

以上return_value这个指针用来保存用户返回的结果值。那么扩展中是如何写的呢?

ZVAL_LONG(return_value, 42);

宏展开后

Z_TYPE_P(return_value) = IS_LONG;
Z_LVAL_P(return_value) = 42;
 
//更彻底的讲,应该是这样的:
return_value->type = IS_LONG;
return_value->value.lval = 42;

return_value有关的宏

return_value如此重要,内核肯定早已经为它准备了大量的宏,来简化我们的操作,提高程序的质量。 在前几章我们接触的宏大多都是以ZVAL_开头的,而接下来我们要介绍的宏的名字是:RETVAL。 再回到上面的那个例子,我们用RETVAL来重写一下:

RETVAL_LONG(42);
// 下面是其他的例子
/*
//这些宏都定义在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 
function sample_array_range() {
    $ret = array();
    for($i = 0; $i < 1000; $i++) {
        $ret[] = $i;
    }
    return $ret;
}
sample_array_range();

上面这种情况运行了一下 但是并没有将数据返回给调用者,那么就可以使用变量return_value_used来减少内存使用。当然这个例子比较极端。

ZEND_FUNCTION(sample_array_range)
{
    if (return_value_used) {
        int i;
         
        //把返回值初始化成一个PHP语言中的数组
        array_init(return_value);
        for(i = 0; i < 1000; i++)
        {
            //向retrun_value里不断的添加新元素,值为i
            add_next_index_long(return_value, i);
        }
        return;
    }
    else
    {
        //抛出一个E_NOTICE级错误
        php_error_docref(NULL TSRMLS_CC, E_NOTICE,"猫了个咪的,我就知道你没用我的劳动成果!");
        RETURN_NULL();
    }
}

还有其他引用返回等等,感兴趣的可以自行研究一下。扩展函数的返回值已经简单的说完了,那么输入参数如何传入呢?

参数输入获取

那我们举个简单例子, PHP参数输入是通过zend api的zend_parse_parameters函数获取的。

ZEND_FUNCTION(sample_getlong) {
 
    long foo;
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"l", &foo) == FAILURE)
    {
        RETURN_NULL();
    }
    php_printf("The integer value of the parameter is: %ld\n", foo);
    RETURN_TRUE;
}

long, string, resource等等都有不同的获取标识符号。string需要两个字段接受,一个是字符串,一个是长度。

在了解完函数的输入参数和返回结果之后,那么如果定义一个类对象呢?

类对象的实现

下面是一个redis demo的类对象的编写代码。我们来介绍一下核心代码实现。

INIT_CLASS_ENTRY(redis_class_entry, "Myredis", myredis_functions);  //注册类
redis_ce = zend_register_internal_class(&redis_class_entry TSRMLS_CC);

1,首先我们看一下INIT_CLASS_ENTRY这个宏的具体展开式实现

#define INIT_CLASS_ENTRY(class_container, class_name, functions)	INIT_OVERLOADER_CLASS_ENTRY(class_container, class_name, functions, NULL, NULL, NULL)
#define INIT_OVERLOADER_CLASS_ENTRY(class_container, class_name, functions, handle_fcall, handle_propget, handle_propset) \
	INIT_OVERLOADER_CLASS_ENTRY_EX(class_container, class_name, sizeof(class_name)-1,functions, handle_fcall, handle_propget, handle_propset)
#define INIT_OVERLOADER_CLASS_ENTRY_EX(class_container, class_name, class_name_len, functions, handle_fcall, handle_propget, handle_propset) 
{
	const char * cl_name = class_name;
	int _len = class_name_len;
	class_container.name = zend_new_interned_string(cl_name, _len+1, 0 TSRMLS_CC);
	if(class_container.name == cl_name){
		//??
		class_container.name = zend_strndup(cl_name, _len);
	}
	class_container.name_length = _len;
	INIT_CLASS_ENTRY_INIT_METHODS(class_container, functions, handle_fcall, handle_propget, handle_propset)
}
#define INIT_CLASS_ENTRY_INIT_METHODS(class_container, functions, handle_fcall, handle_propget, handle_propset)
{
        class_container.builtin_functions = functions;          
        class_container.constructor = NULL;                     
        class_container.destructor = NULL;                      
        class_container.clone = NULL;                           
        class_container.serialize = NULL;                       
        class_container.unserialize = NULL;                     
        class_container.create_object = NULL;                   
        class_container.interface_gets_implemented = NULL;      
        class_container.__call = handle_fcall;                  
        class_container.__tostring = NULL;                      
        class_container.__get = handle_propget;                 
        class_container.__set = handle_propset;                 
        class_container.__unset = NULL;             
        class_container.__isset = NULL;             
        class_container.serialize_func = NULL;                  
        class_container.unserialize_func = NULL;                
        class_container.serialize = NULL;                       
        class_container.unserialize = NULL;                     
        class_container.parent = NULL;                          
        class_container.num_interfaces = 0;                     
        class_container.interfaces = NULL;                      
        class_container.get_iterator = NULL;                    
        class_container.iterator_funcs.funcs = NULL;            
        class_container.module = NULL;                          
}
struct _zend_class_entry {
    char type;     // �类型:ZEND_INTERNAL_CLASS / ZEND_USER_CLASS
    char *name;// 类名称
    zend_uint name_length;                  // 即sizeof(name) - 1
    structͺ_zend_class_entry *parent; // 继承的父类
    intͺrefcount;  // 引用数
    zend_bool constants_updated;
  
    zend_uint ce_flags; // ZEND_ACC_IMPLICIT_ABSTRACT_CLASS: 类存在abstract方法
    // ZEND_ACC_EXPLICIT_ABSTRACT_CLASS: 在类名称前加abstract关键字
     // ZEND_ACC_FINAL_CLASS
    // ZEND_ACC_INTERFACE
    HashTable function_table;      // �方法
    HashTable default_properties;          // 默认属性
    HashTable properties_info;     // 属性信息
    HashTable default_static_members;// �类本身所具有的静态变量
    HashTable *static_members; // type == ZEND_USER_CLASS时,取&default_static_members;
    // type == ZEND_INTERAL_CLASS时,设�NULL
    HashTable constants_table;     // 常量
    struct _zend_function_entry *builtin_functions;// 方法定义入口
  
  
    union _zend_function *constructor;
    union _zend_function *destructor;
    union _zend_function *clone;
  
  
    /* 魔术方法 */
    union _zend_function *__get;
    union _zend_function *__set;
    union _zend_function *__unset;
    union _zend_function *__isset;
    union _zend_function *__call;
    union _zend_function *__tostring;
    union _zend_function *serialize_func;
    union _zend_function *unserialize_func;
    zend_class_iterator_funcs iterator_funcs;// 迭代
  
    /* 类句柄 */
    zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC);
    zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object,
        intby_ref TSRMLS_DC);
  
    /* 类声明的接口 */
    int(*interface_gets_implemented)(zend_class_entry *iface,
            zend_class_entry *class_type TSRMLS_DC);
  
  
    /* 序列化回调函数指针 */
    int(*serialize)(zval *object unsignedchar**buffer, zend_uint *buf_len,
             zend_serialize_data *data TSRMLS_DC);
    int(*unserialize)(zval **object, zend_class_entry *ce, 
constunsignedchar*buf,
            zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC);
  
  
    zend_class_entry **interfaces;  // 类实现的接口
    zend_uint num_interfaces;   // 类实现的接口数
  
    char *filename; // 类的存放文件地址 绝对地址
    zend_uint line_start;   // 类定义的开始行 �
    zend_uint line_end; // 类定义的结束行
    char *doc_comment;
    zend_uint doc_comment_len;
  
  
    struct _zend_module_entry *module; // 类所在的模块入口EG(current_module)
};

上面的代码段执行了其实就是zend_class_entry对象的初始化。

static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class_entry, zend_uint ce_flags TSRMLS_DC) /* {{{ */
{
   zend_class_entry *class_entry = malloc(sizeof(zend_class_entry));
   char *lowercase_name = emalloc(orig_class_entry->name_length + 1);
   *class_entry = *orig_class_entry;

   class_entry->type = ZEND_INTERNAL_CLASS;
   zend_initialize_class_data(class_entry, 0 TSRMLS_CC);
   class_entry->ce_flags = ce_flags;
   class_entry->info.internal.module = EG(current_module);

   if (class_entry->info.internal.builtin_functions) {
      zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, MODULE_PERSISTENT TSRMLS_CC);
   }

   zend_str_tolower_copy(lowercase_name, orig_class_entry->name, class_entry->name_length);
   lowercase_name = (char*)zend_new_interned_string(lowercase_name, class_entry->name_length + 1, 1 TSRMLS_CC);
   if (IS_INTERNED(lowercase_name)) {
      zend_hash_quick_update(CG(class_table), lowercase_name, class_entry->name_length+1, INTERNED_HASH(lowercase_name), &class_entry, sizeof(zend_class_entry *), NULL);
   } else {
      zend_hash_update(CG(class_table), lowercase_name, class_entry->name_length+1, &class_entry, sizeof(zend_class_entry *), NULL);
   }
   str_efree(lowercase_name);
   return class_entry;
}

该函数主要用于类对象的在PHP内存分配使用,???

那么说完PHP类对象创建的过程后,那我们开始说这种创建的类对象的自定义函数是怎么绑定关联的。

1,首先我们先介绍几种常见的函数宏。

普通函数

PHP_FUNCTION(func_name)
等价于void {func_name}(INTERNAL_FUNCTION_PARAMETERS)

类函数

PHP_METHOD(class_name, func_name)
等价于zim_{class_name}_{func_name}
PHP_ME(class_name, func_name, arg_info, flags)
等价于 {class_name, zim_{class_name} _{func_name}, arg_info, 
(zend_uint)(sizeof(arg_info)/sizeof(struct _zend_arg_info) - 1),flags},

下面是一个简单的关联类函数的demo片段。

const zend_function_entry myext_functions[] = {
         PHP_ME(Myext, __construct, NULL, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC)
         PHP_ME(Myext, __destruct, NULL, ZEND_ACC_DTOR | ZEND_ACC_PUBLIC)
         PHP_ME(Myext, close, NULL, ZEND_ACC_PUBLIC)
         PHP_ME(Myext, getFile, NULL, ZEND_ACC_PUBLIC)
         PHP_ME(Myext, getHttp, NULL, ZEND_ACC_PUBLIC)
         {NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(myext)
{
      zend_class_entry ext_class_entry;
      /* ext class */
      INIT_CLASS_ENTRY(ext_class_entry, "Myext", myext_functions);
      ext_ce = zend_register_internal_class(&ext_class_entry TSRMLS_CC);
      return SUCCESS;
}
PHP_METHOD(Myext, getHttp){
     php_stream *stream;
     char inbuf[1024];
     zval *object;
     char *host, *path, *errstr = NULL;
     int host_len, path_len, implicit_tcp = 1, errcode = 0;
     long port;
     int options = ENFORCE_SAFE_MODE;
     char *content = "";
     int flags = STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT;
     if(zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss",
                                     &object, ext_ce, &host, &host_len, &path, &path_len) == FAILURE)
     {
         RETURN_FALSE;
     }
 
     stream = php_stream_xport_create(host, host_len,
             options, flags,
             NULL, NULL, NULL, &errstr, &errcode);
     if (errstr) {
         php_error_docref(NULL TSRMLS_CC, E_WARNING, "[%d] %s",
                 errcode, errstr);
         efree(errstr);
     }
     if (!stream) {
         RETURN_FALSE;
     }
 
     char getContent[4096];
     sprintf(getContent, "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3\r\nConnection:close\r\n\r\n" ,path,host);
         php_stream_write(stream, getContent, sizeof(getContent));
         while(php_stream_gets(stream, inbuf, sizeof(inbuf)) != NULL) {
         content = mystrcat(content, inbuf);
     }
     RETURN_STRING(content, 1); 
}

那么已上就是常见的独立函数开发,已经类对象的开发过程。

基础理解的常用结构体如下

_zval_struct
zend_class_entry
zend_function_entry
HashTable

插一下PHP弱类型变量数据结构实现。 简单说一下PHP内存回收机制。

struct _zval_struct {
    zvalue_value value;     /* value */
    zend_uint refcount__gc;  /* variable ref count */
    zend_uchar type;          /* active type */
    zend_uchar is_ref__gc;    /* if it is a ref variable */
};
typedef struct _zval_struct zval;
typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {                    /* string */
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value,used for array */
    zend_object_value obj;      /* object */
} zvalue_value;

PHP HashTable机制

/* zend_hash.c zend_hash.h */
ZEND_API ulong zend_hash_func(const char *arKey, uint nKeyLength) 
ZEND_API ulong zend_get_hash_value(const char *arKey, uint nKeyLength) 
ZEND_API int _zend_hash_init(HashTable *ht, uint nSize, hash_func_t pHashFunction, dtor_func_t pDestructor, zend_bool persistent ZEND_FILE_LINE_DC)
 ZEND_API void zend_hash_set_apply_protection(HashTable *ht, zend_bool bApplyProtection)
ZEND_API int _zend_hash_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
ZEND_API int _zend_hash_quick_add_or_update(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
ZEND_API int _zend_hash_index_update_or_next_insert(HashTable *ht, ulong h, void *pData, uint nDataSize, void **pDest, int flag ZEND_FILE_LINE_DC)
ZEND_API int zend_hash_rehash(HashTable *ht)
static int zend_hash_do_resize(HashTable *ht)
ZEND_API int zend_hash_del_key_or_index(HashTable *ht, const char *arKey, uint nKeyLength, ulong h, int flag)
ZEND_API void zend_hash_destroy(HashTable *ht)
ZEND_API void zend_hash_clean(HashTable *ht)
static Bucket *zend_hash_apply_deleter(HashTable *ht, Bucket *p)
ZEND_API void zend_hash_graceful_destroy(HashTable *ht)
ZEND_API void zend_hash_graceful_reverse_destroy(HashTable *ht)
ZEND_API void zend_hash_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
ZEND_API void zend_hash_apply_with_argument(HashTable *ht, apply_func_arg_t apply_func, void *argument TSRMLS_DC)
ZEND_API void zend_hash_apply_with_arguments(HashTable *ht TSRMLS_DC, apply_func_args_t apply_func, int num_args, …)
ZEND_API void zend_hash_reverse_apply(HashTable *ht, apply_func_t apply_func TSRMLS_DC)
ZEND_API void zend_hash_copy(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size)
ZEND_API void _zend_hash_merge(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, void *tmp, uint size, int overwrite ZEND_FILE_LINE_DC)
static zend_bool zend_hash_replace_checker_wrapper(HashTable *target, void *source_data, Bucket *p, void *pParam, merge_checker_func_t merge_checker_func)
ZEND_API void zend_hash_merge_ex(HashTable *target, HashTable *source, copy_ctor_func_t pCopyConstructor, uint size, merge_checker_func_t pMergeSource, void *pParam)    
ZEND_API int zend_hash_find(const HashTable *ht, const char *arKey, uint nKeyLength, void **pData)
ZEND_API int zend_hash_quick_find(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h, void **pData)
ZEND_API int zend_hash_exists(const HashTable *ht, const char *arKey, uint nKeyLength)
ZEND_API int zend_hash_quick_exists(const HashTable *ht, const char *arKey, uint nKeyLength, ulong h)
ZEND_API int zend_hash_index_find(const HashTable *ht, ulong h, void **pData)
ZEND_API int zend_hash_index_exists(const HashTable *ht, ulong h) 
ZEND_API int zend_hash_num_elements(const HashTable *ht)
ZEND_API int zend_hash_get_pointer(const HashTable *ht, HashPointer *ptr)
ZEND_API int zend_hash_set_pointer(HashTable *ht, const HashPointer *ptr)
ZEND_API void zend_hash_internal_pointer_reset_ex(HashTable *ht, HashPosition *pos)
ZEND_API void zend_hash_internal_pointer_end_ex(HashTable *ht, HashPosition *pos)
ZEND_API int zend_hash_move_forward_ex(HashTable *ht, HashPosition *pos)
ZEND_API int zend_hash_move_backwards_ex(HashTable *ht, HashPosition *pos)
ZEND_API int zend_hash_get_current_key_ex(const HashTable *ht, char **str_index, uint *str_length, ulong *num_index, zend_bool duplicate, HashPosition *pos)
ZEND_API int zend_hash_get_current_key_type_ex(HashTable *ht, HashPosition *pos)
ZEND_API int zend_hash_get_current_data_ex(HashTable *ht, void **pData, HashPosition *pos)
ZEND_API int zend_hash_update_current_key_ex(HashTable *ht, int key_type, const char *str_index, uint str_length, ulong num_index, int mode, HashPosition *pos)
ZEND_API int zend_hash_sort(HashTable *ht, sort_func_t sort_func, compare_func_t compar, int renumber TSRMLS_DC) 
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, zend_bool ordered TSRMLS_DC)
ZEND_API int zend_hash_minmax(const HashTable *ht, compare_func_t compar, int flag, void **pData TSRMLS_DC)
ZEND_API ulong zend_hash_next_free_element(const HashTable *ht)
void zend_hash_display_pListTail(const HashTable *ht)
void zend_hash_display(const HashTable *ht)   

自定义DEMO (网上看到的DEMO,可以自行体验hashTable的API用法, 主要是在一些类对象的获取该对象的时候可能会使用。)

ZEND_FUNCTION(gglinux_test_hashtable)
{
    HashTable *test_ht;
    int ht_len = 10;
    test_ht = test_hashtable_create(test_ht,ht_len);
    test_hashtable_scan(test_ht);
    //毁尸灭迹
    zend_hash_destroy(test_ht);
 
    FREE_HASHTABLE(test_ht);
    return SUCCESS;

    
}

/*
   新建哈希表
*/
void test_hashtable_create(HashTable *ht, int hashtable_len)
{
        //分配内存
    ALLOC_HASHTABLE(ht);

    //初始化
    if (zend_hash_init(ht, hashtable_len, NULL,ZVAL_PTR_DTOR, 0) == FAILURE) {
        FREE_HASHTABLE(ht);
        return FAILURE;
    }
    int i;

    for (i = 0; i < hashtable_len; i++)
    {
        zval *value;
        MAKE_STD_ZVAL(value);
        ZVAL_LONG(value, i);
        i++;
        //添加数据(不需要索引值参数)
        if(zend_hash_next_index_insert(ht, (void **) &value, sizeof(zval *), NULL) == FAILURE) {
            //更新引用计数
            zval_ptr_dtor(&value);
        }
    }
    return ht;
}

/*
    遍历哈希表 回调方法
*/
int test_print_zval(zval **val TSRMLS_DC)
{
    //重新copy一个zval,防止破坏原数据
    zval tmpcopy = **val;
    zval_copy_ctor(&tmpcopy);
     
    //转换为字符串
    INIT_PZVAL(&tmpcopy);
    convert_to_string(&tmpcopy);
    
    //开始输出
    php_printf("The value is: ");
    PHPWRITE(Z_STRVAL(tmpcopy), Z_STRLEN(tmpcopy));
    php_printf("\n");
     
    //毁尸灭迹
    zval_dtor(&tmpcopy);
     
    //返回,继续遍历下一个
    return ZEND_HASH_APPLY_KEEP;
}

/*
    哈希表遍历
*/
void test_hashtable_scan(HashTable *ht)
{
    zend_hash_apply(ht, test_print_zval TSRMLS_CC);
}

PHP Array 常用方法举例

{
    zval *subarray;
    array_init(return_value);

    // ["life"]=> int(42)
    add_assoc_string(return_value, "name", "gglinux",1);
    // [123]=> bool(true)
    add_index_bool(return_value, 123, 1);
    //[124]=> float(7.77)
    add_next_index_double(return_value, 7.77);
    // [125]=> string(3) "Foo"
    add_next_index_string(return_value, "Foo", 1);
    
    //[126]=> string(3) "Bar"
    //PHP内存管理API都在原来的C++函数前面加了一个“e”,这样的内存分配是被PHP内核管理的。这里就不过多展开了。
    add_next_index_string(return_value, estrdup("Bar"), 0);

    //子数组
    MAKE_STD_ZVAL(subarray);
    array_init(subarray);

    //添加值
    add_next_index_long(subarray, 1);
    add_index_bool(subarray, 123, 1);

    //添加到父串中
    add_index_zval(return_value, 444, subarray);
}

PHP扩展的生命周期 (插一句PHP扩展执行的生命周期过程)

PHP_MINIT (扩展模块加载时调用)

PHP_MSHUTDOWN (扩展模块加载时调用)

PHP_RINIT (每个请求开始时)

PHP_RSHUTDOWN (每个请求结束时)

Clone this wiki locally