X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-pq;a=blobdiff_plain;f=src%2Fphp_pq_misc.c;h=e8de5d259375d5efe70197417d02eccfa369f945;hp=943aa57ffce27eaee0791adf0cc89b4b8196d28e;hb=cd5d3cdafda1f9e9fe120c9eaf1269fdc577f04d;hpb=fad4fe6a60c56ad5d5752e10abd1085884cc09c2 diff --git a/src/php_pq_misc.c b/src/php_pq_misc.c index 943aa57..e8de5d2 100644 --- a/src/php_pq_misc.c +++ b/src/php_pq_misc.c @@ -16,6 +16,13 @@ #include #include +#include +#if defined(HAVE_JSON) && !defined(COMPILE_DL_JSON) +# include +#endif + +#include + #include #include "php_pq.h" @@ -174,7 +181,26 @@ Oid *php_pq_ntypes_to_array(zend_bool fill, int argc, ...) } */ -zval *php_pq_date_from_string(char *datetime_str, size_t datetime_len, zval *zv TSRMLS_DC) +zend_class_entry *php_pqdt_class_entry; + +ZEND_BEGIN_ARG_INFO_EX(ai_pqdt_to_string, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(pqdt, __toString) +{ + zval *rv; + + zend_call_method_with_1_params(&getThis(), php_pqdt_class_entry, NULL, "format", &rv, + zend_read_property(php_pqdt_class_entry, getThis(), ZEND_STRL("format"), 0 TSRMLS_CC)); + RETVAL_ZVAL(rv, 1, 1); +} + +static zend_function_entry php_pqdt_methods[] = { + PHP_ME(pqdt, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC) + PHP_MALIAS(pqdt, jsonSerialize, __toString, ai_pqdt_to_string, ZEND_ACC_PUBLIC) + {0} +}; + +zval *php_pqdt_from_string(char *dt_str, size_t dt_len, char *fmt, zval *zv TSRMLS_DC) { php_date_obj *dobj; @@ -182,16 +208,313 @@ zval *php_pq_date_from_string(char *datetime_str, size_t datetime_len, zval *zv MAKE_STD_ZVAL(zv); } - php_date_instantiate(php_date_get_date_ce(), zv TSRMLS_CC); + php_date_instantiate(php_pqdt_class_entry, zv TSRMLS_CC); dobj = zend_object_store_get_object(zv TSRMLS_CC); - if (!php_date_initialize(dobj, datetime_str, datetime_len, NULL, NULL, 1 TSRMLS_CC)) { + if (!php_date_initialize(dobj, dt_str, dt_len, NULL, NULL, 1 TSRMLS_CC)) { zval_dtor(zv); ZVAL_NULL(zv); + } else if (fmt) { + zend_update_property_string(php_pqdt_class_entry, zv, ZEND_STRL("format"), fmt TSRMLS_CC); } return zv; } +PHP_MINIT_FUNCTION(pq_misc) +{ + zend_class_entry **json, ce = {0}; + + INIT_NS_CLASS_ENTRY(ce ,"pq", "DateTime", php_pqdt_methods); + php_pqdt_class_entry = zend_register_internal_class_ex(&ce, php_date_get_date_ce(), "DateTime" TSRMLS_CC); + + zend_declare_property_stringl(php_pqdt_class_entry, ZEND_STRL("format"), ZEND_STRL("Y-m-d H:i:s.u"), ZEND_ACC_PUBLIC TSRMLS_CC); + + /* stop reading this file right here! */ + if (SUCCESS == zend_hash_find(CG(class_table), ZEND_STRS("jsonserializable"), (void *) &json)) { + zend_class_implements(php_pqdt_class_entry TSRMLS_CC, 1, *json); + } + + return SUCCESS; +} + +typedef struct _HashTableList { + HashTable ht; + struct _HashTableList *parent; +} HashTableList; + +typedef struct _ArrayParserState { + const char *ptr, *end; + HashTableList *list; +#ifdef ZTS + void ***ts; +#endif + Oid typ; + unsigned quotes:1; + unsigned escaped:1; +} ArrayParserState; + +static char caa(ArrayParserState *a, const char *any, unsigned advance) +{ + const char *p = any; + TSRMLS_FETCH_FROM_CTX(a->ts); + + do { + if (*p == *a->ptr) { + a->ptr += advance; + return *p; + } + } while (*++p); + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse array: expected one of '%s', got '%c'", any, *a->ptr); \ + return 0; +} + +static STATUS add_element(ArrayParserState *a, const char *start) +{ + zval *zelem; + size_t el_len = a->ptr - start; + char *el_str = estrndup(start, el_len); + TSRMLS_FETCH_FROM_CTX(a->ts); + + if (a->quotes) { + int tmp_len; + + php_stripslashes(el_str, &tmp_len TSRMLS_CC); + el_len = tmp_len; + } else if ((a->ptr - start == 4) && !strncmp(start, "NULL", 4)) { + efree(el_str); + el_str = NULL; + el_len = 0; + } + + if (!el_str) { + MAKE_STD_ZVAL(zelem); + ZVAL_NULL(zelem); + } else { + zelem = php_pq_typed_zval(el_str, el_len, a->typ TSRMLS_CC); + + efree(el_str); + } + + return zend_hash_next_index_insert(&a->list->ht, &zelem, sizeof(zval *), NULL); +} + +static STATUS parse_array(ArrayParserState *a); + +static STATUS parse_element(ArrayParserState *a) +{ + const char *el; + TSRMLS_FETCH_FROM_CTX(a->ts); + + switch (*a->ptr) { + case '{': + return parse_array(a); + + case '"': + a->quotes = 1; + ++a->ptr; + break; + } + + for (el = a->ptr; a->ptr < a->end; ++a->ptr) { + switch (*a->ptr) { + case '"': + if (a->escaped) { + a->escaped = 0; + } else if (a->quotes) { + if (SUCCESS != add_element(a, el)) { + return FAILURE; + } + a->quotes = 0; + ++a->ptr; + return SUCCESS; + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse element, unexpected quote: '%.*s'", (int) (a->ptr - el), el); + return FAILURE; + } + break; + + case ',': + case '}': + if (!a->quotes) { + return add_element(a, el); + } + break; + + case '\\': + a->escaped = !a->escaped; + break; + + default: + a->escaped = 0; + break; + } + } + + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse element, reached end of input"); + return FAILURE; +} + +static STATUS parse_elements(ArrayParserState *a) +{ + TSRMLS_FETCH_FROM_CTX(a->ts); + + while (SUCCESS == parse_element(a)) { + switch (caa(a, ",}", 0)) { + case 0: + return FAILURE; + + case '}': + return SUCCESS; + + default: + if (!*++a->ptr) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse elements, reached end of input"); + return FAILURE; + } + break; + } + } + + return FAILURE; +} + +static STATUS parse_array(ArrayParserState *a) +{ + HashTableList *list; + + if (!caa(a, "{", 1)) { + return FAILURE; + } + + list = ecalloc(1, sizeof(*list)); + ZEND_INIT_SYMTABLE(&list->ht); + + if (a->list) { + zval *zcur; + + MAKE_STD_ZVAL(zcur); + Z_TYPE_P(zcur) = IS_ARRAY; + Z_ARRVAL_P(zcur) = &list->ht; + + zend_hash_next_index_insert(&a->list->ht, &zcur, sizeof(zval *), NULL); + + list->parent = a->list; + } + a->list = list; + + if (SUCCESS != parse_elements(a)) { + return FAILURE; + } + + if (!caa(a, "}", 1)) { + return FAILURE; + } + + if (a->list->parent) { + a->list = a->list->parent; + } + + return SUCCESS; +} + +HashTable *php_pq_parse_array(const char *val_str, size_t val_len, Oid typ TSRMLS_DC) +{ + HashTable *ht = NULL; + ArrayParserState a = {0}; + TSRMLS_SET_CTX(a.ts); + + a.typ = typ; + a.ptr = val_str; + a.end = val_str + val_len; + + if (SUCCESS != parse_array(&a)) { + while (a.list) { + HashTableList *l = a.list->parent; + + zend_hash_destroy(&a.list->ht); + efree(a.list); + a.list = l; + } + return ht; + } + + if (*a.ptr) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Trailing input: '%s'", a.ptr); + } + + do { + ht = &a.list->ht; + } while ((a.list = a.list->parent)); + + return ht; +} + +zval *php_pq_typed_zval(char *val, size_t len, Oid typ TSRMLS_DC) +{ + zval *zv; + + MAKE_STD_ZVAL(zv); + + switch (typ) { +#ifdef HAVE_PHP_PQ_TYPE_H +# undef PHP_PQ_TYPE +# include "php_pq_type.h" + case PHP_PQ_OID_BOOL: + ZVAL_BOOL(zv, *val == 't'); + break; +#if SIZEOF_LONG >= 8 + case PHP_PQ_OID_INT8: + case PHP_PQ_OID_TID: +#endif + case PHP_PQ_OID_INT4: + case PHP_PQ_OID_INT2: + case PHP_PQ_OID_XID: + case PHP_PQ_OID_OID: + ZVAL_LONG(zv, zend_atol(val, len)); + break; + + case PHP_PQ_OID_FLOAT4: + case PHP_PQ_OID_FLOAT8: + ZVAL_DOUBLE(zv, zend_strtod(val, NULL)); + break; + + case PHP_PQ_OID_DATE: + php_pqdt_from_string(val, len, "Y-m-d", zv TSRMLS_CC); + break; + + case PHP_PQ_OID_ABSTIME: + php_pqdt_from_string(val, len, "Y-m-d H:i:s", zv TSRMLS_CC); + break; + + case PHP_PQ_OID_TIMESTAMP: + php_pqdt_from_string(val, len, "Y-m-d H:i:s.u", zv TSRMLS_CC); + break; + + case PHP_PQ_OID_TIMESTAMPTZ: + php_pqdt_from_string(val, len, "Y-m-d H:i:s.uO", zv TSRMLS_CC); + break; + + default: + if (PHP_PQ_TYPE_IS_ARRAY(typ) && (Z_ARRVAL_P(zv) = php_pq_parse_array(val, len, PHP_PQ_TYPE_OF_ARRAY(typ) TSRMLS_CC))) { + Z_TYPE_P(zv) = IS_ARRAY; + } else { + ZVAL_STRINGL(zv, val, len, 1); + } + break; + } +#else + case 16: /* BOOL */ + ZVAL_BOOL(zv, *val == 't'); + break; + + default: + ZVAL_STRINGL(zv, val, len, 1); +#endif + + return zv; +} + /* * Local variables: * tab-width: 4