1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2007 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Alexandre Kalendarev akalend@mail.ru Copyright (c) 2009-2010 |
16 | Lead: |
17 | - Pieter de Zwart |
18 | Maintainers: |
19 | - Brad Rodriguez |
20 | - Jonathan Tansavatdi |
21 +----------------------------------------------------------------------+
22 */
23 #include <stdint.h>
24 #include <amqp.h>
25 #include "Zend/zend_interfaces.h"
26 #include "amqp_type.h"
27 #include "amqp_timestamp.h"
28 #include "amqp_decimal.h"
29
30 #ifdef PHP_WIN32
31 # define strtoimax _strtoi64
32 #endif
33
34 static void php_amqp_type_internal_free_amqp_array(amqp_array_t *array);
35 static void php_amqp_type_internal_free_amqp_table(amqp_table_t *object, zend_bool clear_root);
36
php_amqp_type_char_to_amqp_long(char const * cstr,PHP5to7_param_str_len_type_t len)37 amqp_bytes_t php_amqp_type_char_to_amqp_long(char const *cstr, PHP5to7_param_str_len_type_t len)
38 {
39 amqp_bytes_t result;
40
41 if (len < 1) {
42 return amqp_empty_bytes;
43 }
44
45 result.len = (size_t)len;
46 result.bytes = (void *) cstr;
47
48 return result;
49 }
50
php_amqp_type_amqp_bytes_to_char(amqp_bytes_t bytes)51 char *php_amqp_type_amqp_bytes_to_char(amqp_bytes_t bytes)
52 {
53 /* We will need up to 4 chars per byte, plus the terminating 0 */
54 char *res = emalloc(bytes.len * 4 + 1);
55 uint8_t *data = bytes.bytes;
56 char *p = res;
57 size_t i;
58
59 for (i = 0; i < bytes.len; i++) {
60 if (data[i] >= 32 && data[i] != 127) {
61 *p++ = data[i];
62 } else {
63 *p++ = '\\';
64 *p++ = '0' + (data[i] >> 6);
65 *p++ = '0' + (data[i] >> 3 & 0x7);
66 *p++ = '0' + (data[i] & 0x7);
67 }
68 }
69
70 *p = 0;
71 return res;
72 }
73
php_amqp_type_internal_convert_zval_array(zval * php_array,amqp_field_value_t ** field,zend_bool allow_int_keys TSRMLS_DC)74 void php_amqp_type_internal_convert_zval_array(zval *php_array, amqp_field_value_t **field, zend_bool allow_int_keys TSRMLS_DC)
75 {
76 HashTable *ht;
77 HashPosition pos;
78
79 zval *value;
80 zval **data;
81
82 PHP5to7_ZEND_REAL_HASH_KEY_T *real_key;
83
84 char *key;
85 unsigned key_len;
86
87 zend_ulong index;
88 ht = Z_ARRVAL_P(php_array);
89
90 zend_bool is_amqp_array = 1;
91
92 PHP5to7_ZEND_HASH_FOREACH_KEY_VAL(ht, index, real_key, key, key_len, data, value, pos) {
93 if (PHP5to7_ZEND_HASH_KEY_IS_STRING(ht, real_key, key, key_len, index, pos)) {
94 is_amqp_array = 0;
95 break;
96 }
97
98 } PHP5to7_ZEND_HASH_FOREACH_END();
99
100 if (is_amqp_array) {
101 (*field)->kind = AMQP_FIELD_KIND_ARRAY;
102 php_amqp_type_internal_convert_zval_to_amqp_array(php_array, &(*field)->value.array TSRMLS_CC);
103 } else {
104 (*field)->kind = AMQP_FIELD_KIND_TABLE;
105 php_amqp_type_internal_convert_zval_to_amqp_table(php_array, &(*field)->value.table, allow_int_keys TSRMLS_CC);
106 }
107 }
108
php_amqp_type_internal_convert_zval_to_amqp_table(zval * php_array,amqp_table_t * amqp_table,zend_bool allow_int_keys TSRMLS_DC)109 void php_amqp_type_internal_convert_zval_to_amqp_table(zval *php_array, amqp_table_t *amqp_table, zend_bool allow_int_keys TSRMLS_DC)
110 {
111 HashTable *ht;
112 HashPosition pos;
113
114 zval *value;
115 zval **data;
116
117 PHP5to7_ZEND_REAL_HASH_KEY_T *real_key;
118
119 char *key;
120 unsigned key_len;
121
122 zend_ulong index;
123
124
125 ht = Z_ARRVAL_P(php_array);
126
127 amqp_table->entries = (amqp_table_entry_t *)ecalloc((size_t)zend_hash_num_elements(ht), sizeof(amqp_table_entry_t));
128 amqp_table->num_entries = 0;
129
130 PHP5to7_ZEND_HASH_FOREACH_KEY_VAL(ht, index, real_key, key, key_len, data, value, pos) {
131 char *string_key;
132 amqp_table_entry_t *table_entry;
133 amqp_field_value_t *field;
134
135
136 /* Now pull the key */
137
138 if (!PHP5to7_ZEND_HASH_KEY_IS_STRING(ht, real_key, key, key_len, index, pos)) {
139 if (allow_int_keys) {
140 /* Convert to strings non-string keys */
141 char str[32];
142
143 key_len = sprintf(str, "%lu", index);
144 key = str;
145 } else {
146 /* Skip things that are not strings */
147 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ignoring non-string header field '%lu'", index);
148
149 continue;
150 }
151 } else {
152 PHP5to7_ZEND_HASH_KEY_MAYBE_UNPACK(real_key, key, key_len);
153 }
154
155 /* Build the value */
156 table_entry = &amqp_table->entries[amqp_table->num_entries++];
157 field = &table_entry->value;
158
159 if (!php_amqp_type_internal_convert_php_to_amqp_field_value(value, &field, key TSRMLS_CC)) {
160 /* Reset entries counter back */
161 amqp_table->num_entries --;
162
163 continue;
164 }
165
166 string_key = estrndup(key, key_len);
167 table_entry->key = amqp_cstring_bytes(string_key);
168
169 } PHP5to7_ZEND_HASH_FOREACH_END();
170 }
171
php_amqp_type_internal_convert_zval_to_amqp_array(zval * zvalArguments,amqp_array_t * arguments TSRMLS_DC)172 void php_amqp_type_internal_convert_zval_to_amqp_array(zval *zvalArguments, amqp_array_t *arguments TSRMLS_DC)
173 {
174 HashTable *ht;
175 HashPosition pos;
176
177 zval *value;
178 zval **data;
179
180 PHP5to7_ZEND_REAL_HASH_KEY_T *real_key;
181
182 char *key;
183 unsigned key_len;
184
185 zend_ulong index;
186
187 char type[16];
188
189 ht = Z_ARRVAL_P(zvalArguments);
190
191 /* Allocate all the memory necessary for storing the arguments */
192 arguments->entries = (amqp_field_value_t *)ecalloc((size_t)zend_hash_num_elements(ht), sizeof(amqp_field_value_t));
193 arguments->num_entries = 0;
194
195 PHP5to7_ZEND_HASH_FOREACH_KEY_VAL(ht, index, real_key, key, key_len, data, value, pos) {
196 amqp_field_value_t *field = &arguments->entries[arguments->num_entries++];
197
198 if (!php_amqp_type_internal_convert_php_to_amqp_field_value(value, &field, key TSRMLS_CC)) {
199 /* Reset entries counter back */
200 arguments->num_entries --;
201
202 continue;
203 }
204 } PHP5to7_ZEND_HASH_FOREACH_END();
205 }
206
php_amqp_type_internal_convert_php_to_amqp_field_value(zval * value,amqp_field_value_t ** fieldPtr,char * key TSRMLS_DC)207 zend_bool php_amqp_type_internal_convert_php_to_amqp_field_value(zval *value, amqp_field_value_t **fieldPtr, char *key TSRMLS_DC)
208 {
209 zend_bool result;
210 char type[16];
211 amqp_field_value_t *field;
212
213 result = 1;
214 field = *fieldPtr;
215
216 switch (Z_TYPE_P(value)) {
217 PHP5to7_CASE_IS_BOOL:
218 field->kind = AMQP_FIELD_KIND_BOOLEAN;
219 field->value.boolean = (amqp_boolean_t) !PHP5to7_IS_FALSE_P(value);
220 break;
221 case IS_DOUBLE:
222 field->kind = AMQP_FIELD_KIND_F64;
223 field->value.f64 = Z_DVAL_P(value);
224 break;
225 case IS_LONG:
226 field->kind = AMQP_FIELD_KIND_I64;
227 field->value.i64 = Z_LVAL_P(value);
228 break;
229 case IS_STRING:
230 field->kind = AMQP_FIELD_KIND_UTF8;
231
232 if (Z_STRLEN_P(value)) {
233 amqp_bytes_t bytes;
234 bytes.len = (size_t) Z_STRLEN_P(value);
235 bytes.bytes = estrndup(Z_STRVAL_P(value), (unsigned) Z_STRLEN_P(value));
236
237 field->value.bytes = bytes;
238 } else {
239 field->value.bytes = amqp_empty_bytes;
240 }
241
242 break;
243 case IS_ARRAY:
244 php_amqp_type_internal_convert_zval_array(value, &field, 1 TSRMLS_CC);
245 break;
246 case IS_NULL:
247 field->kind = AMQP_FIELD_KIND_VOID;
248 break;
249 case IS_OBJECT:
250 if (instanceof_function(Z_OBJCE_P(value), amqp_timestamp_class_entry TSRMLS_CC)) {
251 PHP5to7_zval_t result_zv PHP5to7_MAYBE_SET_TO_NULL;
252
253 zend_call_method_with_0_params(PHP5to7_MAYBE_PARAM_PTR(value), amqp_timestamp_class_entry, NULL, "gettimestamp", &result_zv);
254
255 field->kind = AMQP_FIELD_KIND_TIMESTAMP;
256 field->value.u64 = strtoimax(Z_STRVAL(PHP5to7_MAYBE_DEREF(result_zv)), NULL, 10);
257
258 PHP5to7_MAYBE_DESTROY(result_zv);
259
260 break;
261 } else if (instanceof_function(Z_OBJCE_P(value), amqp_decimal_class_entry TSRMLS_CC)) {
262 field->kind = AMQP_FIELD_KIND_DECIMAL;
263 PHP5to7_zval_t result_zv PHP5to7_MAYBE_SET_TO_NULL;
264
265 zend_call_method_with_0_params(PHP5to7_MAYBE_PARAM_PTR(value), amqp_decimal_class_entry, NULL, "getexponent", &result_zv);
266 field->value.decimal.decimals = (uint8_t)Z_LVAL(PHP5to7_MAYBE_DEREF(result_zv));
267 PHP5to7_MAYBE_DESTROY(result_zv);
268
269 zend_call_method_with_0_params(PHP5to7_MAYBE_PARAM_PTR(value), amqp_decimal_class_entry, NULL, "getsignificand", &result_zv);
270 field->value.decimal.value = (uint32_t)Z_LVAL(PHP5to7_MAYBE_DEREF(result_zv));
271
272 PHP5to7_MAYBE_DESTROY(result_zv);
273
274 break;
275 }
276 default:
277 switch(Z_TYPE_P(value)) {
278 case IS_OBJECT:
279 strcpy(type, "object");
280 break;
281 case IS_RESOURCE:
282 strcpy(type, "resource");
283 break;
284 default:
285 strcpy(type, "unknown");
286 break;
287 }
288
289 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Ignoring field '%s' due to unsupported value type (%s)", key, type);
290 result = 0;
291 break;
292 }
293
294 return result;
295 }
296
php_amqp_type_convert_zval_to_amqp_table(zval * php_array TSRMLS_DC)297 amqp_table_t *php_amqp_type_convert_zval_to_amqp_table(zval *php_array TSRMLS_DC)
298 {
299 amqp_table_t *amqp_table;
300 /* In setArguments, we are overwriting all the existing values */
301 amqp_table = (amqp_table_t *)emalloc(sizeof(amqp_table_t));
302
303 php_amqp_type_internal_convert_zval_to_amqp_table(php_array, amqp_table, 0 TSRMLS_CC);
304
305 return amqp_table;
306 }
307
308
php_amqp_type_internal_free_amqp_array(amqp_array_t * array)309 static void php_amqp_type_internal_free_amqp_array(amqp_array_t *array) {
310 if (!array) {
311 return;
312 }
313
314 int macroEntryCounter;
315 for (macroEntryCounter = 0; macroEntryCounter < array->num_entries; macroEntryCounter++) {
316
317 amqp_field_value_t *entry = &array->entries[macroEntryCounter];
318
319 switch (entry->kind) {
320 case AMQP_FIELD_KIND_TABLE:
321 php_amqp_type_internal_free_amqp_table(&entry->value.table, 0);
322 break;
323 case AMQP_FIELD_KIND_ARRAY:
324 php_amqp_type_internal_free_amqp_array(&entry->value.array);
325 break;
326 case AMQP_FIELD_KIND_UTF8:
327 if (entry->value.bytes.bytes) {
328 efree(entry->value.bytes.bytes);
329 }
330 break;
331 //
332 default:
333 break;
334 }
335 }
336
337 if (array->entries) {
338 efree(array->entries);
339 }
340 }
341
php_amqp_type_internal_free_amqp_table(amqp_table_t * object,zend_bool clear_root)342 static void php_amqp_type_internal_free_amqp_table(amqp_table_t *object, zend_bool clear_root)
343 {
344 if (!object) {
345 return;
346 }
347
348 if (object->entries) {
349 int macroEntryCounter;
350 for (macroEntryCounter = 0; macroEntryCounter < object->num_entries; macroEntryCounter++) {
351
352 amqp_table_entry_t *entry = &object->entries[macroEntryCounter];
353 efree(entry->key.bytes);
354
355 switch (entry->value.kind) {
356 case AMQP_FIELD_KIND_TABLE:
357 php_amqp_type_internal_free_amqp_table(&entry->value.value.table, 0);
358 break;
359 case AMQP_FIELD_KIND_ARRAY:
360 php_amqp_type_internal_free_amqp_array(&entry->value.value.array);
361 break;
362 case AMQP_FIELD_KIND_UTF8:
363 if (entry->value.value.bytes.bytes) {
364 efree(entry->value.value.bytes.bytes);
365 }
366 break;
367 default:
368 break;
369 }
370 }
371 efree(object->entries);
372 }
373
374 if (clear_root) {
375 efree(object);
376 }
377 }
378
php_amqp_type_free_amqp_table(amqp_table_t * object)379 void php_amqp_type_free_amqp_table(amqp_table_t *object)
380 {
381 php_amqp_type_internal_free_amqp_table(object, 1);
382 }
383