msgpack/php/msgpack_pack.c

565 lines
16 KiB
C
Raw Normal View History

2010-07-17 18:46:28 +09:00
#include "php.h"
#include "php_ini.h"
#include "ext/standard/php_smart_str.h"
#include "ext/standard/php_incomplete_class.h"
#include "ext/standard/php_var.h"
#include "php_msgpack.h"
#include "msgpack_pack.h"
#include "msgpack_errors.h"
2010-07-17 18:46:28 +09:00
#include "msgpack/pack_define.h"
#define msgpack_pack_user smart_str*
#define msgpack_pack_inline_func(name) \
static inline void msgpack_pack ## name
#define msgpack_pack_inline_func_cint(name) \
static inline void msgpack_pack ## name
#define msgpack_pack_append_buffer(user, buf, len) \
smart_str_appendl(user, (const void*)buf, len)
#include "msgpack/pack_template.h"
#if ZEND_MODULE_API_NO < 20090626
2010-07-17 18:46:28 +09:00
# define Z_ISREF_P(pz) PZVAL_IS_REF(pz)
#endif
inline static int msgpack_var_add(
HashTable *var_hash, zval *var, void *var_old TSRMLS_DC)
{
ulong var_no;
char id[32], *p;
int len;
if ((Z_TYPE_P(var) == IS_OBJECT) && Z_OBJ_HT_P(var)->get_class_entry)
{
p = smart_str_print_long(
id + sizeof(id) - 1,
(((size_t)Z_OBJCE_P(var) << 5)
| ((size_t)Z_OBJCE_P(var) >> (sizeof(long) * 8 - 5)))
+ (long)Z_OBJ_HANDLE_P(var));
len = id + sizeof(id) - 1 - p;
}
2010-12-27 11:09:14 +09:00
else if (Z_TYPE_P(var) == IS_ARRAY)
2010-07-17 18:46:28 +09:00
{
p = smart_str_print_long(id + sizeof(id) - 1, (long)var);
len = id + sizeof(id) - 1 - p;
}
2010-12-27 11:09:14 +09:00
else
{
return FAILURE;
}
2010-07-17 18:46:28 +09:00
if (var_old && zend_hash_find(var_hash, p, len, var_old) == SUCCESS)
{
if (!Z_ISREF_P(var))
{
var_no = -1;
zend_hash_next_index_insert(
var_hash, &var_no, sizeof(var_no), NULL);
}
return FAILURE;
}
var_no = zend_hash_num_elements(var_hash) + 1;
zend_hash_add(var_hash, p, len, &var_no, sizeof(var_no), NULL);
return SUCCESS;
}
inline static void msgpack_serialize_string(
smart_str *buf, char *str, size_t len)
{
msgpack_pack_raw(buf, len);
msgpack_pack_raw_body(buf, str, len);
}
inline static void msgpack_serialize_class(
smart_str *buf, zval *val, zval *retval_ptr, HashTable *var_hash,
char *class_name, zend_uint name_len, zend_bool incomplete_class TSRMLS_DC)
{
int count;
HashTable *ht = HASH_OF(retval_ptr);
count = zend_hash_num_elements(ht);
if (incomplete_class)
{
--count;
}
if (count > 0)
{
char *key;
zval **data, **name;
ulong key_index;
HashPosition pos;
int n;
zval nval, *nvalp;
msgpack_pack_map(buf, count + 1);
msgpack_pack_nil(buf);
msgpack_serialize_string(buf, class_name, name_len);
ZVAL_NULL(&nval);
nvalp = &nval;
zend_hash_internal_pointer_reset_ex(ht, &pos);
for (;; zend_hash_move_forward_ex(ht, &pos))
{
n = zend_hash_get_current_key_ex(
ht, &key, NULL, &key_index, 0, &pos);
if (n == HASH_KEY_NON_EXISTANT)
{
break;
}
if (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0)
{
continue;
}
zend_hash_get_current_data_ex(ht, (void **)&name, &pos);
if (Z_TYPE_PP(name) != IS_STRING)
{
MSGPACK_NOTICE(
"[msgpack] (%s) __sleep should return an array only "
"containing the names of instance-variables to serialize",
__FUNCTION__);
2010-07-17 18:46:28 +09:00
continue;
}
if (zend_hash_find(
Z_OBJPROP_P(val), Z_STRVAL_PP(name),
Z_STRLEN_PP(name) + 1, (void *)&data) == SUCCESS)
{
msgpack_serialize_string(
buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
msgpack_serialize_zval(buf, *data, var_hash TSRMLS_CC);
}
else
{
zend_class_entry *ce;
ce = zend_get_class_entry(val TSRMLS_CC);
if (ce)
{
char *prot_name, *priv_name;
int prop_name_length;
do
{
zend_mangle_property_name(
&priv_name, &prop_name_length, ce->name,
ce->name_length, Z_STRVAL_PP(name),
Z_STRLEN_PP(name),
ce->type & ZEND_INTERNAL_CLASS);
if (zend_hash_find(
Z_OBJPROP_P(val), priv_name,
prop_name_length + 1,
(void *)&data) == SUCCESS)
{
msgpack_serialize_string(
buf, priv_name, prop_name_length);
pefree(priv_name,
ce->type & ZEND_INTERNAL_CLASS);
msgpack_serialize_zval(
buf, *data, var_hash TSRMLS_CC);
break;
}
pefree(priv_name,
ce->type & ZEND_INTERNAL_CLASS);
zend_mangle_property_name(
&prot_name, &prop_name_length, "*", 1,
Z_STRVAL_PP(name), Z_STRLEN_PP(name),
ce->type & ZEND_INTERNAL_CLASS);
if (zend_hash_find(
Z_OBJPROP_P(val), prot_name,
prop_name_length + 1,
(void *)&data) == SUCCESS)
{
msgpack_serialize_string(
buf, prot_name, prop_name_length);
pefree(prot_name,
ce->type & ZEND_INTERNAL_CLASS);
msgpack_serialize_zval(
buf, *data, var_hash TSRMLS_CC);
break;
}
pefree(prot_name, ce->type & ZEND_INTERNAL_CLASS);
MSGPACK_NOTICE(
"[msgpack] (%s) \"%s\" returned as member "
"variable from __sleep() but does not exist",
__FUNCTION__, Z_STRVAL_PP(name));
2010-07-17 18:46:28 +09:00
msgpack_serialize_string(
buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
msgpack_serialize_zval(
buf, nvalp, var_hash TSRMLS_CC);
}
while (0);
}
else
{
msgpack_serialize_string(
buf, Z_STRVAL_PP(name), Z_STRLEN_PP(name));
msgpack_serialize_zval(buf, nvalp, var_hash TSRMLS_CC);
}
}
}
}
}
inline static void msgpack_serialize_array(
smart_str *buf, zval *val, HashTable *var_hash, zend_bool object,
2010-07-17 18:46:28 +09:00
char* class_name, zend_uint name_len, zend_bool incomplete_class TSRMLS_DC)
{
HashTable *ht;
size_t n;
zend_bool hash = 1;
2010-07-17 18:46:28 +09:00
if (object)
{
ht = Z_OBJPROP_P(val);
}
else
{
ht = HASH_OF(val);
}
if (ht)
{
n = zend_hash_num_elements(ht);
}
else
{
n = 0;
}
if (n > 0 && incomplete_class)
{
--n;
}
if (object)
{
2010-09-29 08:47:06 +09:00
if (MSGPACK_G(php_only))
{
if (Z_ISREF_P(val))
{
msgpack_pack_map(buf, n + 2);
msgpack_pack_nil(buf);
msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_REFERENCE);
}
else
{
msgpack_pack_map(buf, n + 1);
}
2010-07-17 18:46:28 +09:00
2010-09-29 08:47:06 +09:00
msgpack_pack_nil(buf);
2010-07-17 18:46:28 +09:00
2010-09-29 08:47:06 +09:00
msgpack_serialize_string(buf, class_name, name_len);
}
else
{
msgpack_pack_array(buf, n);
hash = 0;
2010-09-29 08:47:06 +09:00
}
2010-07-17 18:46:28 +09:00
}
else if (n == 0)
{
hash = 0;
2010-07-17 18:46:28 +09:00
msgpack_pack_array(buf, n);
}
else if (Z_ISREF_P(val) && MSGPACK_G(php_only))
{
msgpack_pack_map(buf, n + 1);
msgpack_pack_nil(buf);
msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_REFERENCE);
}
else if (ht->nNumOfElements == ht->nNextFreeElement)
{
hash = 0;
msgpack_pack_array(buf, n);
}
2010-07-17 18:46:28 +09:00
else
{
msgpack_pack_map(buf, n);
2010-07-17 18:46:28 +09:00
}
if (n > 0)
{
char *key;
uint key_len;
int key_type;
ulong key_index;
zval **data;
HashPosition pos;
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 (incomplete_class && strcmp(key, MAGIC_MEMBER) == 0)
{
continue;
}
2010-09-29 08:47:06 +09:00
if (hash)
2010-07-17 18:46:28 +09:00
{
2010-09-29 08:47:06 +09:00
switch (key_type)
{
case HASH_KEY_IS_LONG:
msgpack_pack_long(buf, key_index);
break;
case HASH_KEY_IS_STRING:
msgpack_serialize_string(buf, key, key_len - 1);
break;
default:
msgpack_serialize_string(buf, "", sizeof(""));
MSGPACK_WARNING(
"[msgpack] (%s) key is not string nor array",
__FUNCTION__);
2010-09-29 08:47:06 +09:00
break;
}
2010-07-17 18:46:28 +09:00
}
if (zend_hash_get_current_data_ex(
ht, (void *)&data, &pos) != SUCCESS ||
!data || data == &val ||
(Z_TYPE_PP(data) == IS_ARRAY &&
Z_ARRVAL_PP(data)->nApplyCount > 1))
{
msgpack_pack_nil(buf);
}
else
{
if (Z_TYPE_PP(data) == IS_ARRAY)
{
Z_ARRVAL_PP(data)->nApplyCount++;
}
msgpack_serialize_zval(buf, *data, var_hash TSRMLS_CC);
if (Z_TYPE_PP(data) == IS_ARRAY)
{
Z_ARRVAL_PP(data)->nApplyCount--;
}
}
}
}
}
inline static void msgpack_serialize_object(
smart_str *buf, zval *val, HashTable *var_hash,
char* class_name, zend_uint name_len, zend_bool incomplete_class TSRMLS_DC)
{
zval *retval_ptr = NULL;
zval fname;
int res;
zend_class_entry *ce = NULL;
if (Z_OBJ_HT_P(val)->get_class_entry)
{
ce = Z_OBJCE_P(val);
}
2010-12-27 11:09:14 +09:00
#if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION > 0)
2010-07-17 18:46:28 +09:00
if (ce && ce->serialize != NULL)
{
unsigned char *serialized_data = NULL;
zend_uint serialized_length;
if (ce->serialize(
val, &serialized_data, &serialized_length,
(zend_serialize_data *)var_hash TSRMLS_CC) == SUCCESS &&
!EG(exception))
{
/* has custom handler */
msgpack_pack_map(buf, 2);
msgpack_pack_nil(buf);
msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_CUSTOM_OBJECT);
msgpack_serialize_string(buf, ce->name, ce->name_length);
msgpack_pack_raw(buf, serialized_length);
msgpack_pack_raw_body(buf, serialized_data, serialized_length);
}
else
{
msgpack_pack_nil(buf);
}
if (serialized_data)
{
efree(serialized_data);
}
return;
}
2010-12-27 11:09:14 +09:00
#endif
2010-07-17 18:46:28 +09:00
if (ce && ce != PHP_IC_ENTRY &&
zend_hash_exists(&ce->function_table, "__sleep", sizeof("__sleep")))
{
INIT_PZVAL(&fname);
ZVAL_STRINGL(&fname, "__sleep", sizeof("__sleep") - 1, 0);
res = call_user_function_ex(CG(function_table), &val, &fname,
&retval_ptr, 0, 0, 1, NULL TSRMLS_CC);
if (res == SUCCESS && !EG(exception))
{
if (retval_ptr)
{
if (HASH_OF(retval_ptr))
{
msgpack_serialize_class(
buf, val, retval_ptr, var_hash,
class_name, name_len, incomplete_class TSRMLS_CC);
}
else
{
MSGPACK_NOTICE(
"[msgpack] (%s) __sleep should return an array only "
"containing the names of instance-variables "
"to serialize", __FUNCTION__);
2010-07-17 18:46:28 +09:00
msgpack_pack_nil(buf);
}
zval_ptr_dtor(&retval_ptr);
}
return;
}
}
if (retval_ptr)
{
zval_ptr_dtor(&retval_ptr);
}
msgpack_serialize_array(
buf, val, var_hash, 1,
2010-07-17 18:46:28 +09:00
class_name, name_len, incomplete_class TSRMLS_CC);
}
void msgpack_serialize_zval(
smart_str *buf, zval *val, HashTable *var_hash TSRMLS_DC)
{
ulong *var_already;
2010-09-29 08:47:06 +09:00
if (MSGPACK_G(php_only) &&
var_hash &&
2010-07-17 18:46:28 +09:00
msgpack_var_add(
var_hash, val, (void *)&var_already TSRMLS_CC) == FAILURE)
{
if (Z_ISREF_P(val))
2010-07-17 18:46:28 +09:00
{
if (Z_TYPE_P(val) == IS_ARRAY)
{
msgpack_pack_map(buf, 2);
2010-07-17 18:46:28 +09:00
msgpack_pack_nil(buf);
msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_RECURSIVE);
2010-07-17 18:46:28 +09:00
msgpack_pack_long(buf, 0);
msgpack_pack_long(buf, *var_already);
2010-07-17 18:46:28 +09:00
return;
}
else if (Z_TYPE_P(val) == IS_OBJECT)
{
msgpack_pack_map(buf, 2);
msgpack_pack_nil(buf);
msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_OBJECT_REFERENCE);
msgpack_pack_long(buf, 0);
msgpack_pack_long(buf, *var_already);
return;
}
2010-07-17 18:46:28 +09:00
}
else if (Z_TYPE_P(val) == IS_OBJECT)
{
msgpack_pack_map(buf, 2);
msgpack_pack_nil(buf);
msgpack_pack_long(buf, MSGPACK_SERIALIZE_TYPE_OBJECT);
2010-07-17 18:46:28 +09:00
2010-09-29 08:47:06 +09:00
msgpack_pack_long(buf, 0);
2010-07-17 18:46:28 +09:00
msgpack_pack_long(buf, *var_already);
return;
}
}
switch (Z_TYPE_P(val))
{
case IS_NULL:
msgpack_pack_nil(buf);
break;
case IS_BOOL:
if (Z_BVAL_P(val))
{
msgpack_pack_true(buf);
}
else
{
msgpack_pack_false(buf);
}
break;
case IS_LONG:
msgpack_pack_long(buf, Z_LVAL_P(val));
break;
case IS_DOUBLE:
{
double dbl = Z_DVAL_P(val);
msgpack_pack_double(buf, dbl);
}
break;
case IS_STRING:
msgpack_serialize_string(
buf, Z_STRVAL_P(val), Z_STRLEN_P(val));
break;
case IS_ARRAY:
msgpack_serialize_array(
buf, val, var_hash, 0, NULL, 0, 0 TSRMLS_CC);
2010-07-17 18:46:28 +09:00
break;
case IS_OBJECT:
{
PHP_CLASS_ATTRIBUTES;
PHP_SET_CLASS_ATTRIBUTES(val);
msgpack_serialize_object(
buf, val, var_hash, class_name, name_len,
incomplete_class TSRMLS_CC);
PHP_CLEANUP_CLASS_ATTRIBUTES();
}
break;
default:
MSGPACK_WARNING(
"[msgpack] (%s) type is unsupported, encoded as null",
__FUNCTION__);
2010-07-17 18:46:28 +09:00
msgpack_pack_nil(buf);
break;
}
return;
}