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

时间:2011-01-26 12:25:01

标签: php c swig

我正在使用SWIG在GLib上生成一个使用回调的PHP扩展。为了允许使用PHP用户空间函数作为回调,我使用的是:

Wrapper(注册一个独特的回调调度程序来处理所有信号发射):

/* {{{ 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实例,但没有成功

任何帮助?

0 个答案:

没有答案