fix issue #23 (PHP-7.1 JSON compat)
[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 #if PHP_PQ_HAVE_PHP_JSON_H
20 #include <php_json.h> /* we've added the include directory to INCLUDES */
21 #endif
22
23 #include <Zend/zend_smart_str.h>
24 #include <Zend/zend_interfaces.h>
25
26 #include <libpq-fe.h>
27
28 #include "php_pq.h"
29 #include "php_pq_params.h"
30 #include "php_pq_misc.h"
31 #undef PHP_PQ_TYPE
32 #include "php_pq_type.h"
33
34 void php_pq_params_set_type_conv(php_pq_params_t *p, HashTable *conv)
35 {
36 zend_hash_clean(&p->type.conv);
37 zend_hash_copy(&p->type.conv, conv, (copy_ctor_func_t) zval_add_ref);
38 }
39
40 static int apply_to_oid(zval *ztype, void *arg)
41 {
42 Oid **types = arg;
43
44 **types = zval_get_long(ztype);
45 ++*types;
46
47 return ZEND_HASH_APPLY_KEEP;
48 }
49
50 unsigned php_pq_params_set_type_oids(php_pq_params_t *p, HashTable *oids)
51 {
52 p->type.count = oids ? zend_hash_num_elements(oids) : 0;
53
54 if (p->type.oids) {
55 efree(p->type.oids);
56 p->type.oids = NULL;
57 }
58 if (p->type.count) {
59 Oid *ptr = ecalloc(p->type.count + 1, sizeof(*p->type.oids));
60 /* +1 for when less types than params are specified */
61 p->type.oids = ptr;
62 zend_hash_apply_with_argument(oids, apply_to_oid, &ptr);
63 }
64 return p->type.count;
65 }
66
67 unsigned php_pq_params_add_type_oid(php_pq_params_t *p, Oid type)
68 {
69 p->type.oids = safe_erealloc(p->type.oids, ++p->type.count, sizeof(*p->type.oids), sizeof(*p->type.oids));
70 p->type.oids[p->type.count] = 0;
71 p->type.oids[p->type.count-1] = type;
72 return p->type.count;
73 }
74
75
76 static zend_string *object_param_to_string(php_pq_params_t *p, zval *zobj, Oid type)
77 {
78 smart_str str = {0};
79
80 switch (type) {
81 #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON)
82 # ifdef PHP_PQ_OID_JSONB
83 case PHP_PQ_OID_JSONB:
84 # endif
85 case PHP_PQ_OID_JSON:
86 # if PHP_VERSION_ID >= 70100
87 JSON_G(encode_max_depth) = PHP_JSON_PARSER_DEFAULT_DEPTH;
88 # endif
89 php_json_encode(&str, zobj, PHP_JSON_UNESCAPED_UNICODE);
90 smart_str_0(&str);
91 return str.s;
92 #endif
93
94 case PHP_PQ_OID_DATE:
95 return php_pqdt_to_string(zobj, "Y-m-d");
96
97 case PHP_PQ_OID_ABSTIME:
98 return php_pqdt_to_string(zobj, "Y-m-d H:i:s");
99
100 case PHP_PQ_OID_TIMESTAMP:
101 return php_pqdt_to_string(zobj, "Y-m-d H:i:s.u");
102
103 case PHP_PQ_OID_TIMESTAMPTZ:
104 return php_pqdt_to_string(zobj, "Y-m-d H:i:s.uO");
105 }
106
107 return zval_get_string(zobj);
108 }
109
110 struct apply_to_param_from_array_arg {
111 php_pq_params_t *params;
112 unsigned index;
113 smart_str *buffer;
114 Oid type;
115 char delim;
116 zval *zconv;
117 };
118
119 static int apply_to_param_from_array(zval *zparam, void *arg_ptr)
120 {
121 struct apply_to_param_from_array_arg subarg, *arg = arg_ptr;
122 char *tmp;
123 size_t len;
124 zend_string *str;
125
126 if (arg->index++) {
127 smart_str_appendc(arg->buffer, arg->delim);
128 }
129
130 if (arg->zconv) {
131 zval ztype, rv;
132
133 ZVAL_LONG(&ztype, arg->type);
134 zend_call_method_with_2_params(arg->zconv, NULL, NULL, "converttostring", &rv, zparam, &ztype);
135 str = zval_get_string(&rv);
136 zval_ptr_dtor(&rv);
137 goto append_string;
138
139 } else {
140 again:
141 switch (Z_TYPE_P(zparam)) {
142 case IS_REFERENCE:
143 ZVAL_DEREF(zparam);
144 goto again;
145
146 case IS_NULL:
147 smart_str_appends(arg->buffer, "NULL");
148 break;
149
150 case IS_TRUE:
151 smart_str_appends(arg->buffer, "t");
152 break;
153
154 case IS_FALSE:
155 smart_str_appends(arg->buffer, "f");
156 break;
157
158 case IS_LONG:
159 smart_str_append_long(arg->buffer, Z_LVAL_P(zparam));
160 break;
161
162 case IS_DOUBLE:
163 len = spprintf(&tmp, 0, "%F", Z_DVAL_P(zparam));
164 smart_str_appendl(arg->buffer, tmp, len);
165 efree(tmp);
166 break;
167
168 case IS_ARRAY:
169 subarg = *arg;
170 subarg.index = 0;
171 smart_str_appendc(arg->buffer, '{');
172 zend_hash_apply_with_argument(Z_ARRVAL_P(zparam), apply_to_param_from_array, &subarg);
173 smart_str_appendc(arg->buffer, '}');
174 break;
175
176 case IS_OBJECT:
177 if ((str = object_param_to_string(arg->params, zparam, arg->type))) {
178 goto append_string;
179 }
180 /* no break */
181 default:
182 str = zval_get_string(zparam);
183
184 append_string:
185 str = php_addslashes(str, 1);
186 smart_str_appendc(arg->buffer, '"');
187 smart_str_appendl(arg->buffer, str->val, str->len);
188 smart_str_appendc(arg->buffer, '"');
189 zend_string_release(str);
190 break;
191 }
192 }
193 ++arg->index;
194 return ZEND_HASH_APPLY_KEEP;
195 }
196
197 static zend_string *array_param_to_string(php_pq_params_t *p, zval *zarr, Oid type)
198 {
199 smart_str s = {0};
200 struct apply_to_param_from_array_arg arg = {NULL};
201
202 switch (type) {
203 #if PHP_PQ_HAVE_PHP_JSON_H && defined(PHP_PQ_OID_JSON)
204 # ifdef PHP_PQ_OID_JSONB
205 case PHP_PQ_OID_JSONB:
206 # endif
207 case PHP_PQ_OID_JSON:
208 php_json_encode(&s, zarr, PHP_JSON_UNESCAPED_UNICODE);
209 break;
210 #endif
211
212 default:
213 arg.params = p;
214 arg.buffer = &s;
215 arg.type = PHP_PQ_TYPE_OF_ARRAY(type);
216 arg.delim = PHP_PQ_DELIM_OF_ARRAY(type);
217 arg.zconv = zend_hash_index_find(&p->type.conv, PHP_PQ_TYPE_OF_ARRAY(type));
218 smart_str_appendc(arg.buffer, '{');
219 SEPARATE_ZVAL(zarr);
220 zend_hash_apply_with_argument(Z_ARRVAL_P(zarr), apply_to_param_from_array, &arg);
221 smart_str_appendc(arg.buffer, '}');
222 break;
223 }
224
225 smart_str_0(&s);
226 return s.s;
227 }
228
229 static void php_pq_params_set_param(php_pq_params_t *p, unsigned index, zval *zpp)
230 {
231 zval *zconv = NULL;
232 Oid type = p->type.count > index ? p->type.oids[index] : 0;
233
234 if (type && (zconv = zend_hash_index_find(&p->type.conv, type))) {
235 zval ztype, rv;
236
237 ZVAL_NULL(&rv);
238 ZVAL_LONG(&ztype, type);
239 zend_call_method_with_2_params(zconv, NULL, NULL, "converttostring", &rv, zpp, &ztype);
240 convert_to_string(&rv);
241 p->param.strings[index] = Z_STRVAL_P(&rv);
242 zend_hash_next_index_insert(&p->param.dtor, &rv);
243 } else {
244 zval tmp;
245 zend_string *str = NULL;
246 char tmp_str[64];
247 size_t tmp_len = 0;
248
249 again:
250 switch (Z_TYPE_P(zpp)) {
251 case IS_REFERENCE:
252 ZVAL_DEREF(zpp);
253 goto again;
254
255 case IS_NULL:
256 p->param.strings[index] = NULL;
257 return;
258
259 case IS_TRUE:
260 p->param.strings[index] = "t";
261 break;
262
263 case IS_FALSE:
264 p->param.strings[index] = "f";
265 return;
266
267 case IS_DOUBLE:
268 tmp_len = slprintf(tmp_str, sizeof(tmp_str), "%F", Z_DVAL_P(zpp));
269 str = zend_string_init(tmp_str, tmp_len, 0);
270 break;
271
272 case IS_ARRAY:
273 str = array_param_to_string(p, zpp, type);
274 break;
275
276 case IS_OBJECT:
277 if ((str = object_param_to_string(p, zpp, type))) {
278 break;
279 }
280 /* no break */
281 default:
282 str = zval_get_string(zpp);
283 break;
284 }
285
286 if (str) {
287 ZVAL_STR(&tmp, str);
288 p->param.strings[index] = Z_STRVAL(tmp);
289 zend_hash_next_index_insert(&p->param.dtor, &tmp);
290 }
291 }
292 }
293
294 struct apply_to_params_arg {
295 php_pq_params_t *params;
296 unsigned index;
297 };
298
299 static int apply_to_params(zval *zp, void *arg_ptr)
300 {
301 struct apply_to_params_arg *arg = arg_ptr;
302
303 SEPARATE_ZVAL_IF_NOT_REF(zp);
304 php_pq_params_set_param(arg->params, arg->index++, zp);
305 return ZEND_HASH_APPLY_KEEP;
306 }
307
308 unsigned php_pq_params_add_param(php_pq_params_t *p, zval *param)
309 {
310 p->param.strings = safe_erealloc(p->param.strings, ++p->param.count, sizeof(*p->param.strings), 0);
311 php_pq_params_set_param(p, p->param.count-1, param);
312 return p->type.count;
313 }
314
315 unsigned php_pq_params_set_params(php_pq_params_t *p, HashTable *params)
316 {
317 p->param.count = params ? zend_hash_num_elements(params) : 0;
318
319 if (p->param.strings) {
320 efree(p->param.strings);
321 p->param.strings = NULL;
322 }
323 zend_hash_clean(&p->param.dtor);
324 if (p->param.count) {
325 struct apply_to_params_arg arg = {p, 0};
326 p->param.strings = ecalloc(p->param.count, sizeof(*p->param.strings));
327 zend_hash_apply_with_argument(params, apply_to_params, &arg);
328 }
329 return p->param.count;
330 }
331
332 void php_pq_params_free(php_pq_params_t **p)
333 {
334 if (*p) {
335 php_pq_params_set_type_oids(*p, NULL);
336 php_pq_params_set_params(*p, NULL);
337
338 zend_hash_destroy(&(*p)->param.dtor);
339 zend_hash_destroy(&(*p)->type.conv);
340
341 efree(*p);
342 *p = NULL;
343 }
344 }
345
346 php_pq_params_t *php_pq_params_init(HashTable *conv, HashTable *oids, HashTable *params)
347 {
348 php_pq_params_t *p = ecalloc(1, sizeof(*p));
349
350 zend_hash_init(&p->type.conv, 0, NULL, ZVAL_PTR_DTOR, 0);
351 zend_hash_init(&p->param.dtor, 0, NULL, ZVAL_PTR_DTOR, 0);
352
353 if (conv) {
354 php_pq_params_set_type_conv(p, conv);
355 }
356 if (oids) {
357 php_pq_params_set_type_oids(p, oids);
358 }
359 if (params) {
360 php_pq_params_set_params(p, params);
361 }
362
363 return p;
364 }
365
366 /*
367 * Local variables:
368 * tab-width: 4
369 * c-basic-offset: 4
370 * End:
371 * vim600: noet sw=4 ts=4 fdm=marker
372 * vim<600: noet sw=4 ts=4
373 */