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