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