Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
1141 lines (902 sloc) 31.1 KB
/* Copyright (C) 2007 Piotr Pokora <piotrek.pokora@gmail.com>
* This program 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 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "php_midgard.h"
#include "php_midgard_gobject.h"
#include "php_midgard_gobject_closures.h"
#include <TSRM.h>
#include <zend_interfaces.h>
#include <locale.h>
#include "php_midgard_timestamp.h"
#include "php_midgard__helpers.h"
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 2
#define Z_OBJ_P(zval_p) \
((zend_object*)(EG(objects_store).object_buckets[Z_OBJ_HANDLE_P(zval_p)].bucket.obj.object))
#endif
/* GVALUE ROUTINES */
static zend_bool php_midgard_gvalue_from_zval(const zval *zvalue, GValue *gvalue TSRMLS_DC)
{
g_assert(zvalue != NULL);
if (MGDG(midgard_memory_debug)) {
printf("php_midgard_gvalue_from_zval(z=%p [refcount=%d], g=%p)\n", zvalue, Z_REFCOUNT_P((zval *)zvalue), gvalue);
}
HashTable *zhash;
GValueArray *array;
zval **value;
HashPosition iterator;
GValue *tmpval;
zval*date_zval;
gchar *lstring;
switch (Z_TYPE_P(zvalue)) {
case IS_ARRAY:
zhash = Z_ARRVAL_P(zvalue);
array = g_value_array_new(zend_hash_num_elements(zhash));
zend_hash_internal_pointer_reset_ex(zhash, &iterator);
while (zend_hash_get_current_data_ex(
zhash, (void **)&value, &iterator) == SUCCESS)
{
tmpval = php_midgard_zval2gvalue(*value TSRMLS_CC);
g_value_array_append(array, tmpval);
g_value_unset(tmpval);
g_free(tmpval);
zend_hash_move_forward_ex(zhash, &iterator);
}
g_value_init(gvalue, G_TYPE_VALUE_ARRAY);
g_value_take_boxed(gvalue, array);
break;
case IS_BOOL:
g_value_init(gvalue, G_TYPE_BOOLEAN);
g_value_set_boolean(gvalue, Z_BVAL_P(zvalue));
break;
case IS_LONG:
g_value_init(gvalue, G_TYPE_INT);
g_value_set_int(gvalue, Z_LVAL_P(zvalue));
break;
case IS_DOUBLE:
g_value_init(gvalue, G_TYPE_FLOAT);
lstring = setlocale(LC_NUMERIC, "0");
setlocale(LC_NUMERIC, "C");
g_value_set_float(gvalue, (gfloat)Z_DVAL_P(zvalue));
setlocale(LC_ALL, lstring);
break;
case IS_STRING:
g_value_init(gvalue, G_TYPE_STRING);
g_value_set_string(gvalue, Z_STRVAL_P(zvalue));
break;
case IS_OBJECT:
/* DateTime object, convert to string value */
if (Z_OBJCE_P(zvalue) == zend_datetime_class_ptr
|| Z_OBJCE_P(zvalue)->parent == zend_datetime_class_ptr)
{
date_zval = php_midgard_datetime_get_timestamp(zvalue TSRMLS_CC);
GValue str_val = {0, };
g_value_init(&str_val, G_TYPE_STRING);
g_value_set_string(&str_val, Z_STRVAL_P(date_zval));
g_value_init(gvalue, MGD_TYPE_TIMESTAMP);
g_value_transform(&str_val, gvalue);
g_value_unset(&str_val);
zval_dtor(date_zval);
} else {
php_midgard_gobject *php_gobject = __php_objstore_object(zvalue);
if (php_gobject && php_gobject->magic == PHP_MIDGARD_GOBJ_MAGIC && php_gobject->gobject) {
g_value_init(gvalue, G_TYPE_OBJECT);
g_value_set_object(gvalue, php_gobject->gobject);
} else {
// NOT GOBJECT. Don't know what to do with it
return FALSE;
}
}
break;
case IS_RESOURCE:
/* There's no way to handle resource gracefully */
php_error(E_WARNING, "Got resource variable. Can not convert to glib-type");
return FALSE;
break;
case IS_NULL:
/* FIXME, we can not fallback to string type */
g_value_init(gvalue, G_TYPE_STRING);
g_value_set_string(gvalue, "");
break;
default:
php_error(E_WARNING, "Got variable of unknown type. Can not convert to glib-type");
return FALSE;
}
return TRUE;
}
GValue *php_midgard_zval2gvalue(const zval *zvalue TSRMLS_DC)
{
g_assert(zvalue != NULL);
GValue *gvalue = g_new0(GValue, 1);
if (!php_midgard_gvalue_from_zval(zvalue, gvalue TSRMLS_CC)) {
g_free(gvalue);
return NULL;
}
return gvalue;
}
zend_bool php_midgard_gvalue2zval(const GValue *gvalue, zval *zvalue TSRMLS_DC)
{
g_assert(gvalue);
g_assert(zvalue);
gchar *tmpstr;
double f, dpval, tmp_val;
GValueArray *array;
GValue *arr_val;
zval *zarr_val;
/* Generic GValue */
GType g_gtype = G_TYPE_FUNDAMENTAL(G_VALUE_TYPE(gvalue));
switch (g_gtype) {
case G_TYPE_STRING:
tmpstr = (gchar *)g_value_get_string(gvalue);
if (!tmpstr)
tmpstr = "";
ZVAL_STRING(zvalue, tmpstr, 1);
return TRUE;
break;
case G_TYPE_INT:
ZVAL_LONG(zvalue, g_value_get_int(gvalue));
return TRUE;
break;
case G_TYPE_UINT:
ZVAL_LONG(zvalue, g_value_get_uint(gvalue));
return TRUE;
break;
case G_TYPE_BOOLEAN:
ZVAL_BOOL(zvalue, g_value_get_boolean(gvalue));
return TRUE;
break;
case G_TYPE_DOUBLE:
/* I follow code from PHP_ROUND_WITH_FUZZ macro.
* If You find better way to add double property value,
* fell free to change this code. We do not have to worry
* about locale settings at this point ( I hope so ) */
dpval = g_value_get_double(gvalue);
tmp_val = dpval;
f = pow(10.0, (double) 6);
tmp_val *= f;
if (tmp_val >= 0.0) {
tmp_val = floor(tmp_val + 0.50000000001);
} else {
tmp_val = ceil(tmp_val - 0.50000000001);
}
tmp_val /= f;
dpval = !zend_isnan(tmp_val) ? tmp_val : dpval;
ZVAL_DOUBLE(zvalue, dpval);
return TRUE;
break;
case G_TYPE_FLOAT:
/* I follow code from PHP_ROUND_WITH_FUZZ macro.
* If You find better way to add double property value,
* fell free to change this code. We do not have to worry
* about locale settings at this point ( I hope so ) */
dpval = (gdouble)g_value_get_float(gvalue);
tmp_val = dpval;
f = pow(10.0, (double) 6);
tmp_val *= f;
if (tmp_val >= 0.0) {
tmp_val = floor(tmp_val + 0.50000000001);
} else {
tmp_val = ceil(tmp_val - 0.50000000001);
}
tmp_val /= f;
dpval = !zend_isnan(tmp_val) ? tmp_val : dpval;
ZVAL_DOUBLE(zvalue, dpval);
return TRUE;
break;
case G_TYPE_OBJECT:
case G_TYPE_INTERFACE:
{
GObject *gobject_property = g_value_get_object(gvalue);
if (gobject_property) {
const gchar *gclass_name = G_OBJECT_TYPE_NAME(gobject_property);
if (!gclass_name)
return FALSE;
g_object_ref(gobject_property);
php_midgard_gobject_init(zvalue, gclass_name, gobject_property, TRUE TSRMLS_CC);
if (MGDG(midgard_memory_debug)) {
printf("php_midgard_gvalue2zval: [%p] refcount=%d, gobj=%p, glib refcount=%d\n", zvalue, Z_REFCOUNT_P(zvalue), gobject_property, gobject_property->ref_count);
}
return TRUE;
} else {
ZVAL_NULL(zvalue);
/* TODO, implement this, currently we do not
* handle such case */
/*zvalue->value.obj =
php_midgard_gobject_new(*ce TSRMLS_CC);
//zend_objects_new(&zobject, *ce TSRMLS_CC);
Z_OBJ_HT_P(zvalue) = &php_midgard_gobject_handlers;
zend_object_std_init(zobject, *ce TSRMLS_CC); */
}
}
break;
case G_TYPE_BOXED:
if (G_VALUE_TYPE(gvalue) == G_TYPE_VALUE_ARRAY) {
array_init(zvalue);
array = (GValueArray *) g_value_get_boxed(gvalue);
if (array == NULL)
return TRUE;
guint i;
for (i = 0; i < array->n_values; i++) {
arr_val = g_value_array_get_nth(array, i);
MAKE_STD_ZVAL(zarr_val);
php_midgard_gvalue2zval(arr_val, zarr_val TSRMLS_CC);
add_index_zval(zvalue, i, zarr_val);
}
}
if (G_VALUE_TYPE(gvalue) == MGD_TYPE_TIMESTAMP) {
php_midgard_datetime_from_gvalue(gvalue, zvalue TSRMLS_CC);
return TRUE;
}
break;
default:
php_error(E_WARNING, "Don't know how to handle '%s' type. returning NULL instead", g_type_name(g_gtype));
ZVAL_NULL(zvalue);
break;
}
return FALSE;
}
/* OBJECTS ROUTINES */
/* check if there is glib-property of object linked to zval */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
int php_midgard_gobject_has_property(zval *zobject, zval *prop, int check_type, const zend_literal *key TSRMLS_DC)
#else
int php_midgard_gobject_has_property(zval *zobject, zval *prop, int check_type TSRMLS_DC)
#endif
{
php_midgard_gobject *php_gobject = __php_objstore_object(zobject);
char *prop_name = Z_STRVAL_P(prop);
if (prop_name == NULL) {
php_error(E_WARNING, "Can not check property with NULL name");
return 0;
}
if (g_str_equal(prop_name, "")) {
php_error(E_WARNING, "Can not check property with empty name");
return 0;
}
if (MGDG(midgard_memory_debug)) {
printf("[%p] php_midgard_gobject_has_property(%s, check_type=%d). object's refcount=%d\n", zobject, prop_name, check_type, Z_REFCOUNT_P(zobject));
printf("[%p] ----> gobject: %p, ref_count = %d\n", zobject, php_gobject, php_gobject->gobject->ref_count);
}
GObjectClass *klass = G_OBJECT_GET_CLASS(php_gobject->gobject);
GParamSpec *pspec = g_object_class_find_property(klass, prop_name);
int result = -1;
if (check_type == 2) {
// key exists, value is not important
if (pspec != NULL) {
return TRUE;
}
} else {
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
zval *value = php_midgard_gobject_read_property(zobject, prop, BP_VAR_IS, key TSRMLS_CC);
#else
zval *value = php_midgard_gobject_read_property(zobject, prop, BP_VAR_IS TSRMLS_CC);
#endif
Z_ADDREF_P(value);
if (MGDG(midgard_memory_debug)) {
printf("[%p] ----> property: %p, ref_count = %d\n", zobject, value, Z_REFCOUNT_P(value));
}
if (check_type == 0) {
// value is not null
result = (Z_TYPE_P(value) != IS_NULL);
} else {
// value can be casted to TRUE
result = zend_is_true(value);
}
zval_ptr_dtor(&value);
}
if (-1 == result) {
zend_object *zobj = Z_OBJ_P(zobject);
zend_object_handlers *std_hnd = zend_get_std_object_handlers();
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
if (zobj->properties != NULL
&& zobj->properties_table != NULL) {
result = std_hnd->has_property(zobject, prop, check_type, key TSRMLS_CC);
} else {
return 0;
}
#else
result = std_hnd->has_property(zobject, prop, check_type TSRMLS_CC);
#endif
}
return result;
}
/* Read Zend object property using underlying GObject's one */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
zval *php_midgard_gobject_read_property(zval *zobject, zval *prop, int type, const zend_literal *key TSRMLS_DC)
#else
zval *php_midgard_gobject_read_property(zval *zobject, zval *prop, int type TSRMLS_DC)
#endif
{
zval *_retval = NULL;
gboolean is_native_property = FALSE;
GParamSpec *pspec = NULL;
GObjectClass *klass = NULL;
int silent = (type == BP_VAR_IS);
const gchar *propname = Z_STRVAL_P(prop);
int proplen = Z_STRLEN_P(prop) + 1;
if (propname == NULL || *propname == '\0')
php_error(E_ERROR, "Can not read empty property name");
if (MGDG(midgard_memory_debug)) {
printf("[%p] php_midgard_gobject_read_property(%s)\n", zobject, propname);
}
GObject *gobject = __php_gobject_ptr(zobject);
if (gobject && Z_STRVAL_P(prop) != NULL) {
klass = G_OBJECT_GET_CLASS(gobject);
/* Find GObject property */
if (G_IS_OBJECT_CLASS(klass)) {
pspec = g_object_class_find_property(klass, propname);
if (pspec != NULL) {
is_native_property = TRUE;
if (!(pspec->flags & G_PARAM_READABLE)) {
MAKE_STD_ZVAL(_retval);
ZVAL_NULL(_retval);
Z_DELREF_P(_retval); // we don't have local reference, so need to decrement refcount
return _retval;
}
}
}
}
/* If found, get property's gvalue. Create zval from it and return */
if (is_native_property) {
gboolean is_datetime_property = FALSE;
if (MIDGARD_IS_DBOBJECT_CLASS(klass)) {
is_datetime_property = php_midgard_is_property_timestamp(MIDGARD_DBOBJECT_CLASS(klass), propname);
}
GType prop_type = G_PARAM_SPEC_VALUE_TYPE(pspec);
if (is_datetime_property) {
/* Datetime property. $obj->metadata->created for example. */
if (MGDG(midgard_memory_debug)) {
php_printf("==========> DateTime\n");
}
_retval = php_midgard_datetime_object_from_property(zobject, propname TSRMLS_CC);
Z_DELREF_P(_retval); //remove extra reference
if (MGDG(midgard_memory_debug)) {
printf("[%p] property's tmp-var refcount: %d [%s]\n", zobject, Z_REFCOUNT_P(_retval), propname);
}
} else if (G_TYPE_IS_OBJECT(prop_type) || G_TYPE_IS_INTERFACE(prop_type)) {
/* Property of object type. $obj->metadata for example. */
if (MGDG(midgard_memory_debug)) {
php_printf("==========> G_TYPE_OBJECT\n");
}
zval **property;
int hf_ret = zend_hash_find(Z_OBJPROP_P(zobject), propname, proplen, (void **) &property);
if (hf_ret == SUCCESS) {
_retval = *property;
// zval_add_ref(property);
if (MGDG(midgard_memory_debug)) {
printf("==========> found\n");
printf("==========> property's tmp-var refcount: %d [%s]\n", Z_REFCOUNT_P(_retval), propname);
GObject *gobj = __php_gobject_ptr(_retval);
printf("==========> property's gobject: %p [refcount: %d]\n", gobj, gobj->ref_count);
}
} else {
if (MGDG(midgard_memory_debug)) {
php_printf("==========> NOT found\n");
}
MAKE_STD_ZVAL(_retval);
ZVAL_NULL(_retval);
Z_DELREF_P(_retval); // we don't have local reference, so need to decrement refcount
return _retval;
}
} else {
/* Property of generic type. String, int, float, etc */
if (MGDG(midgard_memory_debug)) {
php_printf("==========> scalar\n");
}
GValue pval = {0, };
g_value_init(&pval, pspec->value_type);
g_object_get_property(gobject, propname, &pval);
MAKE_STD_ZVAL(_retval);
php_midgard_gvalue2zval(&pval, _retval TSRMLS_CC);
Z_DELREF_P(_retval); // we don't have local reference, so need to decrement refcount
g_value_unset(&pval);
if (MGDG(midgard_memory_debug)) {
printf("[%p] property's tmp-var refcount: %d [%s]\n", zobject, Z_REFCOUNT_P(_retval), propname);
}
}
/* Fallback to zend. */
} else {
/* Property is not found. Fallback to zend's property handler
* Piotras: I have no idea what type should be passed instead
* of BP_VAR_NA. The point is to throw warning when property
* is not registered for (sub)class. */
zend_object_handlers *std_hnd = zend_get_std_object_handlers();
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
_retval = std_hnd->read_property(zobject, prop, silent ? BP_VAR_IS : BP_VAR_NA, key TSRMLS_CC);
#else
_retval = std_hnd->read_property(zobject, prop, silent ? BP_VAR_IS : BP_VAR_NA TSRMLS_CC);
#endif
}
return _retval;
}
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
zval **php_midgard_gobject_get_property_ptr_ptr(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
#else
zval **php_midgard_gobject_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC)
#endif
{
if (MGDG(midgard_memory_debug)) {
const gchar *propname = Z_STRVAL_P(member);
printf("[%p] php_midgard_gobject_get_property_ptr_ptr(%s)\n", object, propname);
}
// we don't want to provide direct access to underlying properties
return NULL;
}
/* convert zval to type described by glib-type */
static void _convert_value(zval *value, GType vtype)
{
switch (vtype) {
case G_TYPE_STRING:
if (Z_TYPE_P(value) != IS_STRING)
convert_to_string(value);
break;
case G_TYPE_UINT:
case G_TYPE_INT:
if (Z_TYPE_P(value) != IS_LONG)
convert_to_long(value);
break;
case G_TYPE_BOOLEAN:
if (Z_TYPE_P(value) != IS_BOOL)
convert_to_boolean(value);
break;
case G_TYPE_FLOAT:
case G_TYPE_DOUBLE:
if (Z_TYPE_P(value) != IS_DOUBLE)
convert_to_double(value);
break;
case G_TYPE_OBJECT:
if (Z_TYPE_P(value) == IS_NULL)
return;
//if (Z_TYPE_P(value) != IS_OBJECT)
// convert_to_object(value);
break;
}
return;
}
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
void php_midgard_gobject_write_property(zval *zobject, zval *prop, zval *value, const zend_literal *key TSRMLS_DC)
#else
void php_midgard_gobject_write_property(zval *zobject, zval *prop, zval *value TSRMLS_DC)
#endif
{
if (MGDG(midgard_memory_debug)) {
const gchar *propname = Z_STRVAL_P(prop);
printf("[%p] php_midgard_gobject_write_property(%s)\n", zobject, propname);
}
GObject *gobject = __php_gobject_ptr(zobject);
/* Find GObject property */
GParamSpec *pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(gobject), Z_STRVAL_P(prop));
/* If found, set property's gvalue.*/
if (pspec) {
/* Property type might be initialized with IS_STRING or unset type.
* Check property's type and convert if needed. */
_convert_value(value, pspec->value_type);
GValue *gvalue = php_midgard_zval2gvalue(value TSRMLS_CC);
if (gvalue) {
/* Transform int to uint */
if (pspec->value_type == G_TYPE_UINT && G_VALUE_HOLDS_INT (gvalue)) {
GValue uintval = {0, };
g_value_init (&uintval, G_TYPE_UINT);
g_value_transform ((const GValue *) gvalue, &uintval);
g_object_set_property (gobject, Z_STRVAL_P (prop), &uintval);
g_value_unset (&uintval);
} else {
g_object_set_property(gobject, Z_STRVAL_P(prop), gvalue);
}
if (Z_TYPE_P(value) != IS_OBJECT) {
g_value_unset(gvalue);
}
g_free(gvalue);
}
} else { /* Fallback to zend */
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
//std_hnd->write_property(zobject, prop, value, 1 TSRMLS_CC);
#else
zend_object_handlers *std_hnd = zend_get_std_object_handlers();
std_hnd->write_property(zobject, prop, value TSRMLS_CC);
#endif
}
return;
}
void php_midgard_gobject_unset_property(zval *object, zval *member TSRMLS_DC)
{
zend_class_entry *ce = Z_OBJCE_P(object);
SEPARATE_ARG_IF_REF(member);
zend_call_method_with_1_params(&object, ce, &ce->__unset, ZEND_UNSET_FUNC_NAME, NULL, member);
zval_ptr_dtor(&member);
}
/* Get object's properties */
HashTable *php_midgard_zendobject_get_properties(zval *zobject TSRMLS_DC)
{
if (zobject == NULL)
return NULL;
php_midgard_gobject *php_gobject = __php_objstore_object(zobject);
if (!G_IS_OBJECT(php_gobject->gobject))
php_error(E_ERROR, "Underlying object is not GObject");
if (MGDG(midgard_memory_debug)) {
printf("[%p] php_midgard_zendobject_get_properties(%s)\n", zobject, Z_OBJCE_P(zobject)->name);
printf("[%p] ----> gobject: %p, ref_count = %d\n", zobject, php_gobject->gobject, php_gobject->gobject->ref_count);
}
GObject *gobject = php_gobject->gobject;
guint propn, i;
GParamSpec **props = g_object_class_list_properties(G_OBJECT_GET_CLASS(gobject), &propn);
for (i = 0; i < propn; i++) {
if (props[i]->flags & G_PARAM_CONSTRUCT_ONLY) {
continue;
}
if (!(props[i]->flags & G_PARAM_READABLE)) {
// not readable
continue;
}
if (php_gobject->has_properties) {
if (G_TYPE_IS_OBJECT(props[i]->value_type) || G_TYPE_IS_INTERFACE(props[i]->value_type)) {
// do not reinit objects
continue;
}
if (props[i]->value_type == MGD_TYPE_TIMESTAMP) {
// do not reinit datetime objects (to keep var_dump() happy)
continue;
}
}
GValue pval = {0, };
g_value_init(&pval, props[i]->value_type);
g_object_get_property(gobject, (gchar*)props[i]->name, &pval);
zval *tmp;
if (props[i]->value_type == MGD_TYPE_TIMESTAMP) {
// link it to object
tmp = php_midgard_datetime_object_from_property(zobject, props[i]->name TSRMLS_CC);
} else {
MAKE_STD_ZVAL(tmp);
php_midgard_gvalue2zval(&pval, tmp TSRMLS_CC);
}
zend_hash_update(php_gobject->zo.properties,
props[i]->name, strlen(props[i]->name)+1,
(void *)&tmp, sizeof(zval *), NULL);
g_value_unset(&pval);
}
g_free(props);
if (MGDG(midgard_memory_debug)) {
printf("[%p] <= php_midgard_zendobject_get_properties()\n", zobject);
}
php_gobject->has_properties = TRUE;
return php_gobject->zo.properties;
}
static void __object_properties_dtor(zend_object *zo TSRMLS_DC)
{
HashPosition iterator;
zval **zvalue = NULL;
HashTable *props = zo->properties;
if (MGDG(midgard_memory_debug)) {
printf("[%p] __object_properties_dtor()\n", zo);
}
zend_hash_internal_pointer_reset_ex(props, &iterator);
while (zend_hash_get_current_data_ex(props, (void **)&zvalue, &iterator) == SUCCESS) {
char *str_index;
uint str_len;
ulong num_index;
zend_hash_get_current_key_ex(props, &str_index, &str_len, &num_index, 0, &iterator);
if (MGDG(midgard_memory_debug)) {
printf("[%p] property's [%p] refcount: %d (before) [%s]\n", zo, *zvalue, Z_REFCOUNT_P(*zvalue), str_index);
}
zend_hash_del(props, str_index, str_len);
// first element was deleted, need to start over
zend_hash_internal_pointer_reset_ex(props, &iterator);
}
if (MGDG(midgard_memory_debug)) {
printf("[%p] <= __object_properties_dtor()\n", zo);
}
}
/* Object destructor */
static void __php_midgard_gobject_dtor(void *object TSRMLS_DC)
{
if (!object)
return;
php_midgard_gobject *php_gobject = (php_midgard_gobject *) object;
if (MGDG(midgard_memory_debug)) {
zend_class_entry *obj_ce = php_gobject->zo.ce;
printf("[%p] __php_midgard_gobject_dtor(%s)\n", php_gobject, obj_ce->name);
}
if (&php_gobject->zo == NULL)
return;
if (php_gobject->gobject == NULL) {
/* This may be disabled, it's just for debugging purpose */
/* php_error(E_NOTICE, "__php_midgard_gobject_dtor. Underlying GObject is NULL"); */
} else if (G_IS_OBJECT(php_gobject->gobject)) {
if (MGDG(midgard_memory_debug)) {
printf("[%p] =========> G_IS_OBJECT\n", object);
}
if (G_OBJECT_TYPE_NAME(php_gobject->gobject) != NULL) {
if (MGDG(midgard_memory_debug)) {
printf("[%p] =========> ..._TYPE_NAME != NULL\n", object);
}
if (MGDG(midgard_memory_debug)) {
printf("[%p] =========> gobject's refcount = %d (before unref)\n", object, php_gobject->gobject->ref_count);
}
/*php_error(E_NOTICE, "%s DTOR (%p)", G_OBJECT_TYPE_NAME(php_gobject->gobject), (void*)php_gobject->gobject); */
/* TODO, find a way to destroy properties of object type.
* Memory usage will be a bit abused, but I really have no idea how it should be implemented */
__object_properties_dtor(&php_gobject->zo TSRMLS_CC);
g_object_unref(php_gobject->gobject);
php_gobject->gobject = NULL;
}
}
zend_object_std_dtor(&php_gobject->zo TSRMLS_CC);
php_gobject->gobject = NULL;
efree(php_gobject);
if (MGDG(midgard_memory_debug)) {
printf("[%p] <= __php_midgard_gobject_dtor()\n", php_gobject);
}
php_gobject = NULL;
object = NULL;
}
/* Object constructor */
zend_object_value php_midgard_gobject_new(zend_class_entry *class_type TSRMLS_DC)
{
php_midgard_gobject *php_gobject;
zend_object_value retval;
php_gobject = ecalloc(1, sizeof(php_midgard_gobject));
zend_object_std_init(&php_gobject->zo, class_type TSRMLS_CC);
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
ALLOC_HASHTABLE((&php_gobject->zo)->properties);
zend_hash_init((&php_gobject->zo)->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
#endif
if (MGDG(midgard_memory_debug)) {
printf("[%p] php_midgard_gobject_new(%s)\n", &php_gobject->zo, class_type->name);
}
/* php_error(E_NOTICE, "CONSTRUCTOR %s", class_type->name); */
php_gobject->gobject = NULL;
php_gobject->has_properties = FALSE;
php_gobject->magic = PHP_MIDGARD_GOBJ_MAGIC;
/* Do not free these members. Those are owned by Zend. We just re use them. */
php_gobject->user_ce = NULL;
php_gobject->user_class_name = NULL;
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 3
/* Not sure if this is required, we need to initialize 'properties' hash, which this function
* initializes properties_table array */
object_properties_init(&(php_gobject->zo), class_type);
#else
zval *tmp;
zend_hash_copy(php_gobject->zo.properties,
&class_type->default_properties,
(copy_ctor_func_t) zval_add_ref,
(void *) &tmp, sizeof(zval *));
#endif
retval.handle = zend_objects_store_put(php_gobject,
(zend_objects_store_dtor_t)zend_objects_destroy_object,
__php_midgard_gobject_dtor,
NULL TSRMLS_CC);
retval.handlers = &php_midgard_gobject_handlers;
if (MGDG(midgard_memory_debug)) {
printf("[%p] <= php_midgard_gobject_new()\n", &php_gobject->zo);
}
return retval;
}
void php_midgard_gobject_init(zval *zvalue, const char *php_classname, GObject *gobject, gboolean dtor TSRMLS_DC)
{
zend_class_entry *ce = NULL;
if (zvalue == NULL)
MAKE_STD_ZVAL(zvalue);
if (MGDG(midgard_memory_debug)) {
printf("[%p] php_midgard_gobject_init(%s, %p [gobject refcount = %d])\n", zvalue, php_classname, gobject, gobject->ref_count);
}
ce = php_midgard_get_class_ptr_by_name(php_classname TSRMLS_CC);
if (ce == NULL)
php_error(E_ERROR, "Class '%s' is not registered", php_classname);
php_midgard_gobject_new_with_gobject(zvalue, ce, gobject, dtor TSRMLS_CC);
}
void php_midgard_gobject_new_with_gobject(zval *zvalue, zend_class_entry *ce, GObject *gobject, gboolean dtor TSRMLS_DC)
{
if (MGDG(midgard_memory_debug)) {
printf("[%p] php_midgard_gobject_new_with_gobject(%s)\n", zvalue, ce->name);
}
object_init_ex(zvalue, ce);
MGD_PHP_SET_GOBJECT_G(zvalue, gobject);
if (MIDGARD_IS_OBJECT(gobject) && ce->constructor) {
zend_call_method_with_0_params(&zvalue, ce, &ce->constructor, "__construct", NULL);
}
if (MGDG(midgard_memory_debug)) {
printf("[%p] <= php_midgard_gobject_new_with_gobject(%s)\n", zvalue, ce->name);
}
}
/* Other routines */
zend_class_entry *php_midgard_get_baseclass_ptr(zend_class_entry *ce)
{
g_assert(ce);
if (ce->parent == NULL) {
return ce;
}
GType g_class_type = g_type_from_name(ce->name);
if (g_class_type != G_TYPE_INVALID) {
if (g_type_is_a (g_class_type, MIDGARD_TYPE_DBOBJECT)) {
return ce;
}
}
if (ce->parent == php_midgard_dbobject_class
|| ce->parent == php_midgard_object_class
|| ce->parent == php_midgard_view_class
) {
return ce;
}
return php_midgard_get_baseclass_ptr(ce->parent);
}
zend_class_entry *php_midgard_get_mgdschema_class_ptr(zend_class_entry *ce)
{
g_assert(ce != NULL);
zend_class_entry *tmp = ce;
GType g_class_type = g_type_from_name(ce->name);
if (g_class_type != G_TYPE_INVALID) {
if (g_type_is_a (g_class_type, MIDGARD_TYPE_DBOBJECT)) {
return tmp;
}
}
while (
tmp->parent
&& tmp->parent != php_midgard_object_class
&& tmp->parent != php_midgard_dbobject_class
) {
tmp = tmp->parent;
}
return tmp;
}
zend_class_entry *php_midgard_get_mgdschema_class_ptr_by_name(const char *name TSRMLS_DC)
{
g_assert(name != NULL);
zend_class_entry *ce = php_midgard_get_class_ptr_by_name(name TSRMLS_CC);
if (ce == NULL) {
return NULL;
}
return php_midgard_get_mgdschema_class_ptr(ce);
}
zend_class_entry *php_midgard_get_baseclass_ptr_by_name(const char *name TSRMLS_DC)
{
g_assert(name != NULL);
zend_class_entry *ce = php_midgard_get_class_ptr_by_name(name TSRMLS_CC);
if (NULL == ce) {
return NULL;
}
return php_midgard_get_baseclass_ptr(ce);
}
// Can fetch class even if EG() is not available (before request started)
zend_class_entry *php_midgard_get_class_ptr_by_name(const char *name TSRMLS_DC)
{
g_assert(name != NULL);
size_t name_length = strlen(name);
if (PG(modules_activated) == 1 && EG(class_table)) {
// request is started
return zend_fetch_class((char *)name, name_length, ZEND_FETCH_CLASS_NO_AUTOLOAD TSRMLS_CC);
}
// request is not started. we have to act on our own
gchar *lower_class_name = g_ascii_strdown(name, name_length);
zend_class_entry **ce;
if (zend_hash_find(CG(class_table), (char *)lower_class_name, name_length + 1, (void **) &ce) != SUCCESS) {
*ce = NULL;
}
g_free(lower_class_name);
return *ce;
}
gboolean php_midgard_is_derived_from_class(const char *classname, GType basetype,
gboolean check_parent, zend_class_entry **base_class TSRMLS_DC)
{
if (classname == NULL || *classname == '\0')
return FALSE;
zend_class_entry *ce = php_midgard_get_baseclass_ptr_by_name(classname TSRMLS_CC);
if (ce == NULL) {
php_error(E_WARNING, "Can not find zend class pointer for given %s class name", classname);
return FALSE;
}
*base_class = ce;
const gchar *g_classname = php_class_name_to_g_class_name(ce->name);
GType classtype = g_type_from_name(g_classname);
if (classtype == basetype)
return TRUE;
if (check_parent == TRUE)
return g_type_is_a(classtype, basetype);
return FALSE;
}
void php_midgard_array_from_objects(GObject **objects, const gchar *class_name, zval *zarray TSRMLS_DC)
{
if (!objects)
return;
zend_class_entry *ce = zend_fetch_class((char *)class_name, strlen(class_name), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
size_t i = 0;
while (objects[i] != NULL) {
zval *zobject;
MAKE_STD_ZVAL(zobject);
php_midgard_gobject_new_with_gobject(zobject, ce, objects[i], TRUE TSRMLS_CC);
zend_hash_next_index_insert(HASH_OF(zarray), &zobject, sizeof(zval *), NULL);
i++;
}
return;
}
/* SIGNALS */
void php_midgard_gobject_connect(INTERNAL_FUNCTION_PARAMETERS)
{
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
char *sname = NULL;
uint sname_length;
zval *zval_object = getThis();
zval *zval_array = NULL;
GClosure *closure = NULL;
/* Keep '!' as passed object parameter ( or params array ) can be NULL */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sf|a!",
&sname, &sname_length,
&fci, &fci_cache, &zval_array) == FAILURE)
{
return;
}
/* Get underlying GObject instance */
GObject *object = __php_gobject_ptr(zval_object);
guint signal_id;
GQuark signal_detail;
if (!g_signal_parse_name(sname, G_OBJECT_TYPE(object), &signal_id, &signal_detail, TRUE)) {
php_error(E_WARNING, "%s signal name is invalid", sname);
RETURN_NULL();
/* TODO , should we handle exception here? */
}
closure = php_midgard_closure_new_default(fci, fci_cache, zval_object, zval_array TSRMLS_CC);
if (!closure) {
php_error(E_WARNING, "Can not create new closure");
RETURN_NULL();
}
g_signal_connect_closure_by_id(object, signal_id, signal_detail, closure, FALSE);
}
GParameter *php_midgard_array_to_gparameter(zval *params, guint *n_params TSRMLS_DC)
{
if (params == NULL)
return NULL;
HashTable *zht = Z_ARRVAL_P(params);
/* count hash elements to set number of parameters */
*n_params = (guint)zend_hash_num_elements(zht);
if (*n_params == 0)
return NULL;
/* Initialize parameters vector */
GParameter *parameters = g_new0(GParameter, *n_params);
HashPosition pos;
zval **value;
char *key;
uint key_len, i = 0, k;
ulong num_index;
/* reset array and set pointer at first position */
zend_hash_internal_pointer_reset_ex(zht, &pos);
/* iterate over array and set parameters' names and values */
while (zend_hash_get_current_data_ex(zht, (void **) &value, &pos) == SUCCESS) {
if (zend_hash_get_current_key_ex(zht, &key, &key_len, &num_index, 0, &pos) == HASH_KEY_IS_STRING) {
parameters[i].name = (const gchar *)key;
GValue gval = {0, };
if (!php_midgard_gvalue_from_zval(*value, &gval TSRMLS_CC))
goto CLEAN_AND_RETURN_NULL;
parameters[i].value = gval;
} else {
php_error(E_WARNING, "Parameter key must be valid string!");
goto CLEAN_AND_RETURN_NULL;
}
i++;
zend_hash_move_forward_ex(zht, &pos);
}
return parameters;
CLEAN_AND_RETURN_NULL:
for (k = i ; k > -1; k--) {
g_value_unset(&parameters[i].value);
}
g_free(parameters);
return NULL;
}
const gchar* php_class_name_to_g_class_name(const char *php_class_name)
{
zend_class_entry *ce = php_midgard_get_class_ptr_by_name(php_class_name TSRMLS_CC);
if (ce)
return ce->name;
return php_class_name;
}
Something went wrong with that request. Please try again.