php扩展开发参考资料
- https://github.com/baixiaoshi/phpbook/blob/master/preface.md
- http://www.web3.xin/manual/detail/id/197.html?page=2
- http://www.php-internals.com/book/
- https://yuerblog.cc/ php7学习博客
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 释放全局变量时
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)