msgpack/php/msgpack_convert.c

293 lines
9.1 KiB
C

#include "php.h"
#include "php_msgpack.h"
#include "msgpack_convert.h"
#include "msgpack_errors.h"
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3)
# define Z_REFCOUNT_P(pz) ((pz)->refcount)
# define Z_SET_ISREF_P(pz) (pz)->is_ref = 1
# define Z_UNSET_ISREF_P(pz) (pz)->is_ref = 0
#endif
#define MSGPACK_CONVERT_COPY_ZVAL(_pz, _ppz) \
ALLOC_INIT_ZVAL(_pz); \
*(_pz) = **(_ppz); \
if (PZVAL_IS_REF(*(_ppz))) { \
if (Z_REFCOUNT_P(*(_ppz)) > 0) { \
zval_copy_ctor(_pz); \
} else { \
FREE_ZVAL(*(_ppz)); \
} \
INIT_PZVAL(_pz); \
Z_SET_ISREF_P(_pz); \
} else { \
zval_copy_ctor(_pz); \
INIT_PZVAL(_pz); \
}
#define MSGPACK_CONVERT_UPDATE_PROPERTY(_ht, _key, _key_len, _val, _var) \
if (zend_symtable_update( \
_ht, _key, _key_len, &_val, sizeof(_val), NULL) == SUCCESS) { \
zend_hash_add(_var, _key, _key_len, &_val, sizeof(_val), NULL); \
return SUCCESS; \
}
inline int msgpack_convert_long_to_properties(
HashTable *ht, HashTable **properties, HashPosition *prop_pos,
uint key_index, zval *val, HashTable *var)
{
if (*properties != NULL)
{
char *prop_key;
uint prop_key_len;
ulong prop_key_index;
for (;; zend_hash_move_forward_ex(*properties, prop_pos))
{
if (zend_hash_get_current_key_ex(
*properties, &prop_key, &prop_key_len,
&prop_key_index, 0, prop_pos) == HASH_KEY_IS_STRING)
{
if (var == NULL ||
!zend_hash_exists(var, prop_key, prop_key_len))
{
zend_hash_move_forward_ex(*properties, prop_pos);
return zend_symtable_update(
ht, prop_key, prop_key_len,
&val, sizeof(val), NULL);
}
}
else
{
break;
}
}
*properties = NULL;
}
return zend_hash_index_update(ht, key_index, &val, sizeof(val), NULL);
}
inline int msgpack_convert_string_to_properties(
zval *object, char *key, uint key_len, zval *val, HashTable *var)
{
zval **data = NULL;
HashTable *ht;
zend_class_entry *ce;
char *prot_name, *priv_name;
int prop_name_len;
TSRMLS_FETCH();
ht = HASH_OF(object);
ce = zend_get_class_entry(object TSRMLS_CC);
/* private */
zend_mangle_property_name(
&priv_name, &prop_name_len, ce->name, ce->name_length, key, key_len, 1);
if (zend_hash_find(
ht, priv_name, prop_name_len, (void **)&data) == SUCCESS)
{
MSGPACK_CONVERT_UPDATE_PROPERTY(ht, priv_name, prop_name_len, val, var);
}
/* protected */
zend_mangle_property_name(
&prot_name, &prop_name_len, "*", 1, key, key_len, 1);
if (zend_hash_find(
ht, prot_name, prop_name_len, (void **)&data) == SUCCESS)
{
MSGPACK_CONVERT_UPDATE_PROPERTY(ht, prot_name, prop_name_len, val, var);
}
/* public */
MSGPACK_CONVERT_UPDATE_PROPERTY(ht, key, key_len, val, var);
return FAILURE;
}
int msgpack_convert_object(zval *return_value, zval *object, zval **value)
{
zend_class_entry *ce, **pce;
HashTable *properties = NULL;
HashPosition prop_pos;
TSRMLS_FETCH();
switch (Z_TYPE_P(object))
{
case IS_STRING:
if (zend_lookup_class(
Z_STRVAL_P(object), Z_STRLEN_P(object),
&pce TSRMLS_CC) != SUCCESS)
{
MSGPACK_ERROR("[msgpack] (%s) Class '%s' not found",
__FUNCTION__, Z_STRVAL_P(object));
return FAILURE;
}
ce = *pce;
break;
case IS_OBJECT:
ce = zend_get_class_entry(object TSRMLS_CC);
break;
default:
MSGPACK_ERROR("[msgpack] (%s) Object type is unsupported",
__FUNCTION__);
return FAILURE;
}
if (Z_TYPE_PP(value) == IS_OBJECT)
{
zend_class_entry *vce;
vce = zend_get_class_entry(*value TSRMLS_CC);
if (strcmp(ce->name, vce->name) == 0)
{
*return_value = **value;
zval_copy_ctor(return_value);
zval_ptr_dtor(value);
return SUCCESS;
}
}
object_init_ex(return_value, ce);
properties = Z_OBJ_HT_P(return_value)->get_properties(
return_value TSRMLS_CC);
if (HASH_OF(object))
{
properties = HASH_OF(object);
}
zend_hash_internal_pointer_reset_ex(properties, &prop_pos);
switch (Z_TYPE_PP(value))
{
case IS_ARRAY:
{
char *key;
uint key_len;
int key_type;
ulong key_index;
zval **data;
HashPosition pos;
HashTable *ht, *ret;
HashTable *var = NULL;
int num;
ht = HASH_OF(*value);
ret = HASH_OF(return_value);
num = zend_hash_num_elements(ht);
if (num <= 0)
{
zval_ptr_dtor(value);
break;
}
ALLOC_HASHTABLE(var);
zend_hash_init(var, num, NULL, NULL, 0);
/* string */
if (ht->nNumOfElements != ht->nNextFreeElement)
{
zend_hash_internal_pointer_reset_ex(ht, &pos);
for (;; zend_hash_move_forward_ex(ht, &pos))
{
key_type = zend_hash_get_current_key_ex(
ht, &key, &key_len, &key_index, 0, &pos);
if (key_type == HASH_KEY_NON_EXISTANT)
{
break;
}
if (zend_hash_get_current_data_ex(
ht, (void *)&data, &pos) != SUCCESS)
{
continue;
}
if (key_type == HASH_KEY_IS_STRING)
{
zval *val;
MSGPACK_CONVERT_COPY_ZVAL(val, data);
if (msgpack_convert_string_to_properties(
return_value, key, key_len, val, var) != SUCCESS)
{
zval_ptr_dtor(&val);
MSGPACK_WARNING(
"[msgpack] (%s) "
"illegal offset type, skip this decoding",
__FUNCTION__);
}
}
}
}
/* index */
zend_hash_internal_pointer_reset_ex(ht, &pos);
for (;; zend_hash_move_forward_ex(ht, &pos))
{
key_type = zend_hash_get_current_key_ex(
ht, &key, &key_len, &key_index, 0, &pos);
if (key_type == HASH_KEY_NON_EXISTANT)
{
break;
}
if (zend_hash_get_current_data_ex(
ht, (void *)&data, &pos) != SUCCESS)
{
continue;
}
switch (key_type)
{
case HASH_KEY_IS_LONG:
{
zval *val;
MSGPACK_CONVERT_COPY_ZVAL(val, data);
if (msgpack_convert_long_to_properties(
ret, &properties, &prop_pos,
key_index, val, var) != SUCCESS)
{
zval_ptr_dtor(&val);
MSGPACK_WARNING(
"[msgpack] (%s) "
"illegal offset type, skip this decoding",
__FUNCTION__);
}
break;
}
case HASH_KEY_IS_STRING:
break;
default:
MSGPACK_WARNING(
"[msgpack] (%s) key is not string nor array",
__FUNCTION__);
break;
}
}
zend_hash_destroy(var);
FREE_HASHTABLE(var);
zval_ptr_dtor(value);
break;
}
default:
if (msgpack_convert_long_to_properties(
HASH_OF(return_value), &properties, &prop_pos,
0, *value, NULL) != SUCCESS)
{
MSGPACK_WARNING(
"[msgpack] (%s) illegal offset type, skip this decoding",
__FUNCTION__);
}
break;
}
return SUCCESS;
}