Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: b549cc2e46
Fetching contributors…

Cannot retrieve contributors at this time

349 lines (297 sloc) 10.227 kb
/*
* PHP-GTK - The PHP language bindings for GTK+
*
* Copyright (C) 2001-2008 Andrei Zmievski <andrei@php.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "php_gtk.h"
#if HAVE_PHP_GTK
/*
* Closures API and helper functions
*/
struct _phpg_closure_t {
GClosure closure;
zval *callback;
zval *user_args;
zval *replace_object;
int connect_type;
char *src_filename;
uint src_lineno;
#ifdef ZTS
TSRMLS_D;
#endif
};
static void phpg_closure_invalidate(gpointer data, GClosure *closure)
{
phpg_closure_t *phpg_closure = (phpg_closure_t *)closure;
zval_ptr_dtor(&phpg_closure->callback);
if (phpg_closure->user_args) {
zval_ptr_dtor(&phpg_closure->user_args);
}
if (phpg_closure->replace_object) {
zval_ptr_dtor(&phpg_closure->replace_object);
}
efree(phpg_closure->src_filename);
phpg_closure->callback = NULL;
phpg_closure->user_args = NULL;
phpg_closure->replace_object = NULL;
phpg_closure->connect_type = PHPG_CONNECT_NORMAL;
phpg_closure->src_filename = NULL;
phpg_closure->src_lineno = 0;
#ifdef ZTS
phpg_closure->TSRMLS_C = NULL;
#endif
}
static void phpg_closure_marshal(GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
phpg_closure_t *phpg_closure = (phpg_closure_t *)closure;
char *callback_name;
zval ***params = NULL;
zval *retval = NULL;
uint n_params = 0, i;
#ifdef ZTS
TSRMLS_D = phpg_closure->TSRMLS_C;
#endif
if (!zend_is_callable(phpg_closure->callback, 0, &callback_name PHPGTK_ZEND_IS_CALLABLE)) {
if (phpg_closure->src_filename)
php_error(E_WARNING,
"Unable to invoke signal callback '%s' specified in %s on line %d",
callback_name, phpg_closure->src_filename, phpg_closure->src_lineno);
else
php_error(E_WARNING, "Unable to invoke signal callback '%s'", callback_name);
efree(callback_name);
return;
}
if (phpg_closure->connect_type == PHPG_CONNECT_SIMPLE) {
/* we don't use any signal params for simple connections */
n_param_values = 0;
} else {
if (phpg_closure->connect_type == PHPG_CONNECT_OBJECT) {
/* skip first parameter */
n_param_values--;
param_values++;
}
n_params = n_param_values;
}
if (phpg_closure->user_args) {
n_params += zend_hash_num_elements(Z_ARRVAL_P(phpg_closure->user_args));
}
params = (zval ***)emalloc(n_params * sizeof(zval **));
i = 0;
if (phpg_closure->connect_type == PHPG_CONNECT_REPLACE) {
params[i++] = &phpg_closure->replace_object;
}
for ( ; i < n_param_values; i++) {
params[i] = (zval **) emalloc(sizeof(zval *));
*(params[i]) = NULL;
if (phpg_gvalue_to_zval(&param_values[i], params[i], FALSE, TRUE TSRMLS_CC) != SUCCESS) {
goto err_marshal;
}
}
if (phpg_closure->user_args) {
for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(phpg_closure->user_args));
zend_hash_get_current_data(Z_ARRVAL_P(phpg_closure->user_args), (void **)&params[i]) == SUCCESS;
zend_hash_move_forward(Z_ARRVAL_P(phpg_closure->user_args)), i++);
/* empty body */
}
assert(i == n_params);
call_user_function_ex(EG(function_table), NULL, phpg_closure->callback,
&retval, n_params, params, 0, NULL TSRMLS_CC);
if (retval) {
if (return_value) {
if (phpg_gvalue_from_zval(return_value, &retval, TRUE TSRMLS_CC) == FAILURE) {
php_error(E_WARNING, "Could not convert return value of signal callback '%s' to '%s'",
callback_name, g_type_name(G_VALUE_TYPE(return_value)));
}
}
zval_ptr_dtor(&retval);
}
err_marshal:
efree(callback_name);
i = (phpg_closure->connect_type == PHPG_CONNECT_REPLACE) ? 1 : 0;
for ( ; i < n_param_values; i++) {
zval_ptr_dtor(params[i]);
efree(params[i]);
}
efree(params);
phpg_handle_marshaller_exception(TSRMLS_C);
}
PHP_GTK_API GClosure* phpg_closure_new(zval *callback, zval *user_args, int connect_type, zval *replace_object TSRMLS_DC)
{
GClosure *closure;
phpg_closure_t *phpg_closure;
phpg_return_val_if_fail(callback != NULL, NULL);
closure = g_closure_new_simple(sizeof(phpg_closure_t), NULL);
g_closure_add_invalidate_notifier(closure, NULL, phpg_closure_invalidate);
g_closure_set_marshal(closure, phpg_closure_marshal);
phpg_closure = (phpg_closure_t *) closure;
zval_add_ref(&callback);
phpg_closure->callback = callback;
phpg_closure->src_filename = estrdup(zend_get_executed_filename(TSRMLS_C));
phpg_closure->src_lineno = zend_get_executed_lineno(TSRMLS_C);
#ifdef ZTS
phpg_closure->TSRMLS_C = TSRMLS_C;
#endif
if (user_args) {
zval_add_ref(&user_args);
if (Z_TYPE_P(user_args) != IS_ARRAY) {
convert_to_array(user_args);
}
phpg_closure->user_args = user_args;
} else {
phpg_closure->user_args = NULL;
}
if (replace_object) {
zval_add_ref(&replace_object);
phpg_closure->replace_object = replace_object;
} else {
phpg_closure->replace_object = NULL;
}
phpg_closure->connect_type = connect_type;
return closure;
}
PHP_GTK_API void phpg_cb_data_destroy(gpointer data)
{
phpg_cb_data_t *cbd = (phpg_cb_data_t *) data;
if (!cbd) return;
zval_ptr_dtor(&cbd->callback);
if (cbd->user_args) {
zval_ptr_dtor(&cbd->user_args);
}
efree(cbd->src_filename);
efree(cbd);
}
static void phpg_signal_class_closure_marshal(GClosure *closure,
GValue *return_value,
guint n_param_values,
const GValue *param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
GObject *object;
GSignalInvocationHint *hint = (GSignalInvocationHint *)invocation_hint;
zval *php_object = NULL;
gchar *method_name, *tmp, *lc_method_name;
gsize method_name_len;
zval ***params = NULL;
zval *retval = NULL;
zval *item, z_method_name;
phpg_gboxed_t *boxed_item;
int i, k;
#ifdef ZTS
TSRMLS_D = closure->data;
#endif;
phpg_return_if_fail(invocation_hint != NULL);
/* verify that first parameter is an object */
object = g_value_get_object(&param_values[0]);
phpg_return_if_fail(object != NULL && G_IS_OBJECT(object));
/* create wrapper for the object */
phpg_gobject_new(&php_object, object TSRMLS_CC);
if (Z_TYPE_P(php_object) == IS_NULL) {
zval_ptr_dtor(&php_object);
return;
}
method_name = g_strconcat("__do_", g_signal_name(hint->signal_id), NULL);
for (tmp = method_name; *tmp != '\0'; tmp++) {
if (*tmp == '-') *tmp = '_';
}
method_name_len = strlen(method_name);
lc_method_name = g_ascii_strdown(method_name, method_name_len);
if (!zend_hash_exists(&Z_OBJCE_P(php_object)->function_table, lc_method_name, method_name_len+1)) {
union _zend_function *func = NULL;
g_free(lc_method_name);
if (Z_OBJ_HT_P(php_object)->get_method != NULL
#if PHP_VERSION_ID < 50399
&& (func = Z_OBJ_HT_P(php_object)->get_method(&php_object, method_name, method_name_len TSRMLS_CC)) != NULL) {
#else
&& (func = Z_OBJ_HT_P(php_object)->get_method(&php_object, method_name, method_name_len, NULL TSRMLS_CC)) != NULL) {
#endif
if (func->type == ZEND_INTERNAL_FUNCTION
&& ((zend_internal_function*)func)->handler == zend_std_call_user_call
) {
efree(((zend_internal_function*)func)->function_name);
efree(func);
zval_ptr_dtor(&php_object);
return;
}
}
zval_ptr_dtor(&php_object);
return;
}
g_free(lc_method_name);
params = (zval ***)emalloc((n_param_values-1) * sizeof(zval **));
for (i = 0; i < (int)n_param_values-1; i++) {
params[i] = (zval **) emalloc(sizeof(zval *));
*(params[i]) = NULL;
if (phpg_gvalue_to_zval(&param_values[i+1], params[i], FALSE, TRUE TSRMLS_CC) != SUCCESS) {
goto err_class_closure_marshal;
}
}
ZVAL_STRINGL(&z_method_name, method_name, method_name_len, 0);
call_user_function_ex(EG(function_table), &php_object, &z_method_name,
&retval, n_param_values-1, params, 0, NULL TSRMLS_CC);
for (k = 0; k < i; k++) {
item = *params[k];
if (Z_TYPE_P(item) == IS_OBJECT &&
instanceof_function(Z_OBJCE_P(item), gboxed_ce TSRMLS_CC) && Z_REFCOUNT_P(item) > 1) {
boxed_item = phpg_gboxed_get(item TSRMLS_CC);
if (!boxed_item->free_on_destroy) {
boxed_item->boxed = g_boxed_copy(boxed_item->gtype, boxed_item->boxed);
boxed_item->free_on_destroy = TRUE;
}
}
}
if (retval) {
if (return_value) {
if (phpg_gvalue_from_zval(return_value, &retval, TRUE TSRMLS_CC) == FAILURE) {
php_error(E_WARNING, "Could not convert return value of custom signal action '%s' to '%s'",
method_name, g_type_name(G_VALUE_TYPE(return_value)));
}
}
zval_ptr_dtor(&retval);
}
i--; // adjust for cleanup
err_class_closure_marshal:
for ( ; i >= 0; i--) {
zval_ptr_dtor(params[i]);
efree(params[i]);
}
efree(params);
g_free(method_name);
zval_ptr_dtor(&php_object);
}
PHP_GTK_API GClosure* phpg_get_signal_class_closure(TSRMLS_D)
{
static GClosure *closure = NULL;
if (closure == NULL) {
#ifdef ZTS
closure = g_closure_new_simple(sizeof(GClosure) + sizeof(TSRMLS_C), TSRMLS_C);
#else
closure = g_closure_new_simple(sizeof(GClosure), NULL);
#endif
g_closure_set_marshal(closure, phpg_signal_class_closure_marshal);
g_closure_ref(closure);
g_closure_sink(closure);
}
return closure;
}
#endif /* HAVE_PHP_GTK */
/* vim: set fdm=marker et sts=4: */
Jump to Line
Something went wrong with that request. Please try again.