连接 PHP 用户空间回调作为 GLib 信号处理程序
我正在使用 SWIG 通过使用回调的 GLib 生成 PHP 扩展。为了允许使用 PHP 用户空间函数作为回调,我使用类似的东西:
包装器(注册一个唯一的回调调度程序来处理所有信号发射):
/* {{{ proto void my_signal_connect(resource $instance, string $signal, mixed $callback, mixed $additional_args) }}}*/
ZEND_NAMED_FUNCTION(_wrap_my_signal_connect) {
GstObject *instance = (GstObject *) 0 ;
gchar *signal = (gchar *) 0 ;
zval *zcallback = (zval *) 0 ;
zval *zargs = (zval *) 0 ;
zval **args[4];
gulong result;
struct the_callback_struct *cb;
GType itype;
guint signal_id;
GSignalQuery *signal_info;
char *callback_name;
/* parse arguments */
SWIG_ResetError();
if(ZEND_NUM_ARGS() != 4 || zend_get_parameters_array_ex(4, args) != SUCCESS) {
WRONG_PARAM_COUNT;
}
{
if(SWIG_ConvertPtr(*args[0], (void **) &instance, 0, 0) < 0) {
if((*args[0])->type==IS_NULL) instance = 0;
else SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in argument 1. Expected SWIGTYPE_p_p_void");
}
}
if((*args[1])->type == IS_NULL) {
signal = (gchar *) 0;
} else {
convert_to_string_ex(args[1]);
signal = (gchar *) Z_STRVAL_PP(args[1]);
}
MAKE_STD_ZVAL(zcallback);
*zcallback = **args[2];
zval_copy_ctor(zcallback);
MAKE_STD_ZVAL(zargs);
*zargs = **args[3];
zval_copy_ctor(zargs);
/* query the signal system for in-depth info about the signal */
{
itype = G_TYPE_FROM_INSTANCE((GObject *) instance);
signal_id = g_signal_lookup((const gchar *) signal, itype);
if(signal_id == 0) {
SWIG_PHP_Error(E_ERROR, "The object does not emit the given signal");
}
signal_info = (GSignalQuery *) emalloc(sizeof(*signal_info));
g_signal_query(signal_id, signal_info);
}
/* get the function name or object + method name */
cb = (struct callback_struct *)emalloc(sizeof(*cb));
if(zcallback->type == IS_NULL) {
SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in callback argument.");
}
if(zcallback->type == IS_ARRAY) {
HashTable *ht = Z_ARRVAL_P(zcallback);
int n = zend_hash_num_elements(ht);
if(n == 2) {
if(zend_hash_index_find(ht, 0, (void **)&cb->target) == SUCCESS && Z_TYPE_PP(cb->target) == IS_OBJECT) {
if(zend_hash_index_find(ht, 1, (void **)&tmp2) == SUCCESS && Z_TYPE_PP(tmp2) == IS_STRING) {
MAKE_STD_ZVAL(cb->fx);
*cb->fx = **tmp2;
zval_copy_ctor(cb->fx);
}
}
}
} else if(zcallback->type == IS_STRING) {
cb->target = NULL;
MAKE_STD_ZVAL(cb->fx);
*cb->fx = *zcallback;
zval_copy_ctor(cb->fx);
} else {
SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in callback argument.");
}
/* Validate callback */
if(zend_is_callable(cb->fx, 0, &callback_name) == FAILURE) {
efree(callback_name);
SWIG_PHP_Error(E_ERROR, "Invalid callback");
}
/* copy the args into the structure */
MAKE_STD_ZVAL(cb->args);
*cb->args = *zargs;
zval_copy_ctor(cb->args);
cb->signal_id = signal_info->signal_id;
cb->signal_name = signal_info->signal_name;
cb->signal_flags = signal_info->signal_flags;
cb->itype = signal_info->itype;
cb->return_type = signal_info->return_type;
cb->n_params = signal_info->n_params;
cb->param_types = signal_info->param_types;
/* connect the signal handler */
result = (gulong)g_signal_connect(instance, signal, G_CALLBACK(my_signal_dispatcher), (gpointer) cb);
{
ZVAL_LONG(return_value,result);
}
return;
fail:
zend_error(SWIG_ErrorCode(),"%s",SWIG_ErrorMsg());
}
回调结构:
struct callback_struct {
zval **target;
zval *fx;
zval *args;
GType itype; /* The type of object/instance which emitted the signal */
guint signal_id; /* The signal id (or 0 if the signal is unknown) */
const gchar *signal_name; /* The signal name */
GSignalFlags signal_flags; /* The signal flags (as declared when creating the signal) */
GType return_type; /* The return type for the callback */
guint n_params; /* The number of parameters of the callback */
const GType *param_types; /* The parameter types for callback arguments */
};
信号调度程序将信号处理映射到 PHP 用户空间函数:
static void my_signal_dispatcher(gpointer instance, ...) {
int i = 0, addr;
gpointer arg, ref;
zval retval;
zval *arglist[3];
struct callback_struct *cb;
/* add emitter instance to arg list */
SWIG_SetPointerZval(arglist[i++], (void *) instance, SWIGTYPE_p__GObject, 1);
va_list ap;
va_start(ap, instance);
/* fetch the variable list of arguments */
while((addr = va_arg(ap, int)) > 2) {
arg = (gpointer) addr;
if(G_IS_OBJECT(arg)) {
SWIG_SetPointerZval(arglist[i++], (void *) arg, SWIGTYPE_p__GObject, 1);
} else {
cb = (struct callback_struct *) arg;
MAKE_STD_ZVAL(arglist[i]);
*arglist[i] = *cb->args;
zval_copy_ctor(arglist[i]);
i++;
break;
}
}
va_end(ap);
if(cb->target == NULL) {
if(call_user_function(EG(function_table), NULL, cb->fx, &retval, i, arglist TSRMLS_CC) == SUCCESS) {
zval_dtor(&retval);
}
} else {
if(call_user_function(NULL, cb->target, cb->fx, &retval, i, arglist TSRMLS_CC) == SUCCESS) {
zval_dtor(&retval);
}
}
zval_ptr_dtor(cb->target);
zval_dtor(cb->fx);
zval_dtor(cb->args);
efree(cb);
}
我能够构建扩展,并将 PHP 信号处理程序(回调)连接到给定信号,例如:
<?php
//...
function cb() {
$s = array();
foreach(func_get_args() as $arg) {
$s[] = gettype($arg) == 'resource' ? 'Resource '.get_resource_type($arg) : (gettype($arg) == 'object' ? 'Object '.get_class($arg) : gettype($arg));
}
$s = implode(', ', $s);
echo " { PHP user-space: cb($s) } ";
return 1;
}
//...
myextension::my_signal_connect($instance, "child-added", array('one' => 1));
?>
因此,当 $instance 发出“child-added”信号时,我得到了 cb() 的输出PHP 函数,以及以下错误:
{ PHP user-space: cb(Resource _p__GObject, Resource _p__GObject, array) }
*** glibc detected *** php: free(): invalid pointer: 0x095080c8 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(+0x6b591)[0xb95591]
/lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0xb96de8]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xb99ecd]
/usr/lib/php5/20090626+lfs/myextension.so(+0x2a477)[0x7510477]
php[0x831c024]
php(zend_hash_del_key_or_index+0x112)[0x831af82]
php(_zend_list_delete+0x8c)[0x831c2ec]
php(_zval_dtor_func+0xb2)[0x830b872]
php(_zval_ptr_dtor+0x4d)[0x82ff00d]
php[0x82ff0c9]
php(zend_call_function+0x764)[0x8301694]
php(call_user_function_ex+0x64)[0x83023b4]
php(call_user_function+0x6b)[0x830242b]
/usr/lib/php5/20090626+lfs/gstreamer.so(+0x93c2d)[0x7579c2d]
/usr/lib/libgobject-2.0.so.0(g_cclosure_marshal_VOID__OBJECT+0x88)[0xd262d8]
======= Memory map: ========
00110000-0026e000 r-xp 00000000 08:04 440863 /usr/lib/libdb-4.8.so
0026e000-00270000 r--p 0015d000 08:04 440863 /usr/lib/libdb-4.8.so
00270000-00271000 rw-p 0015f000 08:04 440863 /usr/lib/libdb-4.8.so
...
在添加到参数列表之前连接信号时,我尝试使用 g_object_ref() 引用 GObject 实例,但没有成功
有帮助吗?
I'm using SWIG to generate a PHP extension over GLib which uses callbacks. To allow using PHP user-space functions as callbacks, i'm using something like:
The Wrapper (registers a unique callback dispatcher to handle all signal emissions):
/* {{{ proto void my_signal_connect(resource $instance, string $signal, mixed $callback, mixed $additional_args) }}}*/
ZEND_NAMED_FUNCTION(_wrap_my_signal_connect) {
GstObject *instance = (GstObject *) 0 ;
gchar *signal = (gchar *) 0 ;
zval *zcallback = (zval *) 0 ;
zval *zargs = (zval *) 0 ;
zval **args[4];
gulong result;
struct the_callback_struct *cb;
GType itype;
guint signal_id;
GSignalQuery *signal_info;
char *callback_name;
/* parse arguments */
SWIG_ResetError();
if(ZEND_NUM_ARGS() != 4 || zend_get_parameters_array_ex(4, args) != SUCCESS) {
WRONG_PARAM_COUNT;
}
{
if(SWIG_ConvertPtr(*args[0], (void **) &instance, 0, 0) < 0) {
if((*args[0])->type==IS_NULL) instance = 0;
else SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in argument 1. Expected SWIGTYPE_p_p_void");
}
}
if((*args[1])->type == IS_NULL) {
signal = (gchar *) 0;
} else {
convert_to_string_ex(args[1]);
signal = (gchar *) Z_STRVAL_PP(args[1]);
}
MAKE_STD_ZVAL(zcallback);
*zcallback = **args[2];
zval_copy_ctor(zcallback);
MAKE_STD_ZVAL(zargs);
*zargs = **args[3];
zval_copy_ctor(zargs);
/* query the signal system for in-depth info about the signal */
{
itype = G_TYPE_FROM_INSTANCE((GObject *) instance);
signal_id = g_signal_lookup((const gchar *) signal, itype);
if(signal_id == 0) {
SWIG_PHP_Error(E_ERROR, "The object does not emit the given signal");
}
signal_info = (GSignalQuery *) emalloc(sizeof(*signal_info));
g_signal_query(signal_id, signal_info);
}
/* get the function name or object + method name */
cb = (struct callback_struct *)emalloc(sizeof(*cb));
if(zcallback->type == IS_NULL) {
SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in callback argument.");
}
if(zcallback->type == IS_ARRAY) {
HashTable *ht = Z_ARRVAL_P(zcallback);
int n = zend_hash_num_elements(ht);
if(n == 2) {
if(zend_hash_index_find(ht, 0, (void **)&cb->target) == SUCCESS && Z_TYPE_PP(cb->target) == IS_OBJECT) {
if(zend_hash_index_find(ht, 1, (void **)&tmp2) == SUCCESS && Z_TYPE_PP(tmp2) == IS_STRING) {
MAKE_STD_ZVAL(cb->fx);
*cb->fx = **tmp2;
zval_copy_ctor(cb->fx);
}
}
}
} else if(zcallback->type == IS_STRING) {
cb->target = NULL;
MAKE_STD_ZVAL(cb->fx);
*cb->fx = *zcallback;
zval_copy_ctor(cb->fx);
} else {
SWIG_PHP_Error(E_ERROR, "Wrapper: Type error in callback argument.");
}
/* Validate callback */
if(zend_is_callable(cb->fx, 0, &callback_name) == FAILURE) {
efree(callback_name);
SWIG_PHP_Error(E_ERROR, "Invalid callback");
}
/* copy the args into the structure */
MAKE_STD_ZVAL(cb->args);
*cb->args = *zargs;
zval_copy_ctor(cb->args);
cb->signal_id = signal_info->signal_id;
cb->signal_name = signal_info->signal_name;
cb->signal_flags = signal_info->signal_flags;
cb->itype = signal_info->itype;
cb->return_type = signal_info->return_type;
cb->n_params = signal_info->n_params;
cb->param_types = signal_info->param_types;
/* connect the signal handler */
result = (gulong)g_signal_connect(instance, signal, G_CALLBACK(my_signal_dispatcher), (gpointer) cb);
{
ZVAL_LONG(return_value,result);
}
return;
fail:
zend_error(SWIG_ErrorCode(),"%s",SWIG_ErrorMsg());
}
The callback struct:
struct callback_struct {
zval **target;
zval *fx;
zval *args;
GType itype; /* The type of object/instance which emitted the signal */
guint signal_id; /* The signal id (or 0 if the signal is unknown) */
const gchar *signal_name; /* The signal name */
GSignalFlags signal_flags; /* The signal flags (as declared when creating the signal) */
GType return_type; /* The return type for the callback */
guint n_params; /* The number of parameters of the callback */
const GType *param_types; /* The parameter types for callback arguments */
};
The signal dispatcher maps the signal handling to a PHP user space function:
static void my_signal_dispatcher(gpointer instance, ...) {
int i = 0, addr;
gpointer arg, ref;
zval retval;
zval *arglist[3];
struct callback_struct *cb;
/* add emitter instance to arg list */
SWIG_SetPointerZval(arglist[i++], (void *) instance, SWIGTYPE_p__GObject, 1);
va_list ap;
va_start(ap, instance);
/* fetch the variable list of arguments */
while((addr = va_arg(ap, int)) > 2) {
arg = (gpointer) addr;
if(G_IS_OBJECT(arg)) {
SWIG_SetPointerZval(arglist[i++], (void *) arg, SWIGTYPE_p__GObject, 1);
} else {
cb = (struct callback_struct *) arg;
MAKE_STD_ZVAL(arglist[i]);
*arglist[i] = *cb->args;
zval_copy_ctor(arglist[i]);
i++;
break;
}
}
va_end(ap);
if(cb->target == NULL) {
if(call_user_function(EG(function_table), NULL, cb->fx, &retval, i, arglist TSRMLS_CC) == SUCCESS) {
zval_dtor(&retval);
}
} else {
if(call_user_function(NULL, cb->target, cb->fx, &retval, i, arglist TSRMLS_CC) == SUCCESS) {
zval_dtor(&retval);
}
}
zval_ptr_dtor(cb->target);
zval_dtor(cb->fx);
zval_dtor(cb->args);
efree(cb);
}
I'am able to build the extension, and to connect a PHP signal handler (callback) to a given signal, for example:
<?php
//...
function cb() {
$s = array();
foreach(func_get_args() as $arg) {
$s[] = gettype($arg) == 'resource' ? 'Resource '.get_resource_type($arg) : (gettype($arg) == 'object' ? 'Object '.get_class($arg) : gettype($arg));
}
$s = implode(', ', $s);
echo " { PHP user-space: cb($s) } ";
return 1;
}
//...
myextension::my_signal_connect($instance, "child-added", array('one' => 1));
?>
so, when $instance emits the "child-added" signal i got the output from the cb() PHP function, and the following error:
{ PHP user-space: cb(Resource _p__GObject, Resource _p__GObject, array) }
*** glibc detected *** php: free(): invalid pointer: 0x095080c8 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(+0x6b591)[0xb95591]
/lib/tls/i686/cmov/libc.so.6(+0x6cde8)[0xb96de8]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xb99ecd]
/usr/lib/php5/20090626+lfs/myextension.so(+0x2a477)[0x7510477]
php[0x831c024]
php(zend_hash_del_key_or_index+0x112)[0x831af82]
php(_zend_list_delete+0x8c)[0x831c2ec]
php(_zval_dtor_func+0xb2)[0x830b872]
php(_zval_ptr_dtor+0x4d)[0x82ff00d]
php[0x82ff0c9]
php(zend_call_function+0x764)[0x8301694]
php(call_user_function_ex+0x64)[0x83023b4]
php(call_user_function+0x6b)[0x830242b]
/usr/lib/php5/20090626+lfs/gstreamer.so(+0x93c2d)[0x7579c2d]
/usr/lib/libgobject-2.0.so.0(g_cclosure_marshal_VOID__OBJECT+0x88)[0xd262d8]
======= Memory map: ========
00110000-0026e000 r-xp 00000000 08:04 440863 /usr/lib/libdb-4.8.so
0026e000-00270000 r--p 0015d000 08:04 440863 /usr/lib/libdb-4.8.so
00270000-00271000 rw-p 0015f000 08:04 440863 /usr/lib/libdb-4.8.so
...
I've tried to ref the GObject instances using g_object_ref() when connecting the signal before adding to the arguments list, without success
Any help?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论