连接 PHP 用户空间回调作为 GLib 信号处理程序

发布于 2024-10-14 12:15:58 字数 7308 浏览 2 评论 0原文

我正在使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文