Merge branch 'master' into phpng
[m6w6/ext-pq] / src / php_pq_misc.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/date/php_date.h>
19 #include <ext/standard/php_string.h>
20
21 #include <Zend/zend_interfaces.h>
22
23 #include <libpq/libpq-fs.h>
24
25 #include "php_pq.h"
26 #include "php_pqexc.h"
27 #include "php_pq_misc.h"
28 #undef PHP_PQ_TYPE
29 #include "php_pq_type.h"
30
31 char *php_pq_rtrim(char *e)
32 {
33 size_t l = strlen(e);
34
35 while (l-- > 0 && e[l] == '\n') {
36 e[l] = '\0';
37 }
38 return e;
39 }
40
41 const char *php_pq_strmode(long mode)
42 {
43 switch (mode & (INV_READ|INV_WRITE)) {
44 case INV_READ|INV_WRITE:
45 return "rw";
46 case INV_READ:
47 return "r";
48 case INV_WRITE:
49 return "w";
50 default:
51 return "-";
52 }
53 }
54
55 int php_pq_compare_index(const void *lptr, const void *rptr)
56 {
57 zend_ulong l = ((const Bucket *) lptr)->h;
58 zend_ulong r = ((const Bucket *) rptr)->h;
59
60 if (l < r) {
61 return -1;
62 }
63 if (l > r) {
64 return 1;
65 }
66 return 0;
67 }
68
69 void php_pq_hash_ptr_dtor(zval *p)
70 {
71 efree(Z_PTR_P(p));
72 }
73
74 zend_class_entry *php_pqdt_class_entry;
75
76 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_to_string, 0, 0, 0)
77 ZEND_END_ARG_INFO();
78 static PHP_METHOD(pqdt, __toString)
79 {
80 zval rv, tmp;
81
82 ZVAL_NULL(&rv);
83 zend_call_method_with_1_params(getThis(), php_pqdt_class_entry, NULL, "format", &rv,
84 zend_read_property(php_pqdt_class_entry, getThis(), ZEND_STRL("format"), 0, &tmp));
85 RETVAL_ZVAL(&rv, 1, 1);
86 }
87
88 ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_create_from_format, 0, 0, 2)
89 ZEND_ARG_INFO(0, format)
90 ZEND_ARG_INFO(0, datetime)
91 ZEND_ARG_INFO(0, timezone)
92 ZEND_END_ARG_INFO();
93 static PHP_METHOD(pqdt, createFromFormat)
94 {
95 zend_error_handling zeh;
96 char *fmt_str, *dt_str;
97 size_t fmt_len, dt_len;
98 zval *ztz = NULL;
99 ZEND_RESULT_CODE rv;
100
101 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh);
102 rv = zend_parse_parameters(ZEND_NUM_ARGS(), "ss|O", &fmt_str, &fmt_len, &dt_str, &dt_len, &ztz, php_date_get_timezone_ce());
103 zend_restore_error_handling(&zeh);
104
105 if (SUCCESS == rv) {
106 php_pqdt_from_string(return_value, fmt_str, dt_str, dt_len, "Y-m-d H:i:s.uO", ztz);
107 }
108 }
109
110 static zend_function_entry php_pqdt_methods[] = {
111 PHP_ME(pqdt, createFromFormat, ai_pqdt_create_from_format, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
112 PHP_ME(pqdt, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC)
113 PHP_MALIAS(pqdt, jsonSerialize, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC)
114 {0}
115 };
116
117 zval *php_pqdt_from_string(zval *zv, char *input_fmt, char *dt_str, size_t dt_len, char *output_fmt, zval *ztimezone)
118 {
119 php_date_obj *dobj;
120
121 php_date_instantiate(php_pqdt_class_entry, zv);
122 dobj = php_date_obj_from_obj(Z_OBJ_P(zv));
123 if (!php_date_initialize(dobj, dt_str, dt_len, input_fmt, ztimezone, 1)) {
124 zval_dtor(zv);
125 ZVAL_NULL(zv);
126 } else if (output_fmt) {
127 zend_update_property_string(php_pqdt_class_entry, zv, ZEND_STRL("format"), output_fmt);
128 }
129
130 return zv;
131 }
132
133 zend_string *php_pqdt_to_string(zval *zdt, const char *format)
134 {
135 zval rv;
136
137 ZVAL_NULL(&rv);
138
139 if (Z_OBJ_HT_P(zdt)->cast_object
140 && SUCCESS == Z_OBJ_HT_P(zdt)->cast_object(zdt, &rv, IS_STRING)
141 ) {
142 return Z_STR(rv);
143 } else if (instanceof_function(Z_OBJCE_P(zdt), php_date_get_date_ce())) {
144 zval rv, zfmt;
145
146 ZVAL_NULL(&rv);
147 ZVAL_STRING(&zfmt, format);
148 zend_call_method_with_1_params(zdt, Z_OBJCE_P(zdt), NULL, "format", &rv, &zfmt);
149 zval_ptr_dtor(&zfmt);
150
151 if (Z_TYPE(rv) == IS_STRING) {
152 return Z_STR(rv);
153 }
154 zval_ptr_dtor(&rv);
155 }
156
157 return NULL;
158 }
159
160 zend_class_entry *php_pqconv_class_entry;
161
162 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_types, 0, 0, 0)
163 ZEND_END_ARG_INFO();
164
165 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_from_string, 0, 0, 2)
166 ZEND_ARG_INFO(0, data)
167 ZEND_ARG_INFO(0, type)
168 ZEND_END_ARG_INFO();
169
170 ZEND_BEGIN_ARG_INFO_EX(ai_pqconv_convert_to_string, 0, 0, 2)
171 ZEND_ARG_INFO(0, data)
172 ZEND_ARG_INFO(0, type)
173 ZEND_END_ARG_INFO();
174
175 zend_function_entry php_pqconv_methods[] = {
176 PHP_ABSTRACT_ME(pqconv, convertTypes, ai_pqconv_convert_types)
177 PHP_ABSTRACT_ME(pqconv, convertFromString, ai_pqconv_convert_from_string)
178 PHP_ABSTRACT_ME(pqconv, convertToString, ai_pqconv_convert_to_string)
179 {0}
180 };
181
182
183 PHP_MINIT_FUNCTION(pq_misc)
184 {
185 zend_class_entry *json, ce = {0};
186
187 INIT_NS_CLASS_ENTRY(ce, "pq", "Converter", php_pqconv_methods);
188 php_pqconv_class_entry = zend_register_internal_interface(&ce);
189
190 memset(&ce, 0, sizeof(ce));
191 INIT_NS_CLASS_ENTRY(ce ,"pq", "DateTime", php_pqdt_methods);
192 php_pqdt_class_entry = zend_register_internal_class_ex(&ce, php_date_get_date_ce());
193
194 zend_declare_property_stringl(php_pqdt_class_entry, ZEND_STRL("format"), ZEND_STRL("Y-m-d H:i:s.uO"), ZEND_ACC_PUBLIC);
195
196 /* stop reading this file right here! */
197 if ((json = zend_hash_str_find_ptr(CG(class_table), ZEND_STRL("jsonserializable")))) {
198 zend_class_implements(php_pqdt_class_entry, 1, json);
199 }
200
201 return SUCCESS;
202 }
203
204 typedef struct _HashTableList {
205 zval arr;
206 struct _HashTableList *parent;
207 } HashTableList;
208
209 typedef struct _ArrayParserState {
210 const char *ptr, *end;
211 HashTableList *list;
212 php_pqres_t *res;
213 Oid typ;
214 unsigned quotes:1;
215 unsigned escaped:1;
216 } ArrayParserState;
217
218 static char caa(ArrayParserState *a, const char *any, unsigned advance)
219 {
220 const char *p = any;
221
222 do {
223 if (*p == *a->ptr) {
224 a->ptr += advance;
225 return *p;
226 }
227 } while (*++p);
228
229 php_error_docref(NULL, E_WARNING, "Failed to parse array: expected one of '%s', got '%c'", any, *a->ptr); \
230 return 0;
231 }
232
233 static ZEND_RESULT_CODE add_element(ArrayParserState *a, const char *start)
234 {
235 zval zelem;
236 zend_string *zstr = zend_string_init(start, a->ptr - start, 0);
237
238 if (a->quotes) {
239 php_stripslashes(zstr);
240 ZVAL_STR(&zelem, zstr);
241 } else if (!zend_string_equals_literal(zstr, "NULL")) {
242 ZVAL_STR(&zelem, zstr);
243 } else {
244 zend_string_release(zstr);
245 ZVAL_NULL(&zelem);
246 }
247
248 if (!ZVAL_IS_NULL(&zelem)) {
249 php_pqres_typed_zval(a->res, a->typ, &zelem);
250 }
251
252 add_next_index_zval(&a->list->arr, &zelem);
253 return SUCCESS;
254 }
255
256 static ZEND_RESULT_CODE parse_array(ArrayParserState *a);
257
258 static ZEND_RESULT_CODE parse_element(ArrayParserState *a, char delim)
259 {
260 const char *el;
261
262 switch (*a->ptr) {
263 case '{':
264 return parse_array(a);
265
266 case '"':
267 a->quotes = 1;
268 ++a->ptr;
269 break;
270 }
271
272 for (el = a->ptr; a->ptr < a->end; ++a->ptr) {
273 switch (*a->ptr) {
274 case '\\':
275 a->escaped = !a->escaped;
276 break;
277
278 case '"':
279 if (a->escaped) {
280 a->escaped = 0;
281 } else if (a->quotes) {
282 if (SUCCESS != add_element(a, el)) {
283 return FAILURE;
284 }
285 a->quotes = 0;
286 ++a->ptr;
287 return SUCCESS;
288 } else {
289 php_error_docref(NULL, E_WARNING, "Failed to parse element, unexpected quote: '%.*s'", (int) (a->ptr - el), el);
290 return FAILURE;
291 }
292 break;
293
294 default:
295 if (delim != *a->ptr) {
296 a->escaped = 0;
297 break;
298 }
299 /* no break */
300 case '}':
301 if (!a->quotes) {
302 return add_element(a, el);
303 }
304 break;
305
306 }
307 }
308
309 php_error_docref(NULL, E_WARNING, "Failed to parse element, reached end of input");
310 return FAILURE;
311 }
312
313 static ZEND_RESULT_CODE parse_elements(ArrayParserState *a)
314 {
315 char delims[] = {'}', PHP_PQ_DELIM_OF_ARRAY(a->typ), 0};
316
317 while (SUCCESS == parse_element(a, delims[1])) {
318 switch (caa(a, delims, 0)) {
319 case 0:
320 return FAILURE;
321
322 case '}':
323 return SUCCESS;
324
325 default:
326 if (!*++a->ptr) {
327 php_error_docref(NULL, E_WARNING, "Failed to parse elements, reached end of input");
328 return FAILURE;
329 }
330 break;
331 }
332 }
333
334 return FAILURE;
335 }
336
337 static ZEND_RESULT_CODE parse_array(ArrayParserState *a)
338 {
339 HashTableList *list;
340
341 if (!caa(a, "{", 1)) {
342 return FAILURE;
343 }
344
345 list = ecalloc(1, sizeof(*list));
346 array_init(&list->arr);
347
348 if (a->list) {
349 add_next_index_zval(&a->list->arr, &list->arr);
350 list->parent = a->list;
351 }
352 a->list = list;
353
354 if (SUCCESS != parse_elements(a)) {
355 return FAILURE;
356 }
357
358 if (!caa(a, "}", 1)) {
359 return FAILURE;
360 }
361
362 /* step one level back up */
363 if (a->list->parent) {
364 HashTableList *l = a->list->parent;
365
366 efree(a->list);
367 a->list = l;
368 }
369
370 return SUCCESS;
371 }
372
373 HashTable *php_pq_parse_array(php_pqres_t *res, const char *val_str, size_t val_len, Oid typ TSRMLS_DC)
374 {
375 HashTable *ht = NULL;
376 ArrayParserState a = {0};
377
378 a.typ = typ;
379 a.ptr = val_str;
380 a.end = val_str + val_len;
381 a.res = res;
382
383 if (SUCCESS != parse_array(&a)) {
384 while (a.list) {
385 HashTableList *l = a.list->parent;
386
387 zval_dtor(&a.list->arr);
388 efree(a.list);
389 a.list = l;
390 }
391 return ht;
392 }
393
394 if (*a.ptr) {
395 php_error_docref(NULL, E_NOTICE, "Trailing input: '%s'", a.ptr);
396 }
397
398 while (a.list) {
399 HashTableList *l = a.list->parent;
400
401 ht = Z_ARRVAL(a.list->arr);
402 efree(a.list);
403 a.list = l;
404 }
405
406 return ht;
407 }
408
409
410 /*
411 * Local variables:
412 * tab-width: 4
413 * c-basic-offset: 4
414 * End:
415 * vim600: noet sw=4 ts=4 fdm=marker
416 * vim<600: noet sw=4 ts=4
417 */