convert zend_hash_apply_with_argument(s)
[m6w6/ext-pq] / src / php_pq_params.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: pq |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <php.h>
18 #include <ext/standard/php_string.h>
19 #include <ext/standard/php_smart_str.h>
20 #include <ext/json/php_json.h>
21
22 #include <Zend/zend_interfaces.h>
23
24 #include <libpq-fe.h>
25
26 #include "php_pq.h"
27 #include "php_pq_params.h"
28 #include "php_pq_misc.h"
29 #undef PHP_PQ_TYPE
30 #include "php_pq_type.h"
31
32 void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv)
33 {
34 zend_hash_clean(&p->type.conv);
35 zend_hash_copy(&p->type.conv, conv, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
36 }
37
38 static int apply_to_oid(void *p, void *arg TSRMLS_DC)
39 {
40 Oid **types = arg;
41 zval **ztype = p;
42
43 if (Z_TYPE_PP(ztype) != IS_LONG) {
44 convert_to_long_ex(ztype);
45 }
46
47 **types = Z_LVAL_PP(ztype);
48 ++*types;
49
50 if (*ztype != *(zval **)p) {
51 zval_ptr_dtor(ztype);
52 }
53 return ZEND_HASH_APPLY_KEEP;
54 }
55
56 unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids)
57 {
58 p->type.count = oids ? zend_hash_num_elements(oids) : 0;
59 TSRMLS_DF(p);
60
61 if (p->type.oids) {
62 efree(p->type.oids);
63 p->type.oids = NULL;
64 }
65 if (p->type.count) {
66 Oid *ptr = ecalloc(p->type.count + 1, sizeof(*p->type.oids));
67 /* +1 for when less types than params are specified */
68 p->type.oids = ptr;
69 zend_hash_apply_with_argument(oids, apply_to_oid, &ptr TSRMLS_CC);
70 }
71 return p->type.count;
72 }
73
74 unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type)
75 {
76 p->type.oids = safe_erealloc(p->type.oids, ++p->type.count, sizeof(*p->type.oids), sizeof(*p->type.oids));
77 p->type.oids[p->type.count] = 0;
78 p->type.oids[p->type.count-1] = type;
79 return p->type.count;
80 }
81
82
83 static zval *object_param_to_string(php_pq_params_t *p, zval *zobj, Oid type TSRMLS_DC)
84 {
85 zval *return_value = NULL;
86 smart_str str = {0};
87
88 switch (type) {
89 #ifdef PHP_PQ_OID_JSON
90 # ifdef PHP_PQ_OID_JSONB
91 case PHP_PQ_OID_JSONB:
92 # endif
93 case PHP_PQ_OID_JSON:
94 php_json_encode(&str, zobj, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC);
95 smart_str_0(&str);
96 break;
97 #endif
98
99 case PHP_PQ_OID_DATE:
100 php_pqdt_to_string(zobj, "Y-m-d", &str.c, &str.len TSRMLS_CC);
101 break;
102
103 case PHP_PQ_OID_ABSTIME:
104 php_pqdt_to_string(zobj, "Y-m-d H:i:s", &str.c, &str.len TSRMLS_CC);
105 break;
106
107 case PHP_PQ_OID_TIMESTAMP:
108 php_pqdt_to_string(zobj, "Y-m-d H:i:s.u", &str.c, &str.len TSRMLS_CC);
109 break;
110
111 case PHP_PQ_OID_TIMESTAMPTZ:
112 php_pqdt_to_string(zobj, "Y-m-d H:i:s.uO", &str.c, &str.len TSRMLS_CC);
113 break;
114
115 default:
116 SEPARATE_ZVAL(&zobj);
117 convert_to_string(zobj);
118 return_value = zobj;
119 break;
120 }
121
122 if (str.c) {
123 MAKE_STD_ZVAL(return_value);
124 RETVAL_STRINGL(str.c, str.len, 0);
125 }
126
127 return return_value;
128 }
129
130 struct apply_to_param_from_array_arg {
131 php_pq_params_t *params;
132 unsigned index;
133 smart_str *buffer;
134 Oid type;
135 zval **zconv;
136 };
137
138 static int apply_to_param_from_array(void *ptr, void *arg_ptr TSRMLS_DC)
139 {
140 struct apply_to_param_from_array_arg subarg, *arg = arg_ptr;
141 zval *ztmp, **zparam = ptr, *zcopy = *zparam;
142 char *tmp;
143 size_t len;
144 int tmp_len;
145
146 if (arg->index++) {
147 smart_str_appendc(arg->buffer, ',');
148 }
149
150 if (arg->zconv) {
151 zval *rv = NULL;
152
153 zend_call_method_with_1_params(arg->zconv, NULL, NULL, "converttostring", &rv, zcopy);
154 convert_to_string(rv);
155 zcopy = rv;
156 goto append_string;
157 } else {
158 switch (Z_TYPE_P(zcopy)) {
159 case IS_NULL:
160 smart_str_appends(arg->buffer, "NULL");
161 break;
162
163 case IS_BOOL:
164 smart_str_appends(arg->buffer, Z_BVAL_P(zcopy) ? "t" : "f");
165 break;
166
167 case IS_LONG:
168 smart_str_append_long(arg->buffer, Z_LVAL_P(zcopy));
169 break;
170
171 case IS_DOUBLE:
172 len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zcopy));
173 smart_str_appendl(arg->buffer, tmp, len);
174 efree(tmp);
175 break;
176
177 case IS_ARRAY:
178 subarg = *arg;
179 subarg.index = 0;
180 smart_str_appendc(arg->buffer, '{');
181 zend_hash_apply_with_argument(Z_ARRVAL_P(zcopy), apply_to_param_from_array, &subarg TSRMLS_CC);
182 smart_str_appendc(arg->buffer, '}');
183 break;
184
185 case IS_OBJECT:
186 if ((ztmp = object_param_to_string(arg->params, zcopy, arg->type TSRMLS_CC))) {
187 zcopy = ztmp;
188 }
189 /* no break */
190 default:
191 SEPARATE_ZVAL(&zcopy);
192 convert_to_string(zcopy);
193
194 append_string:
195 tmp = php_addslashes(Z_STRVAL_P(zcopy), Z_STRLEN_P(zcopy), &tmp_len, 0 TSRMLS_CC);
196 smart_str_appendc(arg->buffer, '"');
197 smart_str_appendl(arg->buffer, tmp, tmp_len);
198 smart_str_appendc(arg->buffer, '"');
199
200 if (zcopy != *zparam) {
201 zval_ptr_dtor(&zcopy);
202 }
203 efree(tmp);
204 break;
205 }
206 }
207 ++arg->index;
208 return ZEND_HASH_APPLY_KEEP;
209 }
210
211 static zval *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type TSRMLS_DC)
212 {
213 zval *return_value;
214 smart_str s = {0};
215 struct apply_to_param_from_array_arg arg = {NULL};
216
217 switch (type) {
218 #ifdef PHP_PQ_OID_JSON
219 # ifdef PHP_PQ_OID_JSONB
220 case PHP_PQ_OID_JSONB:
221 # endif
222 case PHP_PQ_OID_JSON:
223 php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE TSRMLS_CC);
224 smart_str_0(&s);
225 break;
226 #endif
227
228 default:
229 arg.params = p;
230 arg.buffer = &s;
231 arg.type = PHP_PQ_TYPE_OF_ARRAY(type);
232 zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type), (void *) &arg.zconv);
233 smart_str_appendc(arg.buffer, '{');
234 zend_hash_apply_with_argument(Z_ARRVAL_P(zarr), apply_to_param_from_array, &arg TSRMLS_CC);
235 smart_str_appendc(arg.buffer, '}');
236 smart_str_0(&s);
237 break;
238 }
239
240 /* must not return NULL */
241 MAKE_STD_ZVAL(return_value);
242
243 if (s.c) {
244 RETVAL_STRINGL(s.c, s.len, 0);
245 } else {
246 RETVAL_EMPTY_STRING();
247 }
248
249 return return_value;
250 }
251
252 static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval **zpp)
253 {
254 zval **zconv = NULL;
255 Oid type = p->type.count > index ? p->type.oids[index] : 0;
256 TSRMLS_DF(p);
257
258 if (type && SUCCESS == zend_hash_index_find(&p->type.conv, type, (void *) &zconv)) {
259 zval *rv = NULL;
260
261 zend_call_method_with_1_params(zconv, NULL, NULL, "converttostring", &rv, *zpp);
262 convert_to_string(rv);
263 p->param.strings[index] = Z_STRVAL_P(rv);
264 zend_hash_next_index_insert(&p->param.dtor, (void *) &rv, sizeof(zval *), NULL);
265 } else {
266 zval *tmp, *zcopy = *zpp;
267
268 switch (Z_TYPE_P(zcopy)) {
269 case IS_NULL:
270 p->param.strings[index] = NULL;
271 return;
272
273 case IS_BOOL:
274 p->param.strings[index] = Z_BVAL_P(zcopy) ? "t" : "f";
275 return;
276
277 case IS_DOUBLE:
278 SEPARATE_ZVAL(&zcopy);
279 Z_TYPE_P(zcopy) = IS_STRING;
280 Z_STRLEN_P(zcopy) = spprintf(&Z_STRVAL_P(zcopy), 0, "%F", Z_DVAL_PP(zpp));
281 break;
282
283 case IS_ARRAY:
284 zcopy = array_param_to_string(p, zcopy, type TSRMLS_CC);
285 break;
286
287 case IS_OBJECT:
288 if ((tmp = object_param_to_string(p, zcopy, type TSRMLS_CC))) {
289 zcopy = tmp;
290 break;
291 }
292 /* no break */
293
294 default:
295 convert_to_string_ex(&zcopy);
296 break;
297 }
298
299 p->param.strings[index] = Z_STRVAL_P(zcopy);
300
301 if (zcopy != *zpp) {
302 zend_hash_next_index_insert(&p->param.dtor, (void *) &zcopy, sizeof(zval *), NULL);
303 }
304 }
305 }
306
307 static int apply_to_params(void *zp TSRMLS_DC, int argc, va_list argv, zend_hash_key *key)
308 {
309 php_pq_params_t *p = (php_pq_params_t *) va_arg(argv, php_pq_params_t *);
310 unsigned *index = (unsigned *) va_arg(argv, unsigned *);
311
312 php_pq_params_set_param(p, (*index)++, zp);
313 return ZEND_HASH_APPLY_KEEP;
314 }
315
316 unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param)
317 {
318 p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0);
319 php_pq_params_set_param(p, p->param.count-1, &param);
320 return p->type.count;
321 }
322
323 unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params)
324 {
325 p->param.count = params ? zend_hash_num_elements(params) : 0;
326 TSRMLS_DF(p);
327
328 if (p->param.strings) {
329 efree(p->param.strings);
330 p->param.strings = NULL;
331 }
332 zend_hash_clean(&p->param.dtor);
333 if (p->param.count) {
334 unsigned index = 0;
335 p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings));
336 zend_hash_apply_with_arguments(params TSRMLS_CC, apply_to_params, 2, p, &index);
337 }
338 return p->param.count;
339 }
340
341 void php_pq_params_free(php_pq_params_t **p)
342 {
343 if (*p) {
344 php_pq_params_set_type_oids(*p, NULL);
345 php_pq_params_set_params(*p, NULL);
346
347 zend_hash_destroy(&(*p)->param.dtor);
348 zend_hash_destroy(&(*p)->type.conv);
349
350 efree(*p);
351 *p = NULL;
352 }
353 }
354
355 php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params TSRMLS_DC)
356 {
357 php_pq_params_t *p = ecalloc(1, sizeof(*p));
358
359 TSRMLS_CF(p);
360 zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0);
361 zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0);
362
363 if (conv) {
364 php_pq_params_set_type_conv(p, conv);
365 }
366 if (oids) {
367 php_pq_params_set_type_oids(p, oids);
368 }
369 if (params) {
370 php_pq_params_set_params(p, params);
371 }
372
373 return p;
374 }
375
376 /*
377 * Local variables:
378 * tab-width: 4
379 * c-basic-offset: 4
380 * End:
381 * vim600: noet sw=4 ts=4 fdm=marker
382 * vim<600: noet sw=4 ts=4
383 */