php 扩展学习笔记

php扩展开发参考资料

  1. https://github.com/baixiaoshi/phpbook/blob/master/preface.md
  2. http://www.web3.xin/manual/detail/id/197.html?page=2
  3. http://www.php-internals.com/book/
  4. https://yuerblog.cc/ php7学习博客
php的这些个过程
PHP_MINIT_FUNCTION  初始化module时运行 
PHP_MSHUTDOWN_FUNCTIONmodule被卸载时运行 
PHP_RINIT_FUNCTION  当一个REQUEST请求初始化时运行 
PHP_RSHUTDOWN_FUNCTION  当一个REQUEST请求结束时运行 
PHP_MINFO_FUNCTION  这个是设置phpinfo中这个模块的信息 
PHP_GINIT_FUNCTION  初始化全局变量时 
PHP_GSHUTDOWN_FUNCTION  释放全局变量时
zval *name;# 申明一个zval变量
MAKE_STD_ZVAL(zval);#申明一个变量,并分配内存
#php内核解析参数
zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zval) ==FAILURE

//php7中变量并不需要MAKE_STD_ZVAL手动分配内存了
//二是直接可以赋值使用
zval *name = "baixiaoshi@husor.com";
//ZVAL_STRING(return_value, name);
RETVAL_STRING(name);

//变量赋值获取
zval *name;
zend_string *teststr = zend_string_init("hello", strlen("hello"), 0);
ZVAL_STR(name, teststr);
zend_string *haha = Z_STR_P(name);
php_printf("haha=%s\n", ZSTR_VAL(haha));
//判断变量类型
#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)
//下面为引用内容
PHP7将变量的引用计数转移到了具体的value上,所以zval更多的是作为统一的传输格式,很多情况下只是临时性使用,比如函数调用时的传参,最终需要的数据是zval携带的zend_value,函数从zval取得zend_value后就不再关心zval了,这种就可以直接在栈上分配zval。分配完zval后需要将其设置为我们需要的类型以及设置其zend_value,PHP中定义的ZVAL_XXX()系列宏就是用来干这个的,这些宏第一个参数z均为要设置的zval的指针,后面为要设置的zend_value。

ZVAL_UNDEF(z): 表示zval被销毁
ZVAL_NULL(z): 设置为NULL
ZVAL_FALSE(z): 设置为false
ZVAL_TRUE(z): 设置为true
ZVAL_BOOL(z, b): 设置为布尔型,b为IS_TRUE、IS_FALSE,与上面两个等价
ZVAL_LONG(z, l): 设置为整形,l类型为zend_long,如:zval z; ZVAL_LONG(&z, 88);
ZVAL_DOUBLE(z, d): 设置为浮点型,d类型为double
ZVAL_STR(z, s): 设置字符串,将z的value设置为s,s类型为zend_string*,不会增加s的refcount,支持interned strings
ZVAL_NEW_STR(z, s): 同ZVAL_STR(z, s),s为普通字符串,不支持interned strings
ZVAL_STR_COPY(z, s): 将s拷贝到z的value,s类型为zend_string*,同ZVAL_STR(z, s),这里会增加s的refcount
ZVAL_ARR(z, a): 设置为数组,a类型为zend_array*
ZVAL_NEW_ARR(z): 新分配一个数组,主动分配一个zend_array
ZVAL_NEW_PERSISTENT_ARR(z): 创建持久化数组,通过malloc分配,需要手动释放
ZVAL_OBJ(z, o): 设置为对象,o类型为zend_object*
ZVAL_RES(z, r): 设置为资源,r类型为zend_resource*
ZVAL_NEW_RES(z, h, p, t): 新创建一个资源,h为资源handle,t为type,p为资源ptr指向结构
ZVAL_REF(z, r): 设置为引用,r类型为zend_reference*
ZVAL_NEW_EMPTY_REF(z): 新创建一个空引用,没有设置具体引用的value
ZVAL_NEW_REF(z, r): 新创建一个引用,r为引用的值,类型为zval*
//下面为引用内容
zval的类型通过Z_TYPE(zval)、Z_TYPE_P(zval*)两个宏获取,这个值取的就是zval.u1.v.type,但是设置时不要只修改这个type,而是要设置typeinfo,因为zval还有其它的标识需要设置,比如是否使用引用计数、是否可被垃圾回收、是否可被复制等等。

内核提供了Z_XXX(zval)、Z_XXX_P(zval*)系列的宏用于获取不同类型zval的value。

Z_LVAL(zval)、Z_LVAL_P(zval_p): 返回zend_long
Z_DVAL(zval)、Z_DVAL_P(zval_p): 返回double
Z_STR(zval)、Z_STR_P(zval_p): 返回zend_string*
Z_STRVAL(zval)、Z_STRVAL_P(zval_p): 返回char*,即:zend_string->val
Z_STRLEN(zval)、Z_STRLEN_P(zval_p): 获取字符串长度
Z_STRHASH(zval)、Z_STRHASH_P(zval_p): 获取字符串的哈希值
Z_ARR(zval)、Z_ARR_P(zval_p)、Z_ARRVAL(zval)、Z_ARRVAL_P(zval_p): 返回zend_array*
Z_OBJ(zval)、Z_OBJ_P(zval_p): 返回zend_object*
Z_OBJ_HT(zval)、Z_OBJ_HT_P(zval_p): 返回对象的zend_object_handlers,即zend_object->handlers
Z_OBJ_HANDLER(zval, hf)、Z_OBJ_HANDLER_P(zv_p, hf): 获取对象各操作的handler指针,hf为write_property、read_property等,注意:这个宏取到的为只读,不要试图修改这个值(如:Z_OBJ_HANDLER(obj, write_property) = xxx;),因为对象的handlers成员前加了const修饰符
Z_OBJCE(zval)、Z_OBJCE_P(zval_p): 返回对象的zend_class_entry*
Z_OBJPROP(zval)、Z_OBJPROP_P(zval_p): 获取对象的成员数组
Z_RES(zval)、Z_RES_P(zval_p): 返回zend_resource*
Z_RES_HANDLE(zval)、Z_RES_HANDLE_P(zval_p): 返回资源handle
Z_RES_TYPE(zval)、Z_RES_TYPE_P(zval_p): 返回资源type
Z_RES_VAL(zval)、Z_RES_VAL_P(zval_p): 返回资源ptr
Z_REF(zval)、Z_REF_P(zval_p): 返回zend_reference*
Z_REFVAL(zval)、Z_REFVAL_P(zval_p): 返回引用的zval*
除了这些与PHP变量类型相关的宏之外,还有一些内核自己使用类型的宏:

//获取indirect的zval,指向另一个zval
#define Z_INDIRECT(zval)            (zval).value.zv
#define Z_INDIRECT_P(zval_p)        Z_INDIRECT(*(zval_p))

#define Z_CE(zval)                  (zval).value.ce
#define Z_CE_P(zval_p)              Z_CE(*(zval_p))

#define Z_FUNC(zval)                (zval).value.func
#define Z_FUNC_P(zval_p)            Z_FUNC(*(zval_p))

#define Z_PTR(zval)                 (zval).value.ptr
#define Z_PTR_P(zval_p)             Z_PTR(*(zval_p))

zend_parse_params函数中几个特殊的符号含义

! - 可以传null
* - 不定参数,0或者多个
+ - 不定参数,至少传一个参数  a+表示必须传一个数组还有另外一个
| - 竖线后面的参数都是可选参数 a|al
/ - 使函数传参独立拷贝出来

参数array_push

if (zend_parse_parameters(ZEND_NUM_ARGS(), "a/+", &stack, &args, &argc) == FAILURE) {
        return;
    }
//去除特殊字符
zend_string *str = zend_string_init("baixiaoshibai", strlen("baixiaoshibai"), 0);
    zend_string *what = zend_string_init("i", strlen("i"), 0);
    int mode = 2;

    ZVAL_STR(return_value, php_trim(str, (what ? ZSTR_VAL(what) : NULL), (what ? ZSTR_LEN(what) : 0), mode));

    zend_string_release(str);
    zend_string_release(what);
    //zend_string_equals函数测试
    const char *p1 = "hello";
    const char *p2 = "hello1";

    zend_string *str1 = zend_string_alloc(strlen(p1), 0);
    zend_string *str2 = zend_string_alloc(strlen(p2), 0);
    memcpy(ZSTR_VAL(str1), p1, strlen(p1));
    memcpy(ZSTR_VAL(str2), p2, strlen(p2));

    zend_bool boolret = zend_string_equals(str1, str2);

    if (boolret) {
        php_printf("yes");
    } else {
        php_printf("no");
    }
    zend_string_release(str1);
    zend_string_release(str2);
//这种写法一定要学习
memcpy(ZSTR_VAL(foobar) + ZSTR_LEN(FOO), ZSTR_VAL(bar), ZSTR_LEN(bar));
 #下面是类型的列表
Type    Code    Variable Type
Boolean  b       zend_bool
Long     l       long
Double   d       double
String   s       char*, int
Resource r       zval*
Array   a        zval*
Object  o        zval*
zval    z        zval*
#更加全面的类型对照表
type_spec是格式化字符串,其常见的含义如下:

Type Specifiers
Spec    Type    Locals
a   array   zval*
A   array or object     zval*
b   boolean zend_bool
C   class   zend_class_entry*
d   double  double
f   function    zend_fcall_info*, zend_fcall_info_cache*
h   array   HashTable*
H   array or object HashTable*
l   long    long
L   long (limits out-of-range LONG_MAX/LONG_MIN)    long
o   object  zval*
O   object (of specified zend_class_entry)  zval*, zend_class_entry*
p   string (a valid path)   char*, int
P   string (a valid path)   zend_string*
r   resource    zval*
s   string  char*, int
S   string  zend_string*
z   mixed   zval*
Z   mixed   zval**
#获取变量类型
zval num   Z_TYPE(num)
zval *num  Z_TYPE_P(num)
zval **num Z_TYPE_PP(num)
#变量类型列表
IS_NULL,IS_BOOL,IS_LONG,IS_DOUBLE,IS_RESOURCE,IS_ARRAY,IS_OBJECT,IS_STRING

#获取各种zval类型的值
Z_BVAL_P(num) #获取bool型
Z_LVAL_P(num) #获取长整形
Z_DVAL_P(num) #获取double类型
Z_RESVAL_P(num) #获取资源类型
Z_ARRVAL_P(num) #hashtable
Z_STRVAL_P(num) #获取字符串
#注册函数,写在zend_function_entry类型的funcs这个数组中,
#用PHP_FE函数
const zend_function_entry funcs[] = {
    PHP_FE(dump, NULL)
    PHP_FE_END
};

php7操作字符串

    //初始化字符串
    const char* teststr = "Baixiaoshi";
    zend_string *retstr = zend_string_init(teststr, strlen(teststr), 0);
    php_printf("retstr=%s\n", ZSTR_VAL(retstr));
    zend_string_release(retstr);

    RETURN_STR(retstr);
PHPWRITE(ZSTR_VAL(my_str.s), ZSTR_LEN(my_str.s));

//字符串操作就用smart_str
zend_string *extest = zend_string_init("xiaobai", strlen("xiaobai"), 0);

    smart_str my_str = {0};
    smart_str_appends(&my_str, "guoguo");
    smart_str_appendc(&my_str, '@');
    smart_str_append_unsigned(&my_str, zend_hash_num_elements(CG(function_table)));
    smart_str_append_ex(&my_str, extest, 1);
    smart_str_0(&my_str);
    php_printf("my_str=%s\n", ZSTR_VAL(my_str.s));
    smart_str_free(&my_str);
    zend_string_release(extest);
    RETURN_STR(my_str.s);
    char *str = "hello world";
    //字符串在c中是常量,是不能修改的,只能复制之后再修改
    //str[0] = 'i';
    char *ret;
    ret = strdup(str);
    ret[0] = 'i';
    printf("str=%s, retstr=%s\n", str, ret);
#最开始的自己是这样获取的,原来内核中什么都有宏函数搞定
zval *myval;
    if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &myval)) {
        return ;
    }
    zend_uint refcount = myval->refcount__gc;
    zend_uchar is_ref_gc = myval->is_ref__gc;
    php_printf("refcount=%d,is_ref_gc=%d\n", refcount, is_ref_gc);
    if (myval->type == IS_LONG) {
        php_printf("myval=%ld\n", Z_LVAL_P(myval));
    }

    long lval = myval->value.lval;
    double dval = myval->value.dval;
    HashTable *myptr = myval->value.ht;
    char *str = myval->value.str.val;
    zend_uint len = myval->value.str.len;
    php_printf("lval=%ld,dval=%g,str=%s,strlen=%d,hashtable=%p\n", lval, dval, str, len, myptr);
 #为变量复制啊,记住内核中一个函数的返回值直接复制给return_value这个变量就好了
ZVAL_NULL(return_value);

ZVAL_BOOL(return_value, 0);
ZVAL_BOOL(return_value, 1);
/* or better */
ZVAL_FALSE(return_value);
ZVAL_TRUE(return_value);

ZVAL_LONG(return_value, 42);
ZVAL_DOUBLE(return_value, 4.2);
ZVAL_RESOURCE(return_value, resource_id);

ZVAL_EMPTY_STRING(return_value);
/* = ZVAL_STRING(return_value, "", 1); */

ZVAL_STRING(return_value, "string", 1);
/* = ZVAL_STRING(return_value, estrdup("string"), 0); */

ZVAL_STRINGL(return_value, "nul\0string", 10, 1);
/* = ZVAL_STRINGL(return_value, estrndup("nul\0string", 10), 10, 0); */
//别人整理的真清晰
//这些宏都定义在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数组操作

//打印一个hashtable中的一个值的路径,不禁感觉C中的结构体嵌套有点深,在设计的时候是否有哪些原则
 print *stack->value->arr->arData->val->value->str
// 1) key为zend_string

//插入或更新元素,会增加key的refcount
#define zend_hash_update(ht, key, pData) \
        _zend_hash_update(ht, key, pData ZEND_FILE_LINE_CC)

//插入或更新元素,当Bucket类型为indirect时,将pData更新至indirect的值,而不是更新Bucket
#define zend_hash_update_ind(ht, key, pData) \
        _zend_hash_update_ind(ht, key, pData ZEND_FILE_LINE_CC)

//添加元素,与zend_hash_update()类似,不同的地方在于如果元素已经存在则不会更新
#define zend_hash_add(ht, key, pData) \
        _zend_hash_add(ht, key, pData ZEND_FILE_LINE_CC)

//直接插入元素,不管key存在与否,如果存在也不覆盖原来元素,而是当做哈希冲突处理,所有会出现一个数组中key相同的情况,慎用!!!
#define zend_hash_add_new(ht, key, pData) \
        _zend_hash_add_new(ht, key, pData ZEND_FILE_LINE_CC)

// 2) key为普通字符串:char*

//与上面几个对应,这里的key为普通字符串,会自动生成zend_string的key
#define zend_hash_str_update(ht, key, len, pData) \
        _zend_hash_str_update(ht, key, len, pData ZEND_FILE_LINE_CC)
#define zend_hash_str_update_ind(ht, key, len, pData) \
        _zend_hash_str_update_ind(ht, key, len, pData ZEND_FILE_LINE_CC)
#define zend_hash_str_add(ht, key, len, pData) \
        _zend_hash_str_add(ht, key, len, pData ZEND_FILE_LINE_CC)
#define zend_hash_str_add_new(ht, key, len, pData) \
        _zend_hash_str_add_new(ht, key, len, pData ZEND_FILE_LINE_CC)

/ 3) key为数值索引

//插入元素,h为数值
#define zend_hash_index_add(ht, h, pData) \
        _zend_hash_index_add(ht, h, pData ZEND_FILE_LINE_CC)

//与zend_hash_add_new()类似
#define zend_hash_index_add_new(ht, h, pData) \
        _zend_hash_index_add_new(ht, h, pData ZEND_FILE_LINE_CC)

//更新第h个元素
#define zend_hash_index_update(ht, h, pData) \
        _zend_hash_index_update(ht, h, pData ZEND_FILE_LINE_CC)

//使用自动索引值
#define zend_hash_next_index_insert(ht, pData) \
        _zend_hash_next_index_insert(ht, pData ZEND_FILE_LINE_CC)

#define zend_hash_next_index_insert_new(ht, pData) \
        _zend_hash_next_index_insert_new(ht, pData ZEND_FILE_LINE_CC)
//更新hashtable

//根据zend_string key查找数组元素
ZEND_API zval* ZEND_FASTCALL zend_hash_find(const HashTable *ht, zend_string *key);

//根据普通字符串key查找元素
ZEND_API zval* ZEND_FASTCALL zend_hash_str_find(const HashTable *ht, const char *key, size_t len);

//获取数值索引元素
ZEND_API zval* ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h);

//判断元素是否存在
ZEND_API zend_bool ZEND_FASTCALL zend_hash_exists(const HashTable *ht, zend_string *key);
ZEND_API zend_bool ZEND_FASTCALL zend_hash_str_exists(const HashTable *ht, const char *str, size_t len);
ZEND_API zend_bool ZEND_FASTCALL zend_hash_index_exists(const HashTable *ht, zend_ulong h);

//获取数组元素数
#define zend_hash_num_elements(ht) \
    (ht)->nNumOfElements
//与zend_hash_num_elements()类似,会有一些特殊处理
ZEND_API uint32_t zend_array_count(HashTable *ht);
//操作实践
//创建一个hashtable,
HashTable *myht;
//php7这里也许不需要了
//ALLOC_HASHTABLE(myht);
zend_hash_init(myht, 10000, NULL, NULL, 0);
//创建并销毁一个hashtable
HashTable *myht;
zval *zv;
zend_hash_init(myht, 0, NULL, ZVAL_PTR_DTOR, 0);
ZVAL_STRING(zv, "hello");
zend_hash_index_update(myht, 42, zv);
zend_hash_destroy(myht);
//数字索引hashtable操作
//获取hash的元素的个数
HashTable *myht;
zval *zv;
zend_hash_init(myht, 0, NULL, ZVAL_PTR_DTOR, 0);
ZVAL_STRING(zv, "hello");
zend_hash_index_update(myht, 42, zv);
uint32_t hash_len = zend_hash_num_elements(myht);
php_printf("hash_len=%ld\n", hash_len);

//判断一个hash key是否存在
if (zend_hash_index_exists(myht, 43)) {
     php_printf("Index 42 exists\n");
 } else {
     php_printf("Index 42 doesn't exist\n");
 }
 //php7判断key是否存在
 zval *isExist;

 if ((isExist = zend_hash_index_find(myht, 42)) == NULL) {
     php_printf("dont`t exists");
 } else {
     php_printf("exists\n");
 }
 //php如果key存在,则删除
  if((isExist = zend_hash_index_find(myht, 42)) == NULL) {
      php_printf("dont`t exists");
  } else {
      zend_hash_index_del(myht, 42);
      php_printf("exists\n");
  }
    //字符串索引  hashtable操作
    HashTable *myht;
    zval *zv;
    const char* mykey = "name";

    zend_hash_init(myht, 0, NULL, ZVAL_PTR_DTOR, 0);

    ZVAL_STRING(zv, "baixiaoshi");
    zend_hash_update(myht, mykey, zv);
    uint32_t hash_len = zend_hash_num_elements(myht);
    php_printf("hash_len=%ld\n", hash_len);
    if (zend_hash_exists(myht, "name")) {
        php_printf("key name exists\n");
    } else {
        php_printf("key name doesn't exist\n");
    }

    zval *isExist;

    if ((isExist = zend_hash_find(myht, mykey)) == NULL) {
        php_printf("dont`t exists");
    } else {
        zend_hash_del(myht, mykey);
        php_printf("exists\n");
    }

    uint32_t hash_len2 = zend_hash_num_elements(myht);
    php_printf("hash_len2=%ld\n", hash_len2);

    zend_hash_destroy(myht);
//删除key
ZEND_API int ZEND_FASTCALL zend_hash_del(HashTable *ht, zend_string *key);

//与zend_hash_del()类似,不同地方是如果元素类型为indirect则同时销毁indirect的值
ZEND_API int ZEND_FASTCALL zend_hash_del_ind(HashTable *ht, zend_string *key);
ZEND_API int ZEND_FASTCALL zend_hash_str_del(HashTable *ht, const char *key, size_t len);
ZEND_API int ZEND_FASTCALL zend_hash_str_del_ind(HashTable *ht, const char *key, size_t len);
ZEND_API int ZEND_FASTCALL zend_hash_index_del(HashTable *ht, zend_ulong h);
ZEND_API void ZEND_FASTCALL zend_hash_del_bucket(HashTable *ht, Bucket *p);
//获取元素
zval *myval = zend_hash_get_current_data(myht);    
php_printf("myval=%s\n", Z_STRVAL_P(myval));
//php内核参数校验
//基本格式
//pass_rest_by_reference为1时,强制所有参数为引用类型 
ZEND_BEGIN_ARG_INFO(name, 0|1)  
    <arg1>  
    <arg2>  
ZEND_END_ARG_INFO()  

//类型说明
ZEND_ARG_INFO   声明普通参数
ZEND_ARG_OBJ_INFO   声明对象类型的参数
ZEND_ARG_ARRAY_INFO 声明数组类型的参数
ZEND_ARG_PASS_INFO(pass_by_ref) pass_by_ref为1时,强制设置后续的参数为引用类型

//示例,假设我们要定义一个函数user_login(username, password)

ZEND_BEGIN_ARG_INFO(arg_user_login, 0)  
ZEND_ARG_INFO(0, username)  
ZEND_ARG_INFO(0, password)  
ZEND_END_ARG_INFO() 

//这段代码展开后大致如下
static const zend_arg_info arg_user_login[] = {                                                                         
  { NULL, 0, NULL, 0, 0, 0, 0, 0, 0 },  
  { "username", sizeof(“username“)-1, NULL, 0, 0, 0, 0, 0, 0},  
  { "password", sizeof(“password“)-1, NULL, 0, 0, 0, 0, 0, 0 },  
}

typedef struct _zend_arg_info {  
    const char *name;  
    zend_uint name_len;  
    const char *class_name;  
    zend_uint class_name_len;  
    zend_bool array_type_hint;  
    zend_bool allow_null;   
    zend_bool pass_by_reference;   
    zend_bool return_reference;    
    int required_num_args;   
} zend_arg_info;  


name    参数名称
name_len    参数名称字符串长度
class_name  当参数类型为类时,指定类名称
class_name_len  类名称字符串长度
array_type_hint 标识参数类型是否为数组
allow_null  是否允许设置为空
pass_by_reference   是否设置为引用,即使用&操作符
return_reference    标识函数将重写return_value_ptr,后面介绍函数返回值时再做介绍
required_num_args   设置函数被调用时,传递参数至少为前N个,当设置为-1时,必须传递所有参数
    //zend数组的操作
    zend_string *teststr = zend_string_init("testzval", strlen("testzval"), 0);
    zval *zv;
    ZVAL_NEW_STR(zv, teststr);

    array_init(return_value);
    add_assoc_long(return_value, "age",  26);
    add_assoc_bool(return_value, "boolkey", true);
    add_assoc_string(return_value, "testkey", "hello");

    add_assoc_zval(return_value, "testzval", zv);
//php的宏
EG():这个宏可以用来访问符号表,函数,资源信息和常量
CG():用来访问核心全局变量
PG():PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。
举几个使用这个宏的例子:PG(register_globals), PG(safe_mode), PG(memory_limit)
FG():文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构
//获取全局变量的宏

EG()    这个宏可以用来访问符号表,函数,资源信息和常量。
CG()    用来访问核心全局变量。
PG()    PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。举几个使用这个宏的例子:PG(register_globals), PG(safe_mode), PG(memory_limit)
FG()    文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构
php在扩展中定义全局变量,分以下步骤
1.打开lion.c中的注释
ZEND_DECLARE_MODULE_GLOBALS(lion)

2.在php_lion.h中定义全局变量

ZEND_BEGIN_MODULE_GLOBALS(lion)
    unsigned int num;
    zval *name;
ZEND_END_MODULE_GLOBALS(lion)

3.对变量的赋值和获取如下
   zend_string *teststr = zend_string_init("hello", strlen("hello"), 0);
    zval *myname;
    ZVAL_STR(myname, teststr);
    //这里的zval类型是这样子赋值的,
    //切记不要用ZVAL_STR_P(LION_G(name))
    //因为这样赋值会报错
    LION_G(name) = myname;
    LION_G(num) = 100;
    php_printf("num=%d\n", LION_G(num));
    //这里获取的时候倒是可以这样获取
    php_printf("name=%s\n", Z_STRVAL_P(LION_G(name)));

    zend_string_release(teststr);
关于
const zend_function_entry lion_functions[] = {
    PHP_FE(confirm_lion_compiled,   NULL)
    PHP_FE_END
};
的详解,下面参考yaf框架中的代码

1.
   zend_class_entry ce;
    (void)yaf_route_route_arginfo; 
    YAF_INIT_CLASS_ENTRY(ce, "Yaf_Router", "Yaf\\Router", yaf_router_methods);
2.
zend_function_entry yaf_router_methods[] = {
    PHP_ME(yaf_router, __construct,     NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
    PHP_ME(yaf_router, addRoute,        NULL, ZEND_ACC_PUBLIC)
    PHP_ME(yaf_router, addConfig,       NULL, ZEND_ACC_PUBLIC)
    PHP_ME(yaf_router, route,       NULL, ZEND_ACC_PUBLIC)
    PHP_ME(yaf_router, getRoute,        NULL, ZEND_ACC_PUBLIC)
    PHP_ME(yaf_router, getRoutes,       NULL, ZEND_ACC_PUBLIC)
    PHP_ME(yaf_router, getCurrentRoute,     NULL, ZEND_ACC_PUBLIC)
    {NULL, NULL, NULL}
};

如何使用php.ini

1.设置全局变量
ZEND_BEGIN_MODULE_GLOBALS(lion)
    unsigned int num;
    zval *name;
    long global_value;
    zend_string *global_string;
ZEND_END_MODULE_GLOBALS(lion)

2.在这对宏中申明
PHP_INI_BEGIN()
    STD_PHP_INI_ENTRY("lion.global_value",      "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_lion_globals, lion_globals)
    STD_PHP_INI_ENTRY("lion.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_lion_globals, lion_globals)
PHP_INI_END()
3. 启用此方法
static void php_lion_init_globals(zend_lion_globals *lion_globals)
{
    lion_globals->global_value = 0;
    lion_globals->global_string = NULL;
}
4.PHP_MINIT_FUNCTION中启用这行代码
REGISTER_INI_ENTRIES();
5.PHP_MSHUTDOWN_FUNCTION中启用这行代码
UNREGISTER_INI_ENTRIES();
6.PHP_MINFO_FUNCTION(lion)中打开展示的代码
DISPLAY_INI_ENTRIES();

到这里就设置完毕了,我们开始获取

//获取配置的默认值
    const char *default_string = INI_ORIG_STR("lion.global_string");
    php_printf("default_string=%s\n", default_string);
    //获取用户定义配置
    const char *global_string = INI_STR("lion.global_string");
    php_printf("user_string=%s\n", global_string);

    long default_lval = INI_ORIG_INT("lion.global_value");
    php_printf("default_lval=%ld\n", default_lval);

    long lval = INI_INT("lion.global_value");
    php_printf("user_lval=%ld\n", lval);

每种类型有其自己的获取方式:
long lval = INI_INT("sample4.intval"); 
double dval = INI_FLT("sample4.fltval"); 
zend_bool bval = INI_BOOL("sample4.boolval");

//这个更新方法不知用在何处????
PHP_INI_MH(OnUpdateSeparator) {
    YAF_G(name_separator) = ZSTR_VAL(new_value);
    YAF_G(name_separator_len) = ZSTR_LEN(new_value);
    return SUCCESS;
}
php扩展中注册常量
1. 在相应的过程中利用以下结果进行注册

    REGISTER_STRING_CONSTANT("LION_NAME", LION_NAME, CONST_PERSISTENT | CONST_CS);
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)

要不说php是世界上最好的语言呢,你看底层都封装的这么细,不过对于不喜欢记忆的人来说也是一种拖累,暂且不管了。

php中定义多个类实践
lion.c

PHP_MINIT_FUNCTION(lion)
{
    REGISTER_INI_ENTRIES();
    REGISTER_STRING_CONSTANT("LION_NAME", LION_NAME, CONST_PERSISTENT | CONST_CS);
    //调用这个方法,把另外一个类当做一个扩展模块加载
    LION_STARTUP(demo);
    return SUCCESS;
}

php_lion.h

//定义这两个函数哈哈
#define LION_STARTUP_FUNCTION(module) ZEND_MINIT_FUNCTION(lion_##module)
#define LION_STARTUP(module) ZEND_MODULE_STARTUP_N(lion_##module)(INIT_FUNC_ARGS_PASSTHRU)

lion_demo.h

LION_STARTUP_FUNCTION(demo);

lion_demo.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h" /* for zend_alter_ini_entry */
#include "Zend/zend_interfaces.h" /* for zend_call_method_with_* */

#include "php_lion.h"
#include "lion_demo.h"

zend_class_entry *lion_demo_ce;

//这里其实是拼凑一个完整的二维数组来传递多个数组,没错,这里其实就是想初始化数组,真的太他妈的坑爹了啊,
//连这样都可以完成

ZEND_BEGIN_ARG_INFO_EX(lion_demo_void_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(lion_demo_construct_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(lion_demo_hello_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(lion_demo_mydump_arginfo, 0, 0, 1)
    ZEND_ARG_INFO(0, arr1)
ZEND_END_ARG_INFO()

PHP_METHOD(lion_demo, __construct) {
    php_printf("this is __construct");
}

PHP_METHOD(lion_demo, hello) {
    php_printf("this is hello function\n");
}

PHP_METHOD(lion_demo, mydump) {
    zval *arr1;

    if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "a", &arr1) == FAILURE) {
        php_error_docref(NULL, E_ERROR, " params arr1 and arr2 must be array\n");
        return;
    }
    //释放引用
    ZVAL_UNDEF(arr1);
    RETURN_ARR(Z_ARR_P(arr1));
}


zend_function_entry lion_demo_methods[] = {
        PHP_ME(lion_demo, __construct, lion_demo_construct_arginfo, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
        PHP_ME(lion_demo, hello, lion_demo_hello_arginfo, ZEND_ACC_PUBLIC)
        PHP_ME(lion_demo, mydump, lion_demo_mydump_arginfo, ZEND_ACC_PUBLIC)
        {NULL, NULL, NULL}
};

LION_STARTUP_FUNCTION(demo) {
    zend_class_entry ce;
    INIT_CLASS_ENTRY(ce, "lion_demo", lion_demo_methods);
    lion_demo_ce = zend_register_internal_class(&ce TSRMLS_CC);
    return SUCCESS;
}

config.m4

将新增的模块文件添加到这里,注意直接空格分割就好了,而不是逗号分割,自己开始没看清楚,搞死了
  PHP_NEW_EXTENSION(lion, lion.c lion_demo.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
//类型对应的数字
/* regular data types */
#define IS_UNDEF                    0
#define IS_NULL                     1
#define IS_FALSE                    2
#define IS_TRUE                     3
#define IS_LONG                     4
#define IS_DOUBLE                   5
#define IS_STRING                   6
#define IS_ARRAY                    7
#define IS_OBJECT                   8
#define IS_RESOURCE                 9
#define IS_REFERENCE                10

/* constant expressions */
#define IS_CONSTANT                 11
#define IS_CONSTANT_AST             12

/* fake types */
#define _IS_BOOL                    13
#define IS_CALLABLE                 14
#define IS_ITERABLE                 19
#define IS_VOID                     18

/* internal types */
#define IS_INDIRECT                 15
#define IS_PTR                      17
#define _IS_ERROR                   20
//对foreach的测试
PHP_METHOD(lion_demo, testforeach) {
    zval *arr;
    zend_ulong num_key;
    zend_string *str_key;
    zval *zv;
    if (zend_parse_parameters_throw(ZEND_NUM_ARGS(), "a", &arr) == FAILURE) {
        php_error_docref(NULL, E_ERROR, " params faile\n");
        return;
    }


    ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL_P(arr), num_key, str_key, zv) {
        if (Z_TYPE_P(zv) == IS_STRING) {
            php_printf("num_key=%lu, str_key=%s,zv=%s\n", num_key, ZSTR_VAL(str_key), Z_STRVAL_P(zv));
        } else {
            php_printf("num_key=%lu, str_key=%s,zv=%d\n", num_key, ZSTR_VAL(str_key), Z_LVAL_P(zv));            
        }
    } ZEND_HASH_FOREACH_END();

}

php扩展中有关于线程安全的宏

1.在定义方法时(形参)加上TSRMLS_D 或者TSRMLS_DC(有参数)
2.在调用方法时(实参)用TSRMLS_C或者TSRMLS_CC(有参数)
3 编译报错 error: ‘tsrm_ls’ undeclared (first use in this function) 需要在函数顶部加上TSRMLS_FETCH;
SG -> SAPI Global 
//SAPI信息(main/SAPI.h)  

EG -> Executor Global 
//执行时信息(zend/zend_globals_macros.h:43) 

PG -> PHP Core Global 
//主要存储php.ini中的信息 PHP全局变量。php.ini映射一个或者多个PHP全局结构。(main/php_globals.h) 

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

CG -> Complier Global 
//编译时信息,包括函数表等(zend_globals_macros.h:32) 用来访问核心全局变量。(zend/zend_globals_macros.h) 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值