-
-
Save trowski/397f0114538cb3c0a4afb288f6f0802b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* dlep_po extension for PHP */ | |
#ifdef HAVE_CONFIG_H | |
# include "config.h" | |
#endif | |
#include "php.h" | |
#include "ext/standard/info.h" | |
#include "php_dlep_po.h" | |
#include "dlep_po_arginfo.h" | |
#include "zend_smart_str.h" | |
#include "zend_exceptions.h" | |
#include "zend_enum.h" | |
#include "../json/php_json.h" | |
#include "../standard/html.h" | |
zend_class_entry *preserialized_ce; | |
typedef struct _dlep_json_encoder dlep_json_encoder; | |
struct _dlep_json_encoder { | |
int depth; | |
int max_depth; | |
php_json_error_code error_code; | |
}; | |
int dlep_json_encode_zval(smart_str *buf, zval *val, int options, dlep_json_encoder *encoder); | |
static inline void dlep_json_encode_init(dlep_json_encoder *encoder) | |
{ | |
memset(encoder, 0, sizeof(dlep_json_encoder)); | |
} | |
PHP_JSON_API int dlep_json_encode_ex(smart_str *buf, zval *val, int options, zend_long depth) /* {{{ */ | |
{ | |
dlep_json_encoder encoder; | |
int return_code; | |
dlep_json_encode_init(&encoder); | |
encoder.max_depth = depth; | |
return_code = dlep_json_encode_zval(buf, val, options, &encoder); | |
return return_code; | |
} | |
static const char digits[] = "0123456789abcdef"; | |
static int dlep_json_escape_string( | |
smart_str *buf, const char *s, size_t len, | |
int options, dlep_json_encoder *encoder); | |
static int dlep_json_determine_array_type(zval *val) /* {{{ */ | |
{ | |
zend_array *myht = Z_ARRVAL_P(val); | |
if (myht) { | |
return zend_array_is_list(myht) ? PHP_JSON_OUTPUT_ARRAY : PHP_JSON_OUTPUT_OBJECT; | |
} | |
return PHP_JSON_OUTPUT_ARRAY; | |
} | |
/* }}} */ | |
/* {{{ Pretty printing support functions */ | |
static inline void dlep_json_pretty_print_char(smart_str *buf, int options, char c) /* {{{ */ | |
{ | |
if (options & PHP_JSON_PRETTY_PRINT) { | |
smart_str_appendc(buf, c); | |
} | |
} | |
/* }}} */ | |
static inline void dlep_json_pretty_print_indent(smart_str *buf, int options, dlep_json_encoder *encoder) /* {{{ */ | |
{ | |
int i; | |
if (options & PHP_JSON_PRETTY_PRINT) { | |
for (i = 0; i < encoder->depth; ++i) { | |
smart_str_appendl(buf, " ", 4); | |
} | |
} | |
} | |
/* }}} */ | |
/* }}} */ | |
static inline int dlep_json_is_valid_double(double d) /* {{{ */ | |
{ | |
return !zend_isinf(d) && !zend_isnan(d); | |
} | |
/* }}} */ | |
static inline void dlep_json_encode_double(smart_str *buf, double d, int options) /* {{{ */ | |
{ | |
size_t len; | |
char num[ZEND_DOUBLE_MAX_LENGTH]; | |
zend_gcvt(d, (int)PG(serialize_precision), '.', 'e', num); | |
len = strlen(num); | |
if (options & PHP_JSON_PRESERVE_ZERO_FRACTION && strchr(num, '.') == NULL && len < ZEND_DOUBLE_MAX_LENGTH - 2) { | |
num[len++] = '.'; | |
num[len++] = '0'; | |
num[len] = '\0'; | |
} | |
smart_str_appendl(buf, num, len); | |
} | |
/* }}} */ | |
#define PHP_JSON_HASH_PROTECT_RECURSION(_tmp_ht) \ | |
do { \ | |
if (_tmp_ht) { \ | |
GC_TRY_PROTECT_RECURSION(_tmp_ht); \ | |
} \ | |
} while (0) | |
#define PHP_JSON_HASH_UNPROTECT_RECURSION(_tmp_ht) \ | |
do { \ | |
if (_tmp_ht) { \ | |
GC_TRY_UNPROTECT_RECURSION(_tmp_ht); \ | |
} \ | |
} while (0) | |
static int dlep_json_encode_array(smart_str *buf, zval *val, int options, dlep_json_encoder *encoder) /* {{{ */ | |
{ | |
int i, r, need_comma = 0; | |
HashTable *myht, *prop_ht; | |
if (Z_TYPE_P(val) == IS_ARRAY) { | |
myht = Z_ARRVAL_P(val); | |
prop_ht = NULL; | |
r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : dlep_json_determine_array_type(val); | |
} else if (Z_OBJ_P(val)->properties == NULL | |
&& Z_OBJ_HT_P(val)->get_properties_for == NULL | |
&& Z_OBJ_HT_P(val)->get_properties == zend_std_get_properties) { | |
/* Optimized version without rebuilding properties HashTable */ | |
zend_object *obj = Z_OBJ_P(val); | |
zend_class_entry *ce = obj->ce; | |
zend_property_info *prop_info; | |
zval *prop; | |
int i; | |
if (GC_IS_RECURSIVE(obj)) { | |
encoder->error_code = PHP_JSON_ERROR_RECURSION; | |
smart_str_appendl(buf, "null", 4); | |
return FAILURE; | |
} | |
PHP_JSON_HASH_PROTECT_RECURSION(obj); | |
smart_str_appendc(buf, '{'); | |
++encoder->depth; | |
for (i = 0; i < ce->default_properties_count; i++) { | |
prop_info = ce->properties_info_table[i]; | |
if (!prop_info) { | |
continue; | |
} | |
if (ZSTR_VAL(prop_info->name)[0] == '\0' && ZSTR_LEN(prop_info->name) > 0) { | |
/* Skip protected and private members. */ | |
continue; | |
} | |
prop = OBJ_PROP(obj, prop_info->offset); | |
if (Z_TYPE_P(prop) == IS_UNDEF) { | |
continue; | |
} | |
if (need_comma) { | |
smart_str_appendc(buf, ','); | |
} else { | |
need_comma = 1; | |
} | |
dlep_json_pretty_print_char(buf, options, '\n'); | |
dlep_json_pretty_print_indent(buf, options, encoder); | |
if (dlep_json_escape_string(buf, ZSTR_VAL(prop_info->name), ZSTR_LEN(prop_info->name), | |
options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE && | |
(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && | |
buf->s) { | |
ZSTR_LEN(buf->s) -= 4; | |
smart_str_appendl(buf, "\"\"", 2); | |
} | |
smart_str_appendc(buf, ':'); | |
dlep_json_pretty_print_char(buf, options, ' '); | |
if (dlep_json_encode_zval(buf, prop, options, encoder) == FAILURE && | |
!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { | |
PHP_JSON_HASH_UNPROTECT_RECURSION(obj); | |
return FAILURE; | |
} | |
} | |
PHP_JSON_HASH_UNPROTECT_RECURSION(obj); | |
if (encoder->depth > encoder->max_depth) { | |
encoder->error_code = PHP_JSON_ERROR_DEPTH; | |
if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { | |
return FAILURE; | |
} | |
} | |
--encoder->depth; | |
if (need_comma) { | |
dlep_json_pretty_print_char(buf, options, '\n'); | |
dlep_json_pretty_print_indent(buf, options, encoder); | |
} | |
smart_str_appendc(buf, '}'); | |
return SUCCESS; | |
} else { | |
prop_ht = myht = zend_get_properties_for(val, ZEND_PROP_PURPOSE_JSON); | |
r = PHP_JSON_OUTPUT_OBJECT; | |
} | |
if (myht && GC_IS_RECURSIVE(myht)) { | |
encoder->error_code = PHP_JSON_ERROR_RECURSION; | |
smart_str_appendl(buf, "null", 4); | |
zend_release_properties(prop_ht); | |
return FAILURE; | |
} | |
PHP_JSON_HASH_PROTECT_RECURSION(myht); | |
if (r == PHP_JSON_OUTPUT_ARRAY) { | |
smart_str_appendc(buf, '['); | |
} else { | |
smart_str_appendc(buf, '{'); | |
} | |
++encoder->depth; | |
i = myht ? zend_hash_num_elements(myht) : 0; | |
if (i > 0) { | |
zend_string *key; | |
zval *data; | |
zend_ulong index; | |
ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) { | |
if (r == PHP_JSON_OUTPUT_ARRAY) { | |
if (need_comma) { | |
smart_str_appendc(buf, ','); | |
} else { | |
need_comma = 1; | |
} | |
dlep_json_pretty_print_char(buf, options, '\n'); | |
dlep_json_pretty_print_indent(buf, options, encoder); | |
} else if (r == PHP_JSON_OUTPUT_OBJECT) { | |
if (key) { | |
if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) { | |
/* Skip protected and private members. */ | |
continue; | |
} | |
if (need_comma) { | |
smart_str_appendc(buf, ','); | |
} else { | |
need_comma = 1; | |
} | |
dlep_json_pretty_print_char(buf, options, '\n'); | |
dlep_json_pretty_print_indent(buf, options, encoder); | |
if (dlep_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), | |
options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE && | |
(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && | |
buf->s) { | |
ZSTR_LEN(buf->s) -= 4; | |
smart_str_appendl(buf, "\"\"", 2); | |
} | |
} else { | |
if (need_comma) { | |
smart_str_appendc(buf, ','); | |
} else { | |
need_comma = 1; | |
} | |
dlep_json_pretty_print_char(buf, options, '\n'); | |
dlep_json_pretty_print_indent(buf, options, encoder); | |
smart_str_appendc(buf, '"'); | |
smart_str_append_long(buf, (zend_long) index); | |
smart_str_appendc(buf, '"'); | |
} | |
smart_str_appendc(buf, ':'); | |
dlep_json_pretty_print_char(buf, options, ' '); | |
} | |
if (dlep_json_encode_zval(buf, data, options, encoder) == FAILURE && | |
!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
zend_release_properties(prop_ht); | |
return FAILURE; | |
} | |
} ZEND_HASH_FOREACH_END(); | |
} | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
if (encoder->depth > encoder->max_depth) { | |
encoder->error_code = PHP_JSON_ERROR_DEPTH; | |
if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { | |
zend_release_properties(prop_ht); | |
return FAILURE; | |
} | |
} | |
--encoder->depth; | |
/* Only keep closing bracket on same line for empty arrays/objects */ | |
if (need_comma) { | |
dlep_json_pretty_print_char(buf, options, '\n'); | |
dlep_json_pretty_print_indent(buf, options, encoder); | |
} | |
if (r == PHP_JSON_OUTPUT_ARRAY) { | |
smart_str_appendc(buf, ']'); | |
} else { | |
smart_str_appendc(buf, '}'); | |
} | |
zend_release_properties(prop_ht); | |
return SUCCESS; | |
} | |
/* }}} */ | |
static int dlep_json_escape_string( | |
smart_str *buf, const char *s, size_t len, | |
int options, dlep_json_encoder *encoder) /* {{{ */ | |
{ | |
int status; | |
unsigned int us; | |
size_t pos, checkpoint; | |
char *dst; | |
if (len == 0) { | |
smart_str_appendl(buf, "\"\"", 2); | |
return SUCCESS; | |
} | |
if (options & PHP_JSON_NUMERIC_CHECK) { | |
double d; | |
int type; | |
zend_long p; | |
if ((type = is_numeric_string(s, len, &p, &d, 0)) != 0) { | |
if (type == IS_LONG) { | |
smart_str_append_long(buf, p); | |
return SUCCESS; | |
} else if (type == IS_DOUBLE && dlep_json_is_valid_double(d)) { | |
dlep_json_encode_double(buf, d, options); | |
return SUCCESS; | |
} | |
} | |
} | |
checkpoint = buf->s ? ZSTR_LEN(buf->s) : 0; | |
/* pre-allocate for string length plus 2 quotes */ | |
smart_str_alloc(buf, len+2, 0); | |
smart_str_appendc(buf, '"'); | |
pos = 0; | |
do { | |
static const uint32_t charmap[8] = { | |
0xffffffff, 0x500080c4, 0x10000000, 0x00000000, | |
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff}; | |
us = (unsigned char)s[pos]; | |
if (EXPECTED(!ZEND_BIT_TEST(charmap, us))) { | |
pos++; | |
len--; | |
if (len == 0) { | |
smart_str_appendl(buf, s, pos); | |
break; | |
} | |
} else { | |
if (pos) { | |
smart_str_appendl(buf, s, pos); | |
s += pos; | |
pos = 0; | |
} | |
us = (unsigned char)s[0]; | |
if (UNEXPECTED(us >= 0x80)) { | |
us = php_next_utf8_char((unsigned char *)s, len, &pos, &status); | |
/* check whether UTF8 character is correct */ | |
if (UNEXPECTED(status != SUCCESS)) { | |
if (options & PHP_JSON_INVALID_UTF8_IGNORE) { | |
/* ignore invalid UTF8 character */ | |
} else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) { | |
/* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */ | |
if (options & PHP_JSON_UNESCAPED_UNICODE) { | |
smart_str_appendl(buf, "\xef\xbf\xbd", 3); | |
} else { | |
smart_str_appendl(buf, "\\ufffd", 6); | |
} | |
} else { | |
ZSTR_LEN(buf->s) = checkpoint; | |
encoder->error_code = PHP_JSON_ERROR_UTF8; | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
return FAILURE; | |
} | |
/* Escape U+2028/U+2029 line terminators, UNLESS both | |
JSON_UNESCAPED_UNICODE and | |
JSON_UNESCAPED_LINE_TERMINATORS were provided */ | |
} else if ((options & PHP_JSON_UNESCAPED_UNICODE) | |
&& ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS) | |
|| us < 0x2028 || us > 0x2029)) { | |
smart_str_appendl(buf, s, pos); | |
} else { | |
/* From http://en.wikipedia.org/wiki/UTF16 */ | |
if (us >= 0x10000) { | |
unsigned int next_us; | |
us -= 0x10000; | |
next_us = (unsigned short)((us & 0x3ff) | 0xdc00); | |
us = (unsigned short)((us >> 10) | 0xd800); | |
dst = smart_str_extend(buf, 6); | |
dst[0] = '\\'; | |
dst[1] = 'u'; | |
dst[2] = digits[(us >> 12) & 0xf]; | |
dst[3] = digits[(us >> 8) & 0xf]; | |
dst[4] = digits[(us >> 4) & 0xf]; | |
dst[5] = digits[us & 0xf]; | |
us = next_us; | |
} | |
dst = smart_str_extend(buf, 6); | |
dst[0] = '\\'; | |
dst[1] = 'u'; | |
dst[2] = digits[(us >> 12) & 0xf]; | |
dst[3] = digits[(us >> 8) & 0xf]; | |
dst[4] = digits[(us >> 4) & 0xf]; | |
dst[5] = digits[us & 0xf]; | |
} | |
s += pos; | |
len -= pos; | |
pos = 0; | |
} else { | |
s++; | |
switch (us) { | |
case '"': | |
if (options & PHP_JSON_HEX_QUOT) { | |
smart_str_appendl(buf, "\\u0022", 6); | |
} else { | |
smart_str_appendl(buf, "\\\"", 2); | |
} | |
break; | |
case '\\': | |
smart_str_appendl(buf, "\\\\", 2); | |
break; | |
case '/': | |
if (options & PHP_JSON_UNESCAPED_SLASHES) { | |
smart_str_appendc(buf, '/'); | |
} else { | |
smart_str_appendl(buf, "\\/", 2); | |
} | |
break; | |
case '\b': | |
smart_str_appendl(buf, "\\b", 2); | |
break; | |
case '\f': | |
smart_str_appendl(buf, "\\f", 2); | |
break; | |
case '\n': | |
smart_str_appendl(buf, "\\n", 2); | |
break; | |
case '\r': | |
smart_str_appendl(buf, "\\r", 2); | |
break; | |
case '\t': | |
smart_str_appendl(buf, "\\t", 2); | |
break; | |
case '<': | |
if (options & PHP_JSON_HEX_TAG) { | |
smart_str_appendl(buf, "\\u003C", 6); | |
} else { | |
smart_str_appendc(buf, '<'); | |
} | |
break; | |
case '>': | |
if (options & PHP_JSON_HEX_TAG) { | |
smart_str_appendl(buf, "\\u003E", 6); | |
} else { | |
smart_str_appendc(buf, '>'); | |
} | |
break; | |
case '&': | |
if (options & PHP_JSON_HEX_AMP) { | |
smart_str_appendl(buf, "\\u0026", 6); | |
} else { | |
smart_str_appendc(buf, '&'); | |
} | |
break; | |
case '\'': | |
if (options & PHP_JSON_HEX_APOS) { | |
smart_str_appendl(buf, "\\u0027", 6); | |
} else { | |
smart_str_appendc(buf, '\''); | |
} | |
break; | |
default: | |
ZEND_ASSERT(us < ' '); | |
dst = smart_str_extend(buf, 6); | |
dst[0] = '\\'; | |
dst[1] = 'u'; | |
dst[2] = '0'; | |
dst[3] = '0'; | |
dst[4] = digits[(us >> 4) & 0xf]; | |
dst[5] = digits[us & 0xf]; | |
break; | |
} | |
len--; | |
} | |
} | |
} while (len); | |
smart_str_appendc(buf, '"'); | |
return SUCCESS; | |
} | |
/* }}} */ | |
static int dlep_json_encode_serializable_object(smart_str *buf, zval *val, int options, dlep_json_encoder *encoder) /* {{{ */ | |
{ | |
zend_class_entry *ce = Z_OBJCE_P(val); | |
HashTable* myht = Z_OBJPROP_P(val); | |
zval retval, fname; | |
int return_code; | |
if (myht && GC_IS_RECURSIVE(myht)) { | |
encoder->error_code = PHP_JSON_ERROR_RECURSION; | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
return FAILURE; | |
} | |
PHP_JSON_HASH_PROTECT_RECURSION(myht); | |
ZVAL_STRING(&fname, "jsonSerialize"); | |
if (FAILURE == call_user_function(NULL, val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) { | |
if (!EG(exception)) { | |
zend_throw_exception_ex(NULL, 0, "Failed calling %s::jsonSerialize()", ZSTR_VAL(ce->name)); | |
} | |
zval_ptr_dtor(&fname); | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
return FAILURE; | |
} | |
if (EG(exception)) { | |
/* Error already raised */ | |
zval_ptr_dtor(&retval); | |
zval_ptr_dtor(&fname); | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
return FAILURE; | |
} | |
if ((Z_TYPE(retval) == IS_OBJECT) && | |
(Z_OBJ(retval) == Z_OBJ_P(val))) { | |
/* Handle the case where jsonSerialize does: return $this; by going straight to encode array */ | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
return_code = dlep_json_encode_array(buf, &retval, options, encoder); | |
} else { | |
/* All other types, encode as normal */ | |
return_code = dlep_json_encode_zval(buf, &retval, options, encoder); | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
} | |
zval_ptr_dtor(&retval); | |
zval_ptr_dtor(&fname); | |
return return_code; | |
} | |
/* }}} */ | |
static int dlep_json_encode_serializable_enum(smart_str *buf, zval *val, int options, dlep_json_encoder *encoder) | |
{ | |
zend_class_entry *ce = Z_OBJCE_P(val); | |
if (ce->enum_backing_type == IS_UNDEF) { | |
encoder->error_code = PHP_JSON_ERROR_NON_BACKED_ENUM; | |
smart_str_appendc(buf, '0'); | |
return FAILURE; | |
} | |
zval *value_zv = zend_enum_fetch_case_value(Z_OBJ_P(val)); | |
return dlep_json_encode_zval(buf, value_zv, options, encoder); | |
} | |
static int dlep_json_encode_preserializable_object(smart_str *buf, zval *val, int options, dlep_json_encoder *encoder) /* {{{ */ | |
{ | |
zend_class_entry *ce = Z_OBJCE_P(val); | |
HashTable* myht = Z_OBJPROP_P(val); | |
zval retval, fname; | |
zval *prop; | |
if (myht && GC_IS_RECURSIVE(myht)) { | |
encoder->error_code = PHP_JSON_ERROR_RECURSION; | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
return FAILURE; | |
} | |
PHP_JSON_HASH_PROTECT_RECURSION(myht); | |
ZVAL_STRING(&fname, "toPreserializedRawJson"); | |
if (FAILURE == call_user_function(NULL, val, &fname, &retval, 0, NULL) || Z_TYPE(retval) == IS_UNDEF) { | |
if (!EG(exception)) { | |
zend_throw_exception_ex(NULL, 0, "Failed calling %s::toPreserializedRawJson()", ZSTR_VAL(ce->name)); | |
} | |
zval_ptr_dtor(&fname); | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
return FAILURE; | |
} | |
if (EG(exception)) { | |
/* Error already raised */ | |
zval_ptr_dtor(&retval); | |
zval_ptr_dtor(&fname); | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
return FAILURE; | |
} | |
smart_str_appendl(buf, Z_STRVAL(retval), Z_STRLEN(retval)); | |
PHP_JSON_HASH_UNPROTECT_RECURSION(myht); | |
zval_ptr_dtor(&retval); | |
zval_ptr_dtor(&fname); | |
return SUCCESS; | |
} | |
int dlep_json_encode_zval(smart_str *buf, zval *val, int options, dlep_json_encoder *encoder) /* {{{ */ | |
{ | |
again: | |
switch (Z_TYPE_P(val)) | |
{ | |
case IS_NULL: | |
smart_str_appendl(buf, "null", 4); | |
break; | |
case IS_TRUE: | |
smart_str_appendl(buf, "true", 4); | |
break; | |
case IS_FALSE: | |
smart_str_appendl(buf, "false", 5); | |
break; | |
case IS_LONG: | |
smart_str_append_long(buf, Z_LVAL_P(val)); | |
break; | |
case IS_DOUBLE: | |
if (dlep_json_is_valid_double(Z_DVAL_P(val))) { | |
dlep_json_encode_double(buf, Z_DVAL_P(val), options); | |
} else { | |
encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN; | |
smart_str_appendc(buf, '0'); | |
} | |
break; | |
case IS_STRING: | |
return dlep_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder); | |
case IS_OBJECT: | |
if (instanceof_function(Z_OBJCE_P(val), preserialized_ce)) { | |
return dlep_json_encode_preserializable_object(buf, val, options, encoder); | |
} | |
if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) { | |
return dlep_json_encode_serializable_object(buf, val, options, encoder); | |
} | |
if (Z_OBJCE_P(val)->ce_flags & ZEND_ACC_ENUM) { | |
return dlep_json_encode_serializable_enum(buf, val, options, encoder); | |
} | |
/* fallthrough -- Non-serializable object */ | |
ZEND_FALLTHROUGH; | |
case IS_ARRAY: { | |
/* Avoid modifications (and potential freeing) of the array through a reference when a | |
* jsonSerialize() method is invoked. */ | |
zval zv; | |
int res; | |
ZVAL_COPY(&zv, val); | |
res = dlep_json_encode_array(buf, &zv, options, encoder); | |
zval_ptr_dtor_nogc(&zv); | |
return res; | |
} | |
case IS_REFERENCE: | |
val = Z_REFVAL_P(val); | |
goto again; | |
default: | |
encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE; | |
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { | |
smart_str_appendl(buf, "null", 4); | |
} | |
return FAILURE; | |
} | |
return SUCCESS; | |
} | |
/* }}} */ | |
static const char *convert_json_erro(php_json_error_code error_code) /* {{{ */ | |
{ | |
switch(error_code) { | |
case PHP_JSON_ERROR_NONE: | |
return "No error"; | |
case PHP_JSON_ERROR_DEPTH: | |
return "Maximum stack depth exceeded"; | |
case PHP_JSON_ERROR_STATE_MISMATCH: | |
return "State mismatch (invalid or malformed JSON)"; | |
case PHP_JSON_ERROR_CTRL_CHAR: | |
return "Control character error, possibly incorrectly encoded"; | |
case PHP_JSON_ERROR_SYNTAX: | |
return "Syntax error"; | |
case PHP_JSON_ERROR_UTF8: | |
return "Malformed UTF-8 characters, possibly incorrectly encoded"; | |
case PHP_JSON_ERROR_RECURSION: | |
return "Recursion detected"; | |
case PHP_JSON_ERROR_INF_OR_NAN: | |
return "Inf and NaN cannot be JSON encoded"; | |
case PHP_JSON_ERROR_UNSUPPORTED_TYPE: | |
return "Type is not supported"; | |
case PHP_JSON_ERROR_INVALID_PROPERTY_NAME: | |
return "The decoded property name is invalid"; | |
case PHP_JSON_ERROR_UTF16: | |
return "Single unpaired UTF-16 surrogate in unicode escape"; | |
case PHP_JSON_ERROR_NON_BACKED_ENUM: | |
return "Non-backed enums have no default serialization"; | |
default: | |
return "Unknown error"; | |
} | |
} | |
/* {{{ void test1() */ | |
PHP_FUNCTION(dlep_json_encode) | |
{ | |
zval *parameter; | |
smart_str buf = {0}; | |
zend_long options = PHP_JSON_THROW_ON_ERROR; | |
ZEND_PARSE_PARAMETERS_START(1, 1) | |
Z_PARAM_ZVAL(parameter) | |
ZEND_PARSE_PARAMETERS_END(); | |
dlep_json_encode_ex(&buf, parameter, (int)options, 64); | |
if (JSON_G(error_code) != PHP_JSON_ERROR_NONE) { | |
smart_str_free(&buf); | |
zend_throw_exception(zend_ce_exception, convert_json_erro(JSON_G(error_code)), JSON_G(error_code)); | |
RETURN_THROWS(); | |
} | |
smart_str_0(&buf); /* copy? */ | |
if (buf.s) { | |
RETURN_NEW_STR(buf.s); | |
} | |
RETURN_EMPTY_STRING(); | |
} | |
/* }}} */ | |
/* {{{ PHP_RINIT_FUNCTION */ | |
PHP_RINIT_FUNCTION(dlep_po) | |
{ | |
#if defined(ZTS) && defined(COMPILE_DL_DLEP_PO) | |
ZEND_TSRMLS_CACHE_UPDATE(); | |
#endif | |
return SUCCESS; | |
} | |
/* }}} */ | |
/* {{{ PHP_MINFO_FUNCTION */ | |
PHP_MINFO_FUNCTION(dlep_po) | |
{ | |
php_info_print_table_start(); | |
php_info_print_table_header(2, "dlep_po support", "enabled"); | |
php_info_print_table_end(); | |
} | |
/* }}} */ | |
static PHP_MINIT_FUNCTION(dlep_po) { | |
preserialized_ce = register_class_PreserializedRawJson(); | |
return SUCCESS; | |
} | |
/* {{{ dlep_po_module_entry */ | |
zend_module_entry dlep_po_module_entry = { | |
STANDARD_MODULE_HEADER, | |
"dlep_po", /* Extension name */ | |
ext_functions, /* zend_function_entry */ | |
PHP_MINIT(dlep_po), /* PHP_MINIT - Module initialization */ | |
NULL, /* PHP_MSHUTDOWN - Module shutdown */ | |
PHP_RINIT(dlep_po), /* PHP_RINIT - Request initialization */ | |
NULL, /* PHP_RSHUTDOWN - Request shutdown */ | |
PHP_MINFO(dlep_po), /* PHP_MINFO - Module info */ | |
PHP_DLEP_PO_VERSION, /* Version */ | |
STANDARD_MODULE_PROPERTIES | |
}; | |
/* }}} */ | |
#ifdef COMPILE_DL_DLEP_PO | |
# ifdef ZTS | |
ZEND_TSRMLS_CACHE_DEFINE() | |
# endif | |
ZEND_GET_MODULE(dlep_po) | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment