1 #include "php.h" 2 3 #include "php_msgpack.h" 4 #include "msgpack_convert.h" 5 #include "msgpack_errors.h" 6 7 static inline int msgpack_convert_long_to_properties(HashTable *ht, zval *object, HashTable **properties, HashPosition *prop_pos, unsigned int key_index, zval *val, HashTable *var) /* {{{ */ { 8 zval key_zv; 9 HashTable *props = *properties; 10 11 if (props != NULL) { 12 zval *data, tplval, *dataval, prop_key_zv; 13 zend_string *prop_key; 14 zend_ulong prop_key_index; 15 const char *class_name, *prop_name; 16 size_t prop_len; 17 18 for (;; zend_hash_move_forward_ex(props, prop_pos)) { 19 if (zend_hash_get_current_key_ex(props, &prop_key, &prop_key_index, prop_pos) == HASH_KEY_IS_STRING) { 20 zend_unmangle_property_name_ex(prop_key, &class_name, &prop_name, &prop_len); 21 ZVAL_NEW_STR(&prop_key_zv, prop_key); 22 if (var == NULL || !zend_hash_str_exists(var, prop_name, prop_len)) { 23 if ((data = zend_hash_find(ht, prop_key)) != NULL) { 24 switch (Z_TYPE_P(data)) { 25 case IS_ARRAY: 26 { 27 HashTable *dataht; 28 dataht = HASH_OF(val); 29 30 if ((dataval = zend_hash_index_find(dataht, prop_key_index)) == NULL) { 31 MSGPACK_WARNING("[msgpack] (%s) " 32 "can't get data value by index", 33 __FUNCTION__); 34 return FAILURE; 35 } 36 37 if (msgpack_convert_array(&tplval, data, dataval) == SUCCESS) { 38 zend_hash_move_forward_ex(props, prop_pos); 39 zend_update_property(Z_OBJCE_P(object), OBJ_FOR_PROP(object), prop_name, prop_len, &tplval); 40 return SUCCESS; 41 } 42 return FAILURE; 43 } 44 case IS_OBJECT: 45 { 46 if (msgpack_convert_object(&tplval, data, val) == SUCCESS) { 47 zend_hash_move_forward_ex(props, prop_pos); 48 zend_update_property(Z_OBJCE_P(object), OBJ_FOR_PROP(object), prop_name, prop_len, &tplval); 49 return SUCCESS; 50 } 51 return FAILURE; 52 } 53 default: 54 zend_hash_move_forward_ex(props, prop_pos); 55 zend_update_property(Z_OBJCE_P(object), OBJ_FOR_PROP(object), prop_name, prop_len, val); 56 return SUCCESS; 57 } 58 } 59 } 60 } else { 61 break; 62 } 63 } 64 *properties = NULL; 65 } 66 ZVAL_LONG(&key_zv, key_index); 67 #if PHP_VERSION_ID < 80000 68 zend_std_write_property(object, &key_zv, val, NULL); 69 #else 70 { 71 zend_string *key = zval_get_string(&key_zv); 72 zend_std_write_property(Z_OBJ_P(object), key, val, NULL); 73 zend_string_release(key); 74 } 75 #endif 76 return SUCCESS; 77 } 78 /* }}} */ 79 80 static inline int msgpack_convert_string_to_properties(zval *object, zend_string *key, zval *val, HashTable *var)/* {{{ */ { 81 zend_class_entry *ce = Z_OBJCE_P(object); 82 HashTable *propers = Z_OBJPROP_P(object); 83 zend_string *prot_name, *priv_name; 84 zval pub_name; 85 int return_code; 86 87 ZVAL_STR(&pub_name, key); 88 priv_name = zend_mangle_property_name(ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(key), ZSTR_LEN(key), 1); 89 prot_name = zend_mangle_property_name("*", 1, ZSTR_VAL(key), ZSTR_LEN(key), 1); 90 91 if (zend_hash_find(propers, priv_name) != NULL) { 92 zend_update_property_ex(ce, OBJ_FOR_PROP(object), key, val); 93 return_code = SUCCESS; 94 } else if (zend_hash_find(propers, prot_name) != NULL) { 95 zend_update_property_ex(ce, OBJ_FOR_PROP(object), key, val); 96 return_code = SUCCESS; 97 } else { 98 #if PHP_VERSION_ID < 80000 99 zend_std_write_property(object, &pub_name, val, NULL); 100 #else 101 zend_std_write_property(Z_OBJ_P(object), key, val, NULL); 102 #endif 103 return_code = FAILURE; 104 } 105 zend_hash_add(var, Z_STR(pub_name), val); 106 107 zend_string_release(priv_name); 108 zend_string_release(prot_name); 109 110 return return_code; 111 } 112 /* }}} */ 113 114 int msgpack_convert_array(zval *return_value, zval *tpl, zval *value) /* {{{ */ { 115 zend_string *key; 116 int key_type; 117 zend_ulong key_index; 118 zval *data; 119 HashTable *ht, *htval; 120 121 if (Z_TYPE_P(tpl) != IS_ARRAY) { 122 MSGPACK_WARNING("[msgpack] (%s) template is not array", __FUNCTION__); 123 return FAILURE; 124 } 125 126 if (Z_TYPE_P(value) == IS_INDIRECT) { 127 value = Z_INDIRECT_P(value); 128 } 129 130 ht = HASH_OF(tpl); 131 array_init(return_value); 132 133 if (zend_hash_num_elements(ht) == 0) { 134 MSGPACK_WARNING("[msgpack] (%s) template array length is 0", __FUNCTION__); 135 return FAILURE; 136 } 137 138 /* string */ 139 if (ht->nNumOfElements != ht->nNextFreeElement) { 140 HashPosition valpos; 141 142 htval = HASH_OF(value); 143 144 if (!htval) { 145 MSGPACK_WARNING("[msgpack] (%s) input data is not array", __FUNCTION__); 146 return FAILURE; 147 } 148 149 zend_hash_internal_pointer_reset_ex(htval, &valpos); 150 ZEND_HASH_FOREACH_KEY_VAL(ht, key_index, key, data) { 151 if (key) { 152 zval *dataval; 153 int (*convert_function)(zval *, zval *, zval *) = NULL; 154 switch (Z_TYPE_P(data)) { 155 case IS_ARRAY: 156 convert_function = msgpack_convert_array; 157 break; 158 case IS_OBJECT: 159 // case IS_STRING: 160 convert_function = msgpack_convert_object; 161 break; 162 default: 163 break; 164 } 165 166 if ((dataval = zend_hash_get_current_data_ex(htval, &valpos)) == NULL) { 167 MSGPACK_WARNING("[msgpack] (%s) can't get data", __FUNCTION__); 168 return FAILURE; 169 } 170 171 if (Z_TYPE_P(dataval) == IS_INDIRECT) { 172 dataval = Z_INDIRECT_P(dataval); 173 } 174 175 if (convert_function) { 176 zval rv; 177 if (convert_function(&rv, data, dataval) != SUCCESS) { 178 return FAILURE; 179 } 180 zend_symtable_update(Z_ARRVAL_P(return_value), key, &rv); 181 } else { 182 Z_TRY_ADDREF_P(dataval); 183 zend_symtable_update(Z_ARRVAL_P(return_value), key, dataval); 184 } 185 } 186 zend_hash_move_forward_ex(htval, &valpos); 187 } ZEND_HASH_FOREACH_END(); 188 189 return SUCCESS; 190 } else { 191 /* index */ 192 zval *arydata; 193 HashPosition pos; 194 int (*convert_function)(zval *, zval *, zval *) = NULL; 195 196 if (Z_TYPE_P(value) != IS_ARRAY) { 197 MSGPACK_WARNING("[msgpack] (%s) unserialized data must be array.", __FUNCTION__); 198 return FAILURE; 199 } 200 201 zend_hash_internal_pointer_reset_ex(ht, &pos); 202 key_type = zend_hash_get_current_key_ex(ht, &key, &key_index, &pos); 203 if (key_type == HASH_KEY_NON_EXISTENT) { 204 MSGPACK_WARNING( 205 "[msgpack] (%s) first element in template array is empty", 206 __FUNCTION__); 207 return FAILURE; 208 } 209 210 if ((data = zend_hash_get_current_data_ex(ht, &pos)) == NULL) { 211 MSGPACK_WARNING("[msgpack] (%s) invalid template: empty array?", __FUNCTION__); 212 return FAILURE; 213 } 214 215 switch (Z_TYPE_P(data)) { 216 case IS_ARRAY: 217 convert_function = msgpack_convert_array; 218 break; 219 case IS_OBJECT: 220 case IS_STRING: 221 convert_function = msgpack_convert_object; 222 break; 223 default: 224 break; 225 } 226 227 htval = HASH_OF(value); 228 if (zend_hash_num_elements(htval) == 0) { 229 MSGPACK_WARNING("[msgpack] (%s) array length is 0 in unserialized data", __FUNCTION__); 230 return FAILURE; 231 } 232 233 ZEND_HASH_FOREACH_KEY_VAL_IND(htval, key_index, key, arydata) { 234 if (key) { 235 MSGPACK_WARNING("[msgpack] (%s) key is string", __FUNCTION__); 236 return FAILURE; 237 } else { 238 zval rv; 239 if (convert_function) { 240 if (convert_function(&rv, data, arydata) != SUCCESS) { 241 MSGPACK_WARNING( 242 "[msgpack] (%s) " 243 "convert failure in HASH_KEY_IS_LONG " 244 "in indexed array", 245 __FUNCTION__); 246 return FAILURE; 247 } 248 add_next_index_zval(return_value, &rv); 249 } else { 250 Z_TRY_ADDREF_P(arydata); 251 add_next_index_zval(return_value, arydata); 252 } 253 } 254 } ZEND_HASH_FOREACH_END(); 255 return SUCCESS; 256 } 257 258 return FAILURE; 259 } 260 /* }}} */ 261 262 int msgpack_convert_object(zval *return_value, zval *tpl, zval *value) /* {{{ */ { 263 zend_class_entry *ce; 264 265 switch (Z_TYPE_P(tpl)) { 266 case IS_STRING: 267 ce = zend_lookup_class(Z_STR_P(tpl)); 268 if (ce == NULL) { 269 MSGPACK_ERROR("[msgpack] (%s) Class '%s' not found", 270 __FUNCTION__, Z_STRVAL_P(tpl)); 271 return FAILURE; 272 } 273 break; 274 case IS_OBJECT: 275 ce = Z_OBJCE_P(tpl); 276 break; 277 default: 278 MSGPACK_ERROR("[msgpack] (%s) object type is unsupported", 279 __FUNCTION__); 280 return FAILURE; 281 } 282 283 if (Z_TYPE_P(value) == IS_INDIRECT) { 284 value = Z_INDIRECT_P(value); 285 } 286 287 if (Z_TYPE_P(value) == IS_OBJECT) { 288 zend_class_entry *vce = Z_OBJCE_P(value); 289 if (zend_string_equals(ce->name, vce->name)) { 290 ZVAL_COPY(return_value, value); 291 return SUCCESS; 292 } 293 } 294 295 object_init_ex(return_value, ce); 296 297 /* Run the constructor if there is one */ 298 if (ce->constructor && (ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { 299 zval retval; 300 zend_fcall_info fci; 301 zend_fcall_info_cache fcc; 302 303 memset(&fci, 0, sizeof(fci)); 304 memset(&fcc, 0, sizeof(fcc)); 305 306 fci.size = sizeof(fci); 307 #if PHP_VERSION_ID < 70100 308 fci.function_table = EG(function_table); 309 #endif 310 fci.object = Z_OBJ_P(return_value); 311 fci.retval = &retval; 312 #if PHP_VERSION_ID < 80000 313 fci.no_separation = 1; 314 #endif 315 316 #if PHP_VERSION_ID < 70300 317 fcc.initialized = 1; 318 #endif 319 fcc.function_handler = ce->constructor; 320 #if PHP_VERSION_ID < 70100 321 fcc.calling_scope = EG(scope); 322 #else 323 fcc.calling_scope = zend_get_executed_scope(); 324 #endif 325 fcc.called_scope = Z_OBJCE_P(return_value); 326 fcc.object = Z_OBJ_P(return_value); 327 328 if (zend_call_function(&fci, &fcc) == FAILURE) { 329 MSGPACK_WARNING( 330 "[msgpack] (%s) Invocation of %s's constructor failed", 331 __FUNCTION__, ZSTR_VAL(ce->name)); 332 333 return FAILURE; 334 } 335 } 336 337 switch (Z_TYPE_P(value)) { 338 case IS_ARRAY: 339 { 340 int num; 341 HashTable *ht, *ret, *var = NULL; 342 zend_string *str_key; 343 zval *data; 344 zend_ulong num_key; 345 346 ht = HASH_OF(value); 347 ret = HASH_OF(return_value); 348 349 num = zend_hash_num_elements(ht); 350 if (num <= 0) { 351 break; 352 } 353 354 /* string - php_only mode? */ 355 if (ht->nNumOfElements != ht->nNextFreeElement || ht->nNumOfElements != ret->nNumOfElements) { 356 HashTable *properties = NULL; 357 HashPosition prop_pos; 358 359 ALLOC_HASHTABLE(var); 360 zend_hash_init(var, num, NULL, NULL, 0); 361 362 ZEND_HASH_FOREACH_STR_KEY_VAL(ht, str_key, data) { 363 if (str_key) { 364 if (msgpack_convert_string_to_properties(return_value, str_key, data, var) != SUCCESS) { 365 MSGPACK_WARNING("[msgpack] (%s) " 366 "illegal offset type, skip this decoding", 367 __FUNCTION__); 368 } 369 } 370 } ZEND_HASH_FOREACH_END(); 371 372 /* index */ 373 #if PHP_VERSION_ID < 80000 374 properties = Z_OBJ_HT_P(return_value)->get_properties(return_value); 375 #else 376 properties = Z_OBJ_HT_P(return_value)->get_properties(Z_OBJ_P(return_value)); 377 #endif 378 if (HASH_OF(tpl)) { 379 properties = HASH_OF(tpl); 380 } 381 zend_hash_internal_pointer_reset_ex(properties, &prop_pos); 382 383 384 ZEND_HASH_FOREACH_KEY_VAL(ht, num_key, str_key, data) { 385 if (str_key == NULL) { 386 if (msgpack_convert_long_to_properties(ret, return_value, &properties, &prop_pos, num_key, data, var) != SUCCESS) { 387 MSGPACK_WARNING("[msgpack] (%s) " 388 "illegal offset type, skip this decoding", 389 __FUNCTION__); 390 } 391 } 392 } ZEND_HASH_FOREACH_END(); 393 394 zend_hash_destroy(var); 395 FREE_HASHTABLE(var); 396 } else { 397 int (*convert_function)(zval *, zval *, zval *) = NULL; 398 const char *class_name, *prop_name; 399 size_t prop_len; 400 zval *aryval; 401 402 num_key = 0; 403 ZEND_HASH_FOREACH_STR_KEY_VAL(ret, str_key, data) { 404 aryval = zend_hash_index_find(ht, num_key); 405 406 if (data == NULL) { 407 MSGPACK_WARNING("[msgpack] (%s) can't get data value by index", __FUNCTION__); 408 return FAILURE; 409 } 410 411 if (Z_TYPE_P(data) == IS_INDIRECT) { 412 data = Z_INDIRECT_P(data); 413 } 414 415 switch (Z_TYPE_P(data)) { 416 case IS_ARRAY: 417 convert_function = msgpack_convert_array; 418 break; 419 case IS_OBJECT: 420 //case IS_STRING: -- may have default values of 421 // class members, so it's not wise to allow 422 convert_function = msgpack_convert_object; 423 break; 424 } 425 426 zend_unmangle_property_name_ex(str_key, &class_name, &prop_name, &prop_len); 427 428 if (convert_function) { 429 zval nv; 430 if (convert_function(&nv, data, aryval) != SUCCESS) { 431 MSGPACK_WARNING("[msgpack] (%s) " 432 "convert failure in convert_object", 433 __FUNCTION__); 434 return FAILURE; 435 } 436 437 zend_update_property_ex(ce, OBJ_FOR_PROP(return_value), str_key, &nv); 438 zval_ptr_dtor(&nv); 439 } else { 440 zend_update_property(ce, OBJ_FOR_PROP(return_value), prop_name, prop_len, aryval); 441 } 442 num_key++; 443 } ZEND_HASH_FOREACH_END(); 444 } 445 break; 446 } 447 default: 448 { 449 HashTable *properties = NULL; 450 HashPosition prop_pos; 451 452 #if PHP_VERSION_ID < 80000 453 properties = Z_OBJ_HT_P(return_value)->get_properties(return_value); 454 #else 455 properties = Z_OBJ_HT_P(return_value)->get_properties(Z_OBJ_P(return_value)); 456 #endif 457 zend_hash_internal_pointer_reset_ex(properties, &prop_pos); 458 459 if (msgpack_convert_long_to_properties(HASH_OF(return_value), return_value, &properties, &prop_pos, 0, value, NULL) != SUCCESS) { 460 MSGPACK_WARNING("[msgpack] (%s) illegal offset type, skip this decoding", 461 __FUNCTION__); 462 } 463 } 464 } 465 466 return SUCCESS; 467 } 468 /* }}} */ 469 470 int msgpack_convert_template(zval *return_value, zval *tpl, zval *value) /* {{{ */ { 471 switch (Z_TYPE_P(tpl)) { 472 case IS_ARRAY: 473 return msgpack_convert_array(return_value, tpl, value); 474 case IS_STRING: 475 case IS_OBJECT: 476 return msgpack_convert_object(return_value, tpl, value); 477 default: 478 MSGPACK_ERROR("[msgpack] (%s) Template type is unsupported", 479 __FUNCTION__); 480 return FAILURE; 481 } 482 } 483 /* }}} */ 484 485 /* 486 * Local variables: 487 * tab-width: 4 488 * c-basic-offset: 4 489 * End: 490 * vim600: noet sw=4 ts=4 fdm=marker 491 * vim<600: noet sw=4 ts=4 492 */ 493