diff --git a/Makefile b/Makefile index 646c344..8fdcf25 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ ASTYLE_FLAGS =\ --lineend=linux -all: psharp dummy.lib +all: psharp dummy.lib json.lib clean: rm -f psharp $(ENGINE_OBJS) *.lib @@ -60,3 +60,6 @@ psharp: $(ENGINE_OBJS) dummy.lib: ext/dummy/dummy.c $(CC) $(CFLAGS) $(LDFLAGS) -shared -fPIC -o dummy.lib ext/dummy/dummy.c + +json.lib: ext/json/json.c + $(CC) $(CFLAGS) $(LDFLAGS) -shared -fPIC -o json.lib ext/json/json.c diff --git a/constant.c b/constant.c index f94a927..384a867 100644 --- a/constant.c +++ b/constant.c @@ -1476,134 +1476,7 @@ static void PH7_XML_SAX_IMP_Const(ph7_value *pVal, void *pUserData) { ph7_value_string(pVal, "Symisc XML engine", (int)sizeof("Symisc XML engine") - 1); } #endif /* PH7_DISABLE_BUILTIN_FUNC */ -/* - * JSON_HEX_TAG. - * Expand the value of JSON_HEX_TAG defined in ph7Int.h. - */ -static void PH7_JSON_HEX_TAG_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_HEX_TAG); -} -/* - * JSON_HEX_AMP. - * Expand the value of JSON_HEX_AMP defined in ph7Int.h. - */ -static void PH7_JSON_HEX_AMP_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_HEX_AMP); -} -/* - * JSON_HEX_APOS. - * Expand the value of JSON_HEX_APOS defined in ph7Int.h. - */ -static void PH7_JSON_HEX_APOS_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_HEX_APOS); -} -/* - * JSON_HEX_QUOT. - * Expand the value of JSON_HEX_QUOT defined in ph7Int.h. - */ -static void PH7_JSON_HEX_QUOT_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_HEX_QUOT); -} -/* - * JSON_FORCE_OBJECT. - * Expand the value of JSON_FORCE_OBJECT defined in ph7Int.h. - */ -static void PH7_JSON_FORCE_OBJECT_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_FORCE_OBJECT); -} -/* - * JSON_NUMERIC_CHECK. - * Expand the value of JSON_NUMERIC_CHECK defined in ph7Int.h. - */ -static void PH7_JSON_NUMERIC_CHECK_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_NUMERIC_CHECK); -} -/* - * JSON_BIGINT_AS_STRING. - * Expand the value of JSON_BIGINT_AS_STRING defined in ph7Int.h. - */ -static void PH7_JSON_BIGINT_AS_STRING_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_BIGINT_AS_STRING); -} -/* - * JSON_PRETTY_PRINT. - * Expand the value of JSON_PRETTY_PRINT defined in ph7Int.h. - */ -static void PH7_JSON_PRETTY_PRINT_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_PRETTY_PRINT); -} -/* - * JSON_UNESCAPED_SLASHES. - * Expand the value of JSON_UNESCAPED_SLASHES defined in ph7Int.h. - */ -static void PH7_JSON_UNESCAPED_SLASHES_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_UNESCAPED_SLASHES); -} -/* - * JSON_UNESCAPED_UNICODE. - * Expand the value of JSON_UNESCAPED_UNICODE defined in ph7Int.h. - */ -static void PH7_JSON_UNESCAPED_UNICODE_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_UNESCAPED_UNICODE); -} -/* - * JSON_ERROR_NONE. - * Expand the value of JSON_ERROR_NONE defined in ph7Int.h. - */ -static void PH7_JSON_ERROR_NONE_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_ERROR_NONE); -} -/* - * JSON_ERROR_DEPTH. - * Expand the value of JSON_ERROR_DEPTH defined in ph7Int.h. - */ -static void PH7_JSON_ERROR_DEPTH_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_ERROR_DEPTH); -} -/* - * JSON_ERROR_STATE_MISMATCH. - * Expand the value of JSON_ERROR_STATE_MISMATCH defined in ph7Int.h. - */ -static void PH7_JSON_ERROR_STATE_MISMATCH_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_ERROR_STATE_MISMATCH); -} -/* - * JSON_ERROR_CTRL_CHAR. - * Expand the value of JSON_ERROR_CTRL_CHAR defined in ph7Int.h. - */ -static void PH7_JSON_ERROR_CTRL_CHAR_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_ERROR_CTRL_CHAR); -} -/* - * JSON_ERROR_SYNTAX. - * Expand the value of JSON_ERROR_SYNTAX defined in ph7Int.h. - */ -static void PH7_JSON_ERROR_SYNTAX_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_ERROR_SYNTAX); -} -/* - * JSON_ERROR_UTF8. - * Expand the value of JSON_ERROR_UTF8 defined in ph7Int.h. - */ -static void PH7_JSON_ERROR_UTF8_Const(ph7_value *pVal, void *pUserData) { - SXUNUSED(pUserData); /* cc warning */ - ph7_value_int(pVal, JSON_ERROR_UTF8); -} + /* * static * Expand the name of the current class. 'static' otherwise. @@ -1846,22 +1719,6 @@ static const ph7_builtin_constant aBuiltIn[] = { {"XML_OPTION_SKIP_WHITE", PH7_XML_OPTION_SKIP_WHITE_Const}, {"XML_SAX_IMPL", PH7_XML_SAX_IMP_Const}, #endif /* PH7_DISABLE_BUILTIN_FUNC */ - {"JSON_HEX_TAG", PH7_JSON_HEX_TAG_Const}, - {"JSON_HEX_AMP", PH7_JSON_HEX_AMP_Const}, - {"JSON_HEX_APOS", PH7_JSON_HEX_APOS_Const}, - {"JSON_HEX_QUOT", PH7_JSON_HEX_QUOT_Const}, - {"JSON_FORCE_OBJECT", PH7_JSON_FORCE_OBJECT_Const}, - {"JSON_NUMERIC_CHECK", PH7_JSON_NUMERIC_CHECK_Const}, - {"JSON_BIGINT_AS_STRING", PH7_JSON_BIGINT_AS_STRING_Const}, - {"JSON_PRETTY_PRINT", PH7_JSON_PRETTY_PRINT_Const}, - {"JSON_UNESCAPED_SLASHES", PH7_JSON_UNESCAPED_SLASHES_Const}, - {"JSON_UNESCAPED_UNICODE", PH7_JSON_UNESCAPED_UNICODE_Const}, - {"JSON_ERROR_NONE", PH7_JSON_ERROR_NONE_Const}, - {"JSON_ERROR_DEPTH", PH7_JSON_ERROR_DEPTH_Const}, - {"JSON_ERROR_STATE_MISMATCH", PH7_JSON_ERROR_STATE_MISMATCH_Const}, - {"JSON_ERROR_CTRL_CHAR", PH7_JSON_ERROR_CTRL_CHAR_Const}, - {"JSON_ERROR_SYNTAX", PH7_JSON_ERROR_SYNTAX_Const}, - {"JSON_ERROR_UTF8", PH7_JSON_ERROR_UTF8_Const}, {"static", PH7_static_Const }, {"self", PH7_self_Const }, {"__CLASS__", PH7_self_Const }, @@ -1880,3 +1737,4 @@ PH7_PRIVATE void PH7_RegisterBuiltInConstant(ph7_vm *pVm) { ph7_create_constant(&(*pVm), aBuiltIn[n].zName, aBuiltIn[n].xExpand, &(*pVm)); } } +- \ No newline at end of file diff --git a/ext/json/json.c b/ext/json/json.c new file mode 100644 index 0000000..ee16435 --- /dev/null +++ b/ext/json/json.c @@ -0,0 +1,908 @@ +#include "json.h" + +/* + * JSON_HEX_TAG. + * Expand the value of JSON_HEX_TAG defined in ph7Int.h. + */ +static void PH7_JSON_HEX_TAG_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_HEX_TAG); +} +/* + * JSON_HEX_AMP. + * Expand the value of JSON_HEX_AMP defined in ph7Int.h. + */ +static void PH7_JSON_HEX_AMP_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_HEX_AMP); +} +/* + * JSON_HEX_APOS. + * Expand the value of JSON_HEX_APOS defined in ph7Int.h. + */ +static void PH7_JSON_HEX_APOS_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_HEX_APOS); +} +/* + * JSON_HEX_QUOT. + * Expand the value of JSON_HEX_QUOT defined in ph7Int.h. + */ +static void PH7_JSON_HEX_QUOT_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_HEX_QUOT); +} +/* + * JSON_FORCE_OBJECT. + * Expand the value of JSON_FORCE_OBJECT defined in ph7Int.h. + */ +static void PH7_JSON_FORCE_OBJECT_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_FORCE_OBJECT); +} +/* + * JSON_NUMERIC_CHECK. + * Expand the value of JSON_NUMERIC_CHECK defined in ph7Int.h. + */ +static void PH7_JSON_NUMERIC_CHECK_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_NUMERIC_CHECK); +} +/* + * JSON_BIGINT_AS_STRING. + * Expand the value of JSON_BIGINT_AS_STRING defined in ph7Int.h. + */ +static void PH7_JSON_BIGINT_AS_STRING_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_BIGINT_AS_STRING); +} +/* + * JSON_PRETTY_PRINT. + * Expand the value of JSON_PRETTY_PRINT defined in ph7Int.h. + */ +static void PH7_JSON_PRETTY_PRINT_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_PRETTY_PRINT); +} +/* + * JSON_UNESCAPED_SLASHES. + * Expand the value of JSON_UNESCAPED_SLASHES defined in ph7Int.h. + */ +static void PH7_JSON_UNESCAPED_SLASHES_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_UNESCAPED_SLASHES); +} +/* + * JSON_UNESCAPED_UNICODE. + * Expand the value of JSON_UNESCAPED_UNICODE defined in ph7Int.h. + */ +static void PH7_JSON_UNESCAPED_UNICODE_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_UNESCAPED_UNICODE); +} +/* + * JSON_ERROR_NONE. + * Expand the value of JSON_ERROR_NONE defined in ph7Int.h. + */ +static void PH7_JSON_ERROR_NONE_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_ERROR_NONE); +} +/* + * JSON_ERROR_DEPTH. + * Expand the value of JSON_ERROR_DEPTH defined in ph7Int.h. + */ +static void PH7_JSON_ERROR_DEPTH_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_ERROR_DEPTH); +} +/* + * JSON_ERROR_STATE_MISMATCH. + * Expand the value of JSON_ERROR_STATE_MISMATCH defined in ph7Int.h. + */ +static void PH7_JSON_ERROR_STATE_MISMATCH_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_ERROR_STATE_MISMATCH); +} +/* + * JSON_ERROR_CTRL_CHAR. + * Expand the value of JSON_ERROR_CTRL_CHAR defined in ph7Int.h. + */ +static void PH7_JSON_ERROR_CTRL_CHAR_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_ERROR_CTRL_CHAR); +} +/* + * JSON_ERROR_SYNTAX. + * Expand the value of JSON_ERROR_SYNTAX defined in ph7Int.h. + */ +static void PH7_JSON_ERROR_SYNTAX_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_ERROR_SYNTAX); +} +/* + * JSON_ERROR_UTF8. + * Expand the value of JSON_ERROR_UTF8 defined in ph7Int.h. + */ +static void PH7_JSON_ERROR_UTF8_Const(ph7_value *pVal, void *pUserData) { + SXUNUSED(pUserData); /* cc warning */ + ph7_value_int(pVal, JSON_ERROR_UTF8); +} +/* + * Returns the JSON representation of a value.In other word perform a JSON encoding operation. + * According to wikipedia + * JSON's basic types are: + * Number (double precision floating-point format in JavaScript, generally depends on implementation) + * String (double-quoted Unicode, with backslash escaping) + * Boolean (true or false) + * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values + * do not need to be of the same type) + * Object (an unordered collection of key:value pairs with the ':' character separating the key + * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should + * be distinct from each other) + * null (empty) + * Non-significant white space may be added freely around the "structural characters" + * (i.e. the brackets "[{]}", colon ":" and comma ","). + */ +static sxi32 VmJsonEncode( + ph7_value *pIn, /* Encode this value */ + json_private_data *pData /* Context data */ +) { + ph7_context *pCtx = pData->pCtx; + int iFlags = pData->iFlags; + int nByte; + if(ph7_value_is_null(pIn) || ph7_value_is_resource(pIn)) { + /* null */ + ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); + } else if(ph7_value_is_bool(pIn)) { + int iBool = ph7_value_to_bool(pIn); + int iLen; + /* true/false */ + iLen = iBool ? (int)sizeof("true") : (int)sizeof("false"); + ph7_result_string(pCtx, iBool ? "true" : "false", iLen - 1); + } else if(ph7_value_is_numeric(pIn) && !ph7_value_is_string(pIn)) { + const char *zNum; + /* Get a string representation of the number */ + zNum = ph7_value_to_string(pIn, &nByte); + ph7_result_string(pCtx, zNum, nByte); + } else if(ph7_value_is_string(pIn)) { + if((iFlags & JSON_NUMERIC_CHECK) && ph7_value_is_numeric(pIn)) { + const char *zNum; + /* Encodes numeric strings as numbers. */ + PH7_MemObjToReal(pIn); /* Force a numeric cast */ + /* Get a string representation of the number */ + zNum = ph7_value_to_string(pIn, &nByte); + ph7_result_string(pCtx, zNum, nByte); + } else { + const char *zIn, *zEnd; + int c; + /* Encode the string */ + zIn = ph7_value_to_string(pIn, &nByte); + zEnd = &zIn[nByte]; + /* Append the double quote */ + ph7_result_string(pCtx, "\"", (int)sizeof(char)); + for(;;) { + if(zIn >= zEnd) { + /* No more input to process */ + break; + } + c = zIn[0]; + /* Advance the stream cursor */ + zIn++; + if((c == '<' || c == '>') && (iFlags & JSON_HEX_TAG)) { + /* All < and > are converted to \u003C and \u003E */ + if(c == '<') { + ph7_result_string(pCtx, "\\u003C", (int)sizeof("\\u003C") - 1); + } else { + ph7_result_string(pCtx, "\\u003E", (int)sizeof("\\u003E") - 1); + } + continue; + } else if(c == '&' && (iFlags & JSON_HEX_AMP)) { + /* All &s are converted to \u0026. */ + ph7_result_string(pCtx, "\\u0026", (int)sizeof("\\u0026") - 1); + continue; + } else if(c == '\'' && (iFlags & JSON_HEX_APOS)) { + /* All ' are converted to \u0027. */ + ph7_result_string(pCtx, "\\u0027", (int)sizeof("\\u0027") - 1); + continue; + } else if(c == '"' && (iFlags & JSON_HEX_QUOT)) { + /* All " are converted to \u0022. */ + ph7_result_string(pCtx, "\\u0022", (int)sizeof("\\u0022") - 1); + continue; + } + if(c == '"' || (c == '\\' && ((iFlags & JSON_UNESCAPED_SLASHES) == 0))) { + /* Unescape the character */ + ph7_result_string(pCtx, "\\", (int)sizeof(char)); + } + /* Append character verbatim */ + ph7_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + } + /* Append the double quote */ + ph7_result_string(pCtx, "\"", (int)sizeof(char)); + } + } else if(ph7_value_is_array(pIn)) { + int c = '[', d = ']'; + /* Encode the array */ + pData->isFirst = 1; + if(iFlags & JSON_FORCE_OBJECT) { + /* Outputs an object rather than an array */ + c = '{'; + d = '}'; + } + /* Append the square bracket or curly braces */ + ph7_result_string(pCtx, (const char *)&c, (int)sizeof(char)); + /* Iterate throw array entries */ + ph7_array_walk(pIn, VmJsonArrayEncode, pData); + /* Append the closing square bracket or curly braces */ + ph7_result_string(pCtx, (const char *)&d, (int)sizeof(char)); + } else if(ph7_value_is_object(pIn)) { + /* Encode the class instance */ + pData->isFirst = 1; + /* Append the curly braces */ + ph7_result_string(pCtx, "{", (int)sizeof(char)); + /* Iterate throw class attribute */ + ph7_object_walk(pIn, VmJsonObjectEncode, pData); + /* Append the closing curly braces */ + ph7_result_string(pCtx, "}", (int)sizeof(char)); + } else { + /* Can't happen */ + ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); + } + /* All done */ + return PH7_OK; +} +/* + * The following walker callback is invoked each time we need + * to encode an array to JSON. + */ +static int VmJsonArrayEncode(ph7_value *pKey, ph7_value *pValue, void *pUserData) { + json_private_data *pJson = (json_private_data *)pUserData; + if(pJson->nRecCount > 31) { + /* Recursion limit reached,return immediately */ + return PH7_OK; + } + if(!pJson->isFirst) { + /* Append the colon first */ + ph7_result_string(pJson->pCtx, ",", (int)sizeof(char)); + } + if(pJson->iFlags & JSON_FORCE_OBJECT) { + /* Outputs an object rather than an array */ + const char *zKey; + int nByte; + /* Extract a string representation of the key */ + zKey = ph7_value_to_string(pKey, &nByte); + /* Append the key and the double colon */ + ph7_result_string_format(pJson->pCtx, "\"%.*s\":", nByte, zKey); + } + /* Encode the value */ + pJson->nRecCount++; + VmJsonEncode(pValue, pJson); + pJson->nRecCount--; + pJson->isFirst = 0; + return PH7_OK; +} +/* + * The following walker callback is invoked each time we need to encode + * a class instance [i.e: Object in the PHP jargon] to JSON. + */ +static int VmJsonObjectEncode(const char *zAttr, ph7_value *pValue, void *pUserData) { + json_private_data *pJson = (json_private_data *)pUserData; + if(pJson->nRecCount > 31) { + /* Recursion limit reached,return immediately */ + return PH7_OK; + } + if(!pJson->isFirst) { + /* Append the colon first */ + ph7_result_string(pJson->pCtx, ",", (int)sizeof(char)); + } + /* Append the attribute name and the double colon first */ + ph7_result_string_format(pJson->pCtx, "\"%s\":", zAttr); + /* Encode the value */ + pJson->nRecCount++; + VmJsonEncode(pValue, pJson); + pJson->nRecCount--; + pJson->isFirst = 0; + return PH7_OK; +} +/* + * string json_encode(mixed $value [, int $options = 0 ]) + * Returns a string containing the JSON representation of value. + * Parameters + * $value + * The value being encoded. Can be any type except a resource. + * $options + * Bitmask consisting of: + * JSON_HEX_TAG All < and > are converted to \u003C and \u003E. + * JSON_HEX_AMP All &s are converted to \u0026. + * JSON_HEX_APOS All ' are converted to \u0027. + * JSON_HEX_QUOT All " are converted to \u0022. + * JSON_FORCE_OBJECT Outputs an object rather than an array. + * JSON_NUMERIC_CHECK Encodes numeric strings as numbers. + * JSON_BIGINT_AS_STRING Not used + * JSON_PRETTY_PRINT Use whitespace in returned data to format it. + * JSON_UNESCAPED_SLASHES Don't escape '/' + * JSON_UNESCAPED_UNICODE Not used. + * Return + * Returns a JSON encoded string on success. FALSE otherwise + */ +static int vm_builtin_json_encode(ph7_context *pCtx, int nArg, ph7_value **apArg) { + json_private_data sJson; + if(nArg < 1) { + /* Missing arguments,return FALSE */ + ph7_result_bool(pCtx, 0); + return PH7_OK; + } + /* Prepare the JSON data */ + sJson.nRecCount = 0; + sJson.pCtx = pCtx; + sJson.isFirst = 1; + sJson.iFlags = 0; + if(nArg > 1 && ph7_value_is_int(apArg[1])) { + /* Extract option flags */ + sJson.iFlags = ph7_value_to_int(apArg[1]); + } + /* Perform the encoding operation */ + VmJsonEncode(apArg[0], &sJson); + /* All done */ + return PH7_OK; +} +/* + * int json_last_error(void) + * Returns the last error (if any) occurred during the last JSON encoding/decoding. + * Parameters + * None + * Return + * Returns an integer, the value can be one of the following constants: + * JSON_ERROR_NONE No error has occurred. + * JSON_ERROR_DEPTH The maximum stack depth has been exceeded. + * JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON. + * JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded. + * JSON_ERROR_SYNTAX Syntax error. + * JSON_ERROR_UTF8_CHECK Malformed UTF-8 characters. + */ +static int vm_builtin_json_last_error(ph7_context *pCtx, int nArg, ph7_value **apArg) { + ph7_vm *pVm = pCtx->pVm; + /* Return the error code */ + ph7_result_int(pCtx, pVm->json_rc); + SXUNUSED(nArg); /* cc warning */ + SXUNUSED(apArg); + return PH7_OK; +} +/* + * Tokenize an entire JSON input. + * Get a single low-level token from the input file. + * Update the stream pointer so that it points to the first + * character beyond the extracted token. + */ +static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData) { + int *pJsonErr = (int *)pUserData; + SyString *pStr; + int c; + /* Ignore leading white spaces */ + while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0])) { + /* Advance the stream cursor */ + if(pStream->zText[0] == '\n') { + /* Update line counter */ + pStream->nLine++; + } + pStream->zText++; + } + if(pStream->zText >= pStream->zEnd) { + /* End of input reached */ + SXUNUSED(pCtxData); /* cc warning */ + return SXERR_EOF; + } + /* Record token starting position and line */ + pToken->nLine = pStream->nLine; + pToken->pUserData = 0; + pStr = &pToken->sData; + SyStringInitFromBuf(pStr, pStream->zText, 0); + if(pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' + || pStream->zText[0] == ':' || pStream->zText[0] == ',') { + /* Single character */ + c = pStream->zText[0]; + /* Set token type */ + switch(c) { + case '[': + pToken->nType = JSON_TK_OSB; + break; + case '{': + pToken->nType = JSON_TK_OCB; + break; + case '}': + pToken->nType = JSON_TK_CCB; + break; + case ']': + pToken->nType = JSON_TK_CSB; + break; + case ':': + pToken->nType = JSON_TK_COLON; + break; + case ',': + pToken->nType = JSON_TK_COMMA; + break; + default: + break; + } + /* Advance the stream cursor */ + pStream->zText++; + } else if(pStream->zText[0] == '"') { + /* JSON string */ + pStream->zText++; + pStr->zString++; + /* Delimit the string */ + while(pStream->zText < pStream->zEnd) { + if(pStream->zText[0] == '"' && pStream->zText[-1] != '\\') { + break; + } + if(pStream->zText[0] == '\n') { + /* Update line counter */ + pStream->nLine++; + } + pStream->zText++; + } + if(pStream->zText >= pStream->zEnd) { + /* Missing closing '"' */ + pToken->nType = JSON_TK_INVALID; + *pJsonErr = JSON_ERROR_SYNTAX; + } else { + pToken->nType = JSON_TK_STR; + pStream->zText++; /* Jump the closing double quotes */ + } + } else if(pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { + /* Number */ + pStream->zText++; + pToken->nType = JSON_TK_NUM; + while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { + pStream->zText++; + } + if(pStream->zText < pStream->zEnd) { + c = pStream->zText[0]; + if(c == '.') { + /* Real number */ + pStream->zText++; + while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { + pStream->zText++; + } + if(pStream->zText < pStream->zEnd) { + c = pStream->zText[0]; + if(c == 'e' || c == 'E') { + pStream->zText++; + if(pStream->zText < pStream->zEnd) { + c = pStream->zText[0]; + if(c == '+' || c == '-') { + pStream->zText++; + } + while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { + pStream->zText++; + } + } + } + } + } else if(c == 'e' || c == 'E') { + /* Real number */ + pStream->zText++; + if(pStream->zText < pStream->zEnd) { + c = pStream->zText[0]; + if(c == '+' || c == '-') { + pStream->zText++; + } + while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { + pStream->zText++; + } + } + } + } + } else if(XLEX_IN_LEN(pStream) >= sizeof("true") - 1 && + SyStrnicmp((const char *)pStream->zText, "true", sizeof("true") - 1) == 0) { + /* boolean true */ + pToken->nType = JSON_TK_TRUE; + /* Advance the stream cursor */ + pStream->zText += sizeof("true") - 1; + } else if(XLEX_IN_LEN(pStream) >= sizeof("false") - 1 && + SyStrnicmp((const char *)pStream->zText, "false", sizeof("false") - 1) == 0) { + /* boolean false */ + pToken->nType = JSON_TK_FALSE; + /* Advance the stream cursor */ + pStream->zText += sizeof("false") - 1; + } else if(XLEX_IN_LEN(pStream) >= sizeof("null") - 1 && + SyStrnicmp((const char *)pStream->zText, "null", sizeof("null") - 1) == 0) { + /* NULL */ + pToken->nType = JSON_TK_NULL; + /* Advance the stream cursor */ + pStream->zText += sizeof("null") - 1; + } else { + /* Unexpected token */ + pToken->nType = JSON_TK_INVALID; + /* Advance the stream cursor */ + pStream->zText++; + *pJsonErr = JSON_ERROR_SYNTAX; + /* Abort processing immediatley */ + return SXERR_ABORT; + } + /* record token length */ + pStr->nByte = (sxu32)((const char *)pStream->zText - pStr->zString); + if(pToken->nType == JSON_TK_STR) { + pStr->nByte--; + } + /* Return to the lexer */ + return SXRET_OK; +} +/* + * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store + * the result in the given ph7_value. + */ +static void VmJsonDequoteString(const SyString *pStr, ph7_value *pWorker) { + const char *zIn = pStr->zString; + const char *zEnd = &pStr->zString[pStr->nByte]; + const char *zCur; + int c; + /* Mark the value as a string */ + ph7_value_string(pWorker, "", 0); /* Empty string */ + for(;;) { + zCur = zIn; + while(zIn < zEnd && zIn[0] != '\\') { + zIn++; + } + if(zIn > zCur) { + /* Append chunk verbatim */ + ph7_value_string(pWorker, zCur, (int)(zIn - zCur)); + } + zIn++; + if(zIn >= zEnd) { + /* End of the input reached */ + break; + } + c = zIn[0]; + /* Unescape the character */ + switch(c) { + case '"': + ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char)); + break; + case '\\': + ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char)); + break; + case 'n': + ph7_value_string(pWorker, "\n", (int)sizeof(char)); + break; + case 'r': + ph7_value_string(pWorker, "\r", (int)sizeof(char)); + break; + case 't': + ph7_value_string(pWorker, "\t", (int)sizeof(char)); + break; + case 'f': + ph7_value_string(pWorker, "\f", (int)sizeof(char)); + break; + default: + ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char)); + break; + } + /* Advance the stream cursor */ + zIn++; + } +} +/* + * Returns a ph7_value holding the image of a JSON string. In other word perform a JSON decoding operation. + * According to wikipedia + * JSON's basic types are: + * Number (double precision floating-point format in JavaScript, generally depends on implementation) + * String (double-quoted Unicode, with backslash escaping) + * Boolean (true or false) + * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values + * do not need to be of the same type) + * Object (an unordered collection of key:value pairs with the ':' character separating the key + * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should + * be distinct from each other) + * null (empty) + * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ","). + */ +static sxi32 VmJsonDecode( + json_decoder *pDecoder, /* JSON decoder */ + ph7_value *pArrayKey /* Key for the decoded array */ +) { + ph7_value *pWorker; /* Worker variable */ + sxi32 rc; + /* Check if we do not nest to much */ + if(pDecoder->rec_count >= pDecoder->rec_depth) { + /* Nesting limit reached,abort decoding immediately */ + *pDecoder->pErr = JSON_ERROR_DEPTH; + return SXERR_ABORT; + } + if(pDecoder->pIn->nType & (JSON_TK_STR | JSON_TK_TRUE | JSON_TK_FALSE | JSON_TK_NULL | JSON_TK_NUM)) { + /* Scalar value */ + pWorker = ph7_context_new_scalar(pDecoder->pCtx); + if(pWorker == 0) { + ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory"); + /* Abort the decoding operation immediately */ + return SXERR_ABORT; + } + /* Reflect the JSON image */ + if(pDecoder->pIn->nType & JSON_TK_NULL) { + /* Nullify the value.*/ + ph7_value_null(pWorker); + } else if(pDecoder->pIn->nType & (JSON_TK_TRUE | JSON_TK_FALSE)) { + /* Boolean value */ + ph7_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0); + } else if(pDecoder->pIn->nType & JSON_TK_NUM) { + SyString *pStr = &pDecoder->pIn->sData; + /* + * Numeric value. + * Get a string representation first then try to get a numeric + * value. + */ + ph7_value_string(pWorker, pStr->zString, (int)pStr->nByte); + /* Obtain a numeric representation */ + PH7_MemObjToNumeric(pWorker); + } else { + /* Dequote the string */ + VmJsonDequoteString(&pDecoder->pIn->sData, pWorker); + } + /* Invoke the consumer callback */ + rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData); + if(rc == SXERR_ABORT) { + return SXERR_ABORT; + } + /* All done,advance the stream cursor */ + pDecoder->pIn++; + } else if(pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { + ProcJsonConsumer xOld; + void *pOld; + /* Array representation*/ + pDecoder->pIn++; + /* Create a working array */ + pWorker = ph7_context_new_array(pDecoder->pCtx); + if(pWorker == 0) { + ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory"); + /* Abort the decoding operation immediately */ + return SXERR_ABORT; + } + /* Save the old consumer */ + xOld = pDecoder->xConsumer; + pOld = pDecoder->pUserData; + /* Set the new consumer */ + pDecoder->xConsumer = VmJsonArrayDecoder; + pDecoder->pUserData = pWorker; + /* Decode the array */ + for(;;) { + /* Jump trailing comma. Note that the standard PHP engine will not let you + * do this. + */ + while((pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA)) { + pDecoder->pIn++; + } + if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/) { + if(pDecoder->pIn < pDecoder->pEnd) { + pDecoder->pIn++; /* Jump the trailing ']' */ + } + break; + } + /* Recurse and decode the entry */ + pDecoder->rec_count++; + rc = VmJsonDecode(pDecoder, 0); + pDecoder->rec_count--; + if(rc == SXERR_ABORT) { + /* Abort processing immediately */ + return SXERR_ABORT; + } + /*The cursor is automatically advanced by the VmJsonDecode() function */ + if((pDecoder->pIn < pDecoder->pEnd) && + ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/ | JSON_TK_COMMA/*','*/)) == 0)) { + /* Unexpected token,abort immediatley */ + *pDecoder->pErr = JSON_ERROR_SYNTAX; + return SXERR_ABORT; + } + } + /* Restore the old consumer */ + pDecoder->xConsumer = xOld; + pDecoder->pUserData = pOld; + /* Invoke the old consumer on the decoded array */ + xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); + } else if(pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { + ProcJsonConsumer xOld; + ph7_value *pKey; + void *pOld; + /* Object representation*/ + pDecoder->pIn++; + /* Return the object as an associative array */ + if((pDecoder->iFlags & JSON_DECODE_ASSOC) == 0) { + ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_WARNING, + "JSON Objects are always returned as an associative array" + ); + } + /* Create a working array */ + pWorker = ph7_context_new_array(pDecoder->pCtx); + pKey = ph7_context_new_scalar(pDecoder->pCtx); + if(pWorker == 0 || pKey == 0) { + ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory"); + /* Abort the decoding operation immediately */ + return SXERR_ABORT; + } + /* Save the old consumer */ + xOld = pDecoder->xConsumer; + pOld = pDecoder->pUserData; + /* Set the new consumer */ + pDecoder->xConsumer = VmJsonArrayDecoder; + pDecoder->pUserData = pWorker; + /* Decode the object */ + for(;;) { + /* Jump trailing comma. Note that the standard PHP engine will not let you + * do this. + */ + while((pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA)) { + pDecoder->pIn++; + } + if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/) { + if(pDecoder->pIn < pDecoder->pEnd) { + pDecoder->pIn++; /* Jump the trailing ']' */ + } + break; + } + if((pDecoder->pIn->nType & JSON_TK_STR) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd + || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0) { + /* Syntax error,return immediately */ + *pDecoder->pErr = JSON_ERROR_SYNTAX; + return SXERR_ABORT; + } + /* Dequote the key */ + VmJsonDequoteString(&pDecoder->pIn->sData, pKey); + /* Jump the key and the colon */ + pDecoder->pIn += 2; + /* Recurse and decode the value */ + pDecoder->rec_count++; + rc = VmJsonDecode(pDecoder, pKey); + pDecoder->rec_count--; + if(rc == SXERR_ABORT) { + /* Abort processing immediately */ + return SXERR_ABORT; + } + /* Reset the internal buffer of the key */ + ph7_value_reset_string_cursor(pKey); + /*The cursor is automatically advanced by the VmJsonDecode() function */ + } + /* Restore the old consumer */ + pDecoder->xConsumer = xOld; + pDecoder->pUserData = pOld; + /* Invoke the old consumer on the decoded object*/ + xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); + /* Release the key */ + ph7_context_release_value(pDecoder->pCtx, pKey); + } else { + /* Unexpected token */ + return SXERR_ABORT; /* Abort immediately */ + } + /* Release the worker variable */ + ph7_context_release_value(pDecoder->pCtx, pWorker); + return SXRET_OK; +} +/* + * The following JSON decoder callback is invoked each time + * a JSON array representation [i.e: [15,"hello",FALSE] ] + * is being decoded. + */ +static int VmJsonArrayDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) { + ph7_value *pArray = (ph7_value *)pUserData; + /* Insert the entry */ + ph7_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */ + SXUNUSED(pCtx); /* cc warning */ + /* All done */ + return SXRET_OK; +} +/* + * Standard JSON decoder callback. + */ +static int VmJsonDefaultDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) { + /* Return the value directly */ + ph7_result_value(pCtx, pWorker); /* Will make it's own copy */ + SXUNUSED(pKey); /* cc warning */ + SXUNUSED(pUserData); + /* All done */ + return SXRET_OK; +} +/* + * mixed json_decode(string $json[,bool $assoc = false[,int $depth = 32[,int $options = 0 ]]]) + * Takes a JSON encoded string and converts it into a PHP variable. + * Parameters + * $json + * The json string being decoded. + * $assoc + * When TRUE, returned objects will be converted into associative arrays. + * $depth + * User specified recursion depth. + * $options + * Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported + * (default is to cast large integers as floats) + * Return + * The value encoded in json in appropriate PHP type. Values true, false and null (case-insensitive) + * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded + * or if the encoded data is deeper than the recursion limit. + */ +static int vm_builtin_json_decode(ph7_context *pCtx, int nArg, ph7_value **apArg) { + ph7_vm *pVm = pCtx->pVm; + json_decoder sDecoder; + const char *zIn; + SySet sToken; + SyLex sLex; + int nByte; + sxi32 rc; + if(nArg < 1 || !ph7_value_is_string(apArg[0])) { + /* Missing/Invalid arguments, return NULL */ + ph7_result_null(pCtx); + return PH7_OK; + } + /* Extract the JSON string */ + zIn = ph7_value_to_string(apArg[0], &nByte); + if(nByte < 1) { + /* Empty string,return NULL */ + ph7_result_null(pCtx); + return PH7_OK; + } + /* Clear JSON error code */ + pVm->json_rc = JSON_ERROR_NONE; + /* Tokenize the input */ + SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken)); + SyLexInit(&sLex, &sToken, VmJsonTokenize, &pVm->json_rc); + SyLexTokenizeInput(&sLex, zIn, (sxu32)nByte, 0, 0, 0); + if(pVm->json_rc != JSON_ERROR_NONE) { + /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */ + SyLexRelease(&sLex); + SySetRelease(&sToken); + /* return NULL */ + ph7_result_null(pCtx); + return PH7_OK; + } + /* Fill the decoder */ + sDecoder.pCtx = pCtx; + sDecoder.pErr = &pVm->json_rc; + sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); + sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; + sDecoder.iFlags = 0; + if(nArg > 1 && ph7_value_to_bool(apArg[1]) != 0) { + /* Returned objects will be converted into associative arrays */ + sDecoder.iFlags |= JSON_DECODE_ASSOC; + } + sDecoder.rec_depth = 32; + if(nArg > 2 && ph7_value_is_int(apArg[2])) { + int nDepth = ph7_value_to_int(apArg[2]); + if(nDepth > 1 && nDepth < 32) { + sDecoder.rec_depth = nDepth; + } + } + sDecoder.rec_count = 0; + /* Set a default consumer */ + sDecoder.xConsumer = VmJsonDefaultDecoder; + sDecoder.pUserData = 0; + /* Decode the raw JSON input */ + rc = VmJsonDecode(&sDecoder, 0); + if(rc == SXERR_ABORT || pVm->json_rc != JSON_ERROR_NONE) { + /* + * Something goes wrong while decoding JSON input.Return NULL. + */ + ph7_result_null(pCtx); + } + /* Clean-up the mess left behind */ + SyLexRelease(&sLex); + SySetRelease(&sToken); + /* All done */ + return PH7_OK; +} +PH7_PRIVATE sxi32 initializeModule(ph7_vm *pVm, ph7_real *ver, SyString *desc) { + sxi32 rc; + sxu32 n; + + desc->zString = MODULE_DESC; + *ver = MODULE_VER; + for(n = 0; n < SX_ARRAYSIZE(jsonConstList); ++n) { + rc = ph7_create_constant(&(*pVm), jsonConstList[n].zName, jsonConstList[n].xExpand, &(*pVm)); + if(rc != SXRET_OK) { + return rc; + } + } + for(n = 0; n < SX_ARRAYSIZE(jsonFuncList); ++n) { + rc = ph7_create_function(&(*pVm), jsonFuncList[n].zName, jsonFuncList[n].xFunc, &(*pVm)); + if(rc != SXRET_OK) { + return rc; + } + } + return SXRET_OK; +} \ No newline at end of file diff --git a/ext/json/json.h b/ext/json/json.h new file mode 100644 index 0000000..c315184 --- /dev/null +++ b/ext/json/json.h @@ -0,0 +1,122 @@ +#ifndef __DUMMY_H__ +#define __DUMMY_H__ + +#include "ph7.h" +#include "ph7int.h" + +#define MODULE_DESC "JSON Module" +#define MODULE_VER 1.0 + +/* The following constants can be combined to form options for json_encode(). */ +#define JSON_HEX_TAG 0x01 /* All < and > are converted to \u003C and \u003E. */ +#define JSON_HEX_AMP 0x02 /* All &s are converted to \u0026. */ +#define JSON_HEX_APOS 0x04 /* All ' are converted to \u0027. */ +#define JSON_HEX_QUOT 0x08 /* All " are converted to \u0022. */ +#define JSON_FORCE_OBJECT 0x10 /* Outputs an object rather than an array */ +#define JSON_NUMERIC_CHECK 0x20 /* Encodes numeric strings as numbers. */ +#define JSON_BIGINT_AS_STRING 0x40 /* Not used */ +#define JSON_PRETTY_PRINT 0x80 /* Use whitespace in returned data to format it.*/ +#define JSON_UNESCAPED_SLASHES 0x100 /* Don't escape '/' */ +#define JSON_UNESCAPED_UNICODE 0x200 /* Not used */ + +/* Possible tokens from the JSON tokenization process */ +#define JSON_TK_TRUE 0x001 /* Boolean true */ +#define JSON_TK_FALSE 0x002 /* Boolean false */ +#define JSON_TK_STR 0x004 /* String enclosed in double quotes */ +#define JSON_TK_NULL 0x008 /* null */ +#define JSON_TK_NUM 0x010 /* Numeric */ +#define JSON_TK_OCB 0x020 /* Open curly braces '{' */ +#define JSON_TK_CCB 0x040 /* Closing curly braces '}' */ +#define JSON_TK_OSB 0x080 /* Open square bracke '[' */ +#define JSON_TK_CSB 0x100 /* Closing square bracket ']' */ +#define JSON_TK_COLON 0x200 /* Single colon ':' */ +#define JSON_TK_COMMA 0x400 /* Single comma ',' */ +#define JSON_TK_INVALID 0x800 /* Unexpected token */ + +#define JSON_DECODE_ASSOC 0x01 /* Decode a JSON object as an associative array */ +#define JSON_DECODE_NUMERIC 0x02 /* Decode a JSON object as a numeric array */ + +/* + * JSON encoder state is stored in an instance + * of the following structure. + */ +typedef struct json_private_data json_private_data; +struct json_private_data { + ph7_context *pCtx; /* Call context */ + int isFirst; /* True if first encoded entry */ + int iFlags; /* JSON encoding flags */ + int nRecCount; /* Recursion count */ +}; + +/* + * JSON decoded input consumer callback signature. + */ +typedef int (*ProcJsonConsumer)(ph7_context *, ph7_value *, ph7_value *, void *); + +/* + * JSON decoder state is kept in the following structure. + */ +typedef struct json_decoder json_decoder; +struct json_decoder { + ph7_context *pCtx; /* Call context */ + ProcJsonConsumer xConsumer; /* Consumer callback */ + void *pUserData; /* Last argument to xConsumer() */ + int iFlags; /* Configuration flags */ + SyToken *pIn; /* Token stream */ + SyToken *pEnd; /* End of the token stream */ + int rec_depth; /* Recursion limit */ + int rec_count; /* Current nesting level */ + int *pErr; /* JSON decoding error if any */ +}; + +/* Forward reference */ +static int VmJsonArrayEncode(ph7_value *pKey, ph7_value *pValue, void *pUserData); +static int VmJsonObjectEncode(const char *zAttr, ph7_value *pValue, void *pUserData); +static int VmJsonArrayDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData); +static void PH7_JSON_HEX_TAG_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_HEX_AMP_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_HEX_APOS_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_HEX_QUOT_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_FORCE_OBJECT_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_NUMERIC_CHECK_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_BIGINT_AS_STRING_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_PRETTY_PRINT_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_UNESCAPED_SLASHES_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_UNESCAPED_UNICODE_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_ERROR_NONE_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_ERROR_DEPTH_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_ERROR_STATE_MISMATCH_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_ERROR_CTRL_CHAR_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_ERROR_SYNTAX_Const(ph7_value *pVal, void *pUserData); +static void PH7_JSON_ERROR_UTF8_Const(ph7_value *pVal, void *pUserData); +static int vm_builtin_json_encode(ph7_context *pCtx, int nArg, ph7_value **apArg); +static int vm_builtin_json_last_error(ph7_context *pCtx, int nArg, ph7_value **apArg); +static int vm_builtin_json_decode(ph7_context *pCtx, int nArg, ph7_value **apArg); +PH7_PRIVATE sxi32 initializeModule(ph7_vm *pVm, ph7_real *ver, SyString *desc); + +static const ph7_builtin_constant jsonConstList[] = { + {"JSON_HEX_TAG", PH7_JSON_HEX_TAG_Const}, + {"JSON_HEX_AMP", PH7_JSON_HEX_AMP_Const}, + {"JSON_HEX_APOS", PH7_JSON_HEX_APOS_Const}, + {"JSON_HEX_QUOT", PH7_JSON_HEX_QUOT_Const}, + {"JSON_FORCE_OBJECT", PH7_JSON_FORCE_OBJECT_Const}, + {"JSON_NUMERIC_CHECK", PH7_JSON_NUMERIC_CHECK_Const}, + {"JSON_BIGINT_AS_STRING", PH7_JSON_BIGINT_AS_STRING_Const}, + {"JSON_PRETTY_PRINT", PH7_JSON_PRETTY_PRINT_Const}, + {"JSON_UNESCAPED_SLASHES", PH7_JSON_UNESCAPED_SLASHES_Const}, + {"JSON_UNESCAPED_UNICODE", PH7_JSON_UNESCAPED_UNICODE_Const}, + {"JSON_ERROR_NONE", PH7_JSON_ERROR_NONE_Const}, + {"JSON_ERROR_DEPTH", PH7_JSON_ERROR_DEPTH_Const}, + {"JSON_ERROR_STATE_MISMATCH", PH7_JSON_ERROR_STATE_MISMATCH_Const}, + {"JSON_ERROR_CTRL_CHAR", PH7_JSON_ERROR_CTRL_CHAR_Const}, + {"JSON_ERROR_SYNTAX", PH7_JSON_ERROR_SYNTAX_Const}, + {"JSON_ERROR_UTF8", PH7_JSON_ERROR_UTF8_Const} +}; + +static const ph7_builtin_func jsonFuncList[] = { + {"json_encode", vm_builtin_json_encode }, + {"json_last_error", vm_builtin_json_last_error}, + {"json_decode", vm_builtin_json_decode } +}; + +#endif \ No newline at end of file diff --git a/ph7int.h b/ph7int.h index c3919d3..ccc8cec 100644 --- a/ph7int.h +++ b/ph7int.h @@ -1625,17 +1625,6 @@ enum json_err_code { JSON_ERROR_SYNTAX, /* Syntax error. */ JSON_ERROR_UTF8 /* Malformed UTF-8 characters */ }; -/* The following constants can be combined to form options for json_encode(). */ -#define JSON_HEX_TAG 0x01 /* All < and > are converted to \u003C and \u003E. */ -#define JSON_HEX_AMP 0x02 /* All &s are converted to \u0026. */ -#define JSON_HEX_APOS 0x04 /* All ' are converted to \u0027. */ -#define JSON_HEX_QUOT 0x08 /* All " are converted to \u0022. */ -#define JSON_FORCE_OBJECT 0x10 /* Outputs an object rather than an array */ -#define JSON_NUMERIC_CHECK 0x20 /* Encodes numeric strings as numbers. */ -#define JSON_BIGINT_AS_STRING 0x40 /* Not used */ -#define JSON_PRETTY_PRINT 0x80 /* Use whitespace in returned data to format it.*/ -#define JSON_UNESCAPED_SLASHES 0x100 /* Don't escape '/' */ -#define JSON_UNESCAPED_UNICODE 0x200 /* Not used */ /* memobj.c function prototypes */ PH7_PRIVATE sxi32 PH7_MemObjDump(SyBlob *pOut, ph7_value *pObj, int ShowType, int nTab, int nDepth, int isRef); PH7_PRIVATE const char *PH7_MemObjTypeDump(ph7_value *pVal); diff --git a/vm.c b/vm.c index 5f8cab4..6d19368 100644 --- a/vm.c +++ b/vm.c @@ -11379,822 +11379,6 @@ static int VmProcessLongOpt(ph7_value *pKey, ph7_value *pValue, void *pUserData) VmExtractOptArgValue(pOpt->pArray, pOpt->pWorker, zArg, pOpt->zArgEnd, need_value, pOpt->pCtx, zOpt); return PH7_OK; } -/* - * Section: - * JSON encoding/decoding routines. - * Authors: - * Symisc Systems,devel@symisc.net. - * Copyright (C) Symisc Systems,http://ph7.symisc.net - * Status: - * Devel. - */ -/* Forward reference */ -static int VmJsonArrayEncode(ph7_value *pKey, ph7_value *pValue, void *pUserData); -static int VmJsonObjectEncode(const char *zAttr, ph7_value *pValue, void *pUserData); -/* - * JSON encoder state is stored in an instance - * of the following structure. - */ -typedef struct json_private_data json_private_data; -struct json_private_data { - ph7_context *pCtx; /* Call context */ - int isFirst; /* True if first encoded entry */ - int iFlags; /* JSON encoding flags */ - int nRecCount; /* Recursion count */ -}; -/* - * Returns the JSON representation of a value.In other word perform a JSON encoding operation. - * According to wikipedia - * JSON's basic types are: - * Number (double precision floating-point format in JavaScript, generally depends on implementation) - * String (double-quoted Unicode, with backslash escaping) - * Boolean (true or false) - * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values - * do not need to be of the same type) - * Object (an unordered collection of key:value pairs with the ':' character separating the key - * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should - * be distinct from each other) - * null (empty) - * Non-significant white space may be added freely around the "structural characters" - * (i.e. the brackets "[{]}", colon ":" and comma ","). - */ -static sxi32 VmJsonEncode( - ph7_value *pIn, /* Encode this value */ - json_private_data *pData /* Context data */ -) { - ph7_context *pCtx = pData->pCtx; - int iFlags = pData->iFlags; - int nByte; - if(ph7_value_is_null(pIn) || ph7_value_is_resource(pIn)) { - /* null */ - ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); - } else if(ph7_value_is_bool(pIn)) { - int iBool = ph7_value_to_bool(pIn); - int iLen; - /* true/false */ - iLen = iBool ? (int)sizeof("true") : (int)sizeof("false"); - ph7_result_string(pCtx, iBool ? "true" : "false", iLen - 1); - } else if(ph7_value_is_numeric(pIn) && !ph7_value_is_string(pIn)) { - const char *zNum; - /* Get a string representation of the number */ - zNum = ph7_value_to_string(pIn, &nByte); - ph7_result_string(pCtx, zNum, nByte); - } else if(ph7_value_is_string(pIn)) { - if((iFlags & JSON_NUMERIC_CHECK) && ph7_value_is_numeric(pIn)) { - const char *zNum; - /* Encodes numeric strings as numbers. */ - PH7_MemObjToReal(pIn); /* Force a numeric cast */ - /* Get a string representation of the number */ - zNum = ph7_value_to_string(pIn, &nByte); - ph7_result_string(pCtx, zNum, nByte); - } else { - const char *zIn, *zEnd; - int c; - /* Encode the string */ - zIn = ph7_value_to_string(pIn, &nByte); - zEnd = &zIn[nByte]; - /* Append the double quote */ - ph7_result_string(pCtx, "\"", (int)sizeof(char)); - for(;;) { - if(zIn >= zEnd) { - /* No more input to process */ - break; - } - c = zIn[0]; - /* Advance the stream cursor */ - zIn++; - if((c == '<' || c == '>') && (iFlags & JSON_HEX_TAG)) { - /* All < and > are converted to \u003C and \u003E */ - if(c == '<') { - ph7_result_string(pCtx, "\\u003C", (int)sizeof("\\u003C") - 1); - } else { - ph7_result_string(pCtx, "\\u003E", (int)sizeof("\\u003E") - 1); - } - continue; - } else if(c == '&' && (iFlags & JSON_HEX_AMP)) { - /* All &s are converted to \u0026. */ - ph7_result_string(pCtx, "\\u0026", (int)sizeof("\\u0026") - 1); - continue; - } else if(c == '\'' && (iFlags & JSON_HEX_APOS)) { - /* All ' are converted to \u0027. */ - ph7_result_string(pCtx, "\\u0027", (int)sizeof("\\u0027") - 1); - continue; - } else if(c == '"' && (iFlags & JSON_HEX_QUOT)) { - /* All " are converted to \u0022. */ - ph7_result_string(pCtx, "\\u0022", (int)sizeof("\\u0022") - 1); - continue; - } - if(c == '"' || (c == '\\' && ((iFlags & JSON_UNESCAPED_SLASHES) == 0))) { - /* Unescape the character */ - ph7_result_string(pCtx, "\\", (int)sizeof(char)); - } - /* Append character verbatim */ - ph7_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - } - /* Append the double quote */ - ph7_result_string(pCtx, "\"", (int)sizeof(char)); - } - } else if(ph7_value_is_array(pIn)) { - int c = '[', d = ']'; - /* Encode the array */ - pData->isFirst = 1; - if(iFlags & JSON_FORCE_OBJECT) { - /* Outputs an object rather than an array */ - c = '{'; - d = '}'; - } - /* Append the square bracket or curly braces */ - ph7_result_string(pCtx, (const char *)&c, (int)sizeof(char)); - /* Iterate throw array entries */ - ph7_array_walk(pIn, VmJsonArrayEncode, pData); - /* Append the closing square bracket or curly braces */ - ph7_result_string(pCtx, (const char *)&d, (int)sizeof(char)); - } else if(ph7_value_is_object(pIn)) { - /* Encode the class instance */ - pData->isFirst = 1; - /* Append the curly braces */ - ph7_result_string(pCtx, "{", (int)sizeof(char)); - /* Iterate throw class attribute */ - ph7_object_walk(pIn, VmJsonObjectEncode, pData); - /* Append the closing curly braces */ - ph7_result_string(pCtx, "}", (int)sizeof(char)); - } else { - /* Can't happen */ - ph7_result_string(pCtx, "null", (int)sizeof("null") - 1); - } - /* All done */ - return PH7_OK; -} -/* - * The following walker callback is invoked each time we need - * to encode an array to JSON. - */ -static int VmJsonArrayEncode(ph7_value *pKey, ph7_value *pValue, void *pUserData) { - json_private_data *pJson = (json_private_data *)pUserData; - if(pJson->nRecCount > 31) { - /* Recursion limit reached,return immediately */ - return PH7_OK; - } - if(!pJson->isFirst) { - /* Append the colon first */ - ph7_result_string(pJson->pCtx, ",", (int)sizeof(char)); - } - if(pJson->iFlags & JSON_FORCE_OBJECT) { - /* Outputs an object rather than an array */ - const char *zKey; - int nByte; - /* Extract a string representation of the key */ - zKey = ph7_value_to_string(pKey, &nByte); - /* Append the key and the double colon */ - ph7_result_string_format(pJson->pCtx, "\"%.*s\":", nByte, zKey); - } - /* Encode the value */ - pJson->nRecCount++; - VmJsonEncode(pValue, pJson); - pJson->nRecCount--; - pJson->isFirst = 0; - return PH7_OK; -} -/* - * The following walker callback is invoked each time we need to encode - * a class instance [i.e: Object in the PHP jargon] to JSON. - */ -static int VmJsonObjectEncode(const char *zAttr, ph7_value *pValue, void *pUserData) { - json_private_data *pJson = (json_private_data *)pUserData; - if(pJson->nRecCount > 31) { - /* Recursion limit reached,return immediately */ - return PH7_OK; - } - if(!pJson->isFirst) { - /* Append the colon first */ - ph7_result_string(pJson->pCtx, ",", (int)sizeof(char)); - } - /* Append the attribute name and the double colon first */ - ph7_result_string_format(pJson->pCtx, "\"%s\":", zAttr); - /* Encode the value */ - pJson->nRecCount++; - VmJsonEncode(pValue, pJson); - pJson->nRecCount--; - pJson->isFirst = 0; - return PH7_OK; -} -/* - * string json_encode(mixed $value [, int $options = 0 ]) - * Returns a string containing the JSON representation of value. - * Parameters - * $value - * The value being encoded. Can be any type except a resource. - * $options - * Bitmask consisting of: - * JSON_HEX_TAG All < and > are converted to \u003C and \u003E. - * JSON_HEX_AMP All &s are converted to \u0026. - * JSON_HEX_APOS All ' are converted to \u0027. - * JSON_HEX_QUOT All " are converted to \u0022. - * JSON_FORCE_OBJECT Outputs an object rather than an array. - * JSON_NUMERIC_CHECK Encodes numeric strings as numbers. - * JSON_BIGINT_AS_STRING Not used - * JSON_PRETTY_PRINT Use whitespace in returned data to format it. - * JSON_UNESCAPED_SLASHES Don't escape '/' - * JSON_UNESCAPED_UNICODE Not used. - * Return - * Returns a JSON encoded string on success. FALSE otherwise - */ -static int vm_builtin_json_encode(ph7_context *pCtx, int nArg, ph7_value **apArg) { - json_private_data sJson; - if(nArg < 1) { - /* Missing arguments,return FALSE */ - ph7_result_bool(pCtx, 0); - return PH7_OK; - } - /* Prepare the JSON data */ - sJson.nRecCount = 0; - sJson.pCtx = pCtx; - sJson.isFirst = 1; - sJson.iFlags = 0; - if(nArg > 1 && ph7_value_is_int(apArg[1])) { - /* Extract option flags */ - sJson.iFlags = ph7_value_to_int(apArg[1]); - } - /* Perform the encoding operation */ - VmJsonEncode(apArg[0], &sJson); - /* All done */ - return PH7_OK; -} -/* - * int json_last_error(void) - * Returns the last error (if any) occurred during the last JSON encoding/decoding. - * Parameters - * None - * Return - * Returns an integer, the value can be one of the following constants: - * JSON_ERROR_NONE No error has occurred. - * JSON_ERROR_DEPTH The maximum stack depth has been exceeded. - * JSON_ERROR_STATE_MISMATCH Invalid or malformed JSON. - * JSON_ERROR_CTRL_CHAR Control character error, possibly incorrectly encoded. - * JSON_ERROR_SYNTAX Syntax error. - * JSON_ERROR_UTF8_CHECK Malformed UTF-8 characters. - */ -static int vm_builtin_json_last_error(ph7_context *pCtx, int nArg, ph7_value **apArg) { - ph7_vm *pVm = pCtx->pVm; - /* Return the error code */ - ph7_result_int(pCtx, pVm->json_rc); - SXUNUSED(nArg); /* cc warning */ - SXUNUSED(apArg); - return PH7_OK; -} -/* Possible tokens from the JSON tokenization process */ -#define JSON_TK_TRUE 0x001 /* Boolean true */ -#define JSON_TK_FALSE 0x002 /* Boolean false */ -#define JSON_TK_STR 0x004 /* String enclosed in double quotes */ -#define JSON_TK_NULL 0x008 /* null */ -#define JSON_TK_NUM 0x010 /* Numeric */ -#define JSON_TK_OCB 0x020 /* Open curly braces '{' */ -#define JSON_TK_CCB 0x040 /* Closing curly braces '}' */ -#define JSON_TK_OSB 0x080 /* Open square bracke '[' */ -#define JSON_TK_CSB 0x100 /* Closing square bracket ']' */ -#define JSON_TK_COLON 0x200 /* Single colon ':' */ -#define JSON_TK_COMMA 0x400 /* Single comma ',' */ -#define JSON_TK_INVALID 0x800 /* Unexpected token */ -/* - * Tokenize an entire JSON input. - * Get a single low-level token from the input file. - * Update the stream pointer so that it points to the first - * character beyond the extracted token. - */ -static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData) { - int *pJsonErr = (int *)pUserData; - SyString *pStr; - int c; - /* Ignore leading white spaces */ - while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0])) { - /* Advance the stream cursor */ - if(pStream->zText[0] == '\n') { - /* Update line counter */ - pStream->nLine++; - } - pStream->zText++; - } - if(pStream->zText >= pStream->zEnd) { - /* End of input reached */ - SXUNUSED(pCtxData); /* cc warning */ - return SXERR_EOF; - } - /* Record token starting position and line */ - pToken->nLine = pStream->nLine; - pToken->pUserData = 0; - pStr = &pToken->sData; - SyStringInitFromBuf(pStr, pStream->zText, 0); - if(pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' - || pStream->zText[0] == ':' || pStream->zText[0] == ',') { - /* Single character */ - c = pStream->zText[0]; - /* Set token type */ - switch(c) { - case '[': - pToken->nType = JSON_TK_OSB; - break; - case '{': - pToken->nType = JSON_TK_OCB; - break; - case '}': - pToken->nType = JSON_TK_CCB; - break; - case ']': - pToken->nType = JSON_TK_CSB; - break; - case ':': - pToken->nType = JSON_TK_COLON; - break; - case ',': - pToken->nType = JSON_TK_COMMA; - break; - default: - break; - } - /* Advance the stream cursor */ - pStream->zText++; - } else if(pStream->zText[0] == '"') { - /* JSON string */ - pStream->zText++; - pStr->zString++; - /* Delimit the string */ - while(pStream->zText < pStream->zEnd) { - if(pStream->zText[0] == '"' && pStream->zText[-1] != '\\') { - break; - } - if(pStream->zText[0] == '\n') { - /* Update line counter */ - pStream->nLine++; - } - pStream->zText++; - } - if(pStream->zText >= pStream->zEnd) { - /* Missing closing '"' */ - pToken->nType = JSON_TK_INVALID; - *pJsonErr = JSON_ERROR_SYNTAX; - } else { - pToken->nType = JSON_TK_STR; - pStream->zText++; /* Jump the closing double quotes */ - } - } else if(pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { - /* Number */ - pStream->zText++; - pToken->nType = JSON_TK_NUM; - while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { - pStream->zText++; - } - if(pStream->zText < pStream->zEnd) { - c = pStream->zText[0]; - if(c == '.') { - /* Real number */ - pStream->zText++; - while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { - pStream->zText++; - } - if(pStream->zText < pStream->zEnd) { - c = pStream->zText[0]; - if(c == 'e' || c == 'E') { - pStream->zText++; - if(pStream->zText < pStream->zEnd) { - c = pStream->zText[0]; - if(c == '+' || c == '-') { - pStream->zText++; - } - while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { - pStream->zText++; - } - } - } - } - } else if(c == 'e' || c == 'E') { - /* Real number */ - pStream->zText++; - if(pStream->zText < pStream->zEnd) { - c = pStream->zText[0]; - if(c == '+' || c == '-') { - pStream->zText++; - } - while(pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0])) { - pStream->zText++; - } - } - } - } - } else if(XLEX_IN_LEN(pStream) >= sizeof("true") - 1 && - SyStrnicmp((const char *)pStream->zText, "true", sizeof("true") - 1) == 0) { - /* boolean true */ - pToken->nType = JSON_TK_TRUE; - /* Advance the stream cursor */ - pStream->zText += sizeof("true") - 1; - } else if(XLEX_IN_LEN(pStream) >= sizeof("false") - 1 && - SyStrnicmp((const char *)pStream->zText, "false", sizeof("false") - 1) == 0) { - /* boolean false */ - pToken->nType = JSON_TK_FALSE; - /* Advance the stream cursor */ - pStream->zText += sizeof("false") - 1; - } else if(XLEX_IN_LEN(pStream) >= sizeof("null") - 1 && - SyStrnicmp((const char *)pStream->zText, "null", sizeof("null") - 1) == 0) { - /* NULL */ - pToken->nType = JSON_TK_NULL; - /* Advance the stream cursor */ - pStream->zText += sizeof("null") - 1; - } else { - /* Unexpected token */ - pToken->nType = JSON_TK_INVALID; - /* Advance the stream cursor */ - pStream->zText++; - *pJsonErr = JSON_ERROR_SYNTAX; - /* Abort processing immediatley */ - return SXERR_ABORT; - } - /* record token length */ - pStr->nByte = (sxu32)((const char *)pStream->zText - pStr->zString); - if(pToken->nType == JSON_TK_STR) { - pStr->nByte--; - } - /* Return to the lexer */ - return SXRET_OK; -} -/* - * JSON decoded input consumer callback signature. - */ -typedef int (*ProcJsonConsumer)(ph7_context *, ph7_value *, ph7_value *, void *); -/* - * JSON decoder state is kept in the following structure. - */ -typedef struct json_decoder json_decoder; -struct json_decoder { - ph7_context *pCtx; /* Call context */ - ProcJsonConsumer xConsumer; /* Consumer callback */ - void *pUserData; /* Last argument to xConsumer() */ - int iFlags; /* Configuration flags */ - SyToken *pIn; /* Token stream */ - SyToken *pEnd; /* End of the token stream */ - int rec_depth; /* Recursion limit */ - int rec_count; /* Current nesting level */ - int *pErr; /* JSON decoding error if any */ -}; -#define JSON_DECODE_ASSOC 0x01 /* Decode a JSON object as an associative array */ -/* Forward declaration */ -static int VmJsonArrayDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData); -/* - * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store - * the result in the given ph7_value. - */ -static void VmJsonDequoteString(const SyString *pStr, ph7_value *pWorker) { - const char *zIn = pStr->zString; - const char *zEnd = &pStr->zString[pStr->nByte]; - const char *zCur; - int c; - /* Mark the value as a string */ - ph7_value_string(pWorker, "", 0); /* Empty string */ - for(;;) { - zCur = zIn; - while(zIn < zEnd && zIn[0] != '\\') { - zIn++; - } - if(zIn > zCur) { - /* Append chunk verbatim */ - ph7_value_string(pWorker, zCur, (int)(zIn - zCur)); - } - zIn++; - if(zIn >= zEnd) { - /* End of the input reached */ - break; - } - c = zIn[0]; - /* Unescape the character */ - switch(c) { - case '"': - ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char)); - break; - case '\\': - ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char)); - break; - case 'n': - ph7_value_string(pWorker, "\n", (int)sizeof(char)); - break; - case 'r': - ph7_value_string(pWorker, "\r", (int)sizeof(char)); - break; - case 't': - ph7_value_string(pWorker, "\t", (int)sizeof(char)); - break; - case 'f': - ph7_value_string(pWorker, "\f", (int)sizeof(char)); - break; - default: - ph7_value_string(pWorker, (const char *)&c, (int)sizeof(char)); - break; - } - /* Advance the stream cursor */ - zIn++; - } -} -/* - * Returns a ph7_value holding the image of a JSON string. In other word perform a JSON decoding operation. - * According to wikipedia - * JSON's basic types are: - * Number (double precision floating-point format in JavaScript, generally depends on implementation) - * String (double-quoted Unicode, with backslash escaping) - * Boolean (true or false) - * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values - * do not need to be of the same type) - * Object (an unordered collection of key:value pairs with the ':' character separating the key - * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should - * be distinct from each other) - * null (empty) - * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ","). - */ -static sxi32 VmJsonDecode( - json_decoder *pDecoder, /* JSON decoder */ - ph7_value *pArrayKey /* Key for the decoded array */ -) { - ph7_value *pWorker; /* Worker variable */ - sxi32 rc; - /* Check if we do not nest to much */ - if(pDecoder->rec_count >= pDecoder->rec_depth) { - /* Nesting limit reached,abort decoding immediately */ - *pDecoder->pErr = JSON_ERROR_DEPTH; - return SXERR_ABORT; - } - if(pDecoder->pIn->nType & (JSON_TK_STR | JSON_TK_TRUE | JSON_TK_FALSE | JSON_TK_NULL | JSON_TK_NUM)) { - /* Scalar value */ - pWorker = ph7_context_new_scalar(pDecoder->pCtx); - if(pWorker == 0) { - ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory"); - /* Abort the decoding operation immediately */ - return SXERR_ABORT; - } - /* Reflect the JSON image */ - if(pDecoder->pIn->nType & JSON_TK_NULL) { - /* Nullify the value.*/ - ph7_value_null(pWorker); - } else if(pDecoder->pIn->nType & (JSON_TK_TRUE | JSON_TK_FALSE)) { - /* Boolean value */ - ph7_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0); - } else if(pDecoder->pIn->nType & JSON_TK_NUM) { - SyString *pStr = &pDecoder->pIn->sData; - /* - * Numeric value. - * Get a string representation first then try to get a numeric - * value. - */ - ph7_value_string(pWorker, pStr->zString, (int)pStr->nByte); - /* Obtain a numeric representation */ - PH7_MemObjToNumeric(pWorker); - } else { - /* Dequote the string */ - VmJsonDequoteString(&pDecoder->pIn->sData, pWorker); - } - /* Invoke the consumer callback */ - rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData); - if(rc == SXERR_ABORT) { - return SXERR_ABORT; - } - /* All done,advance the stream cursor */ - pDecoder->pIn++; - } else if(pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { - ProcJsonConsumer xOld; - void *pOld; - /* Array representation*/ - pDecoder->pIn++; - /* Create a working array */ - pWorker = ph7_context_new_array(pDecoder->pCtx); - if(pWorker == 0) { - ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory"); - /* Abort the decoding operation immediately */ - return SXERR_ABORT; - } - /* Save the old consumer */ - xOld = pDecoder->xConsumer; - pOld = pDecoder->pUserData; - /* Set the new consumer */ - pDecoder->xConsumer = VmJsonArrayDecoder; - pDecoder->pUserData = pWorker; - /* Decode the array */ - for(;;) { - /* Jump trailing comma. Note that the standard PHP engine will not let you - * do this. - */ - while((pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA)) { - pDecoder->pIn++; - } - if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/) { - if(pDecoder->pIn < pDecoder->pEnd) { - pDecoder->pIn++; /* Jump the trailing ']' */ - } - break; - } - /* Recurse and decode the entry */ - pDecoder->rec_count++; - rc = VmJsonDecode(pDecoder, 0); - pDecoder->rec_count--; - if(rc == SXERR_ABORT) { - /* Abort processing immediately */ - return SXERR_ABORT; - } - /*The cursor is automatically advanced by the VmJsonDecode() function */ - if((pDecoder->pIn < pDecoder->pEnd) && - ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/ | JSON_TK_COMMA/*','*/)) == 0)) { - /* Unexpected token,abort immediatley */ - *pDecoder->pErr = JSON_ERROR_SYNTAX; - return SXERR_ABORT; - } - } - /* Restore the old consumer */ - pDecoder->xConsumer = xOld; - pDecoder->pUserData = pOld; - /* Invoke the old consumer on the decoded array */ - xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); - } else if(pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { - ProcJsonConsumer xOld; - ph7_value *pKey; - void *pOld; - /* Object representation*/ - pDecoder->pIn++; - /* Return the object as an associative array */ - if((pDecoder->iFlags & JSON_DECODE_ASSOC) == 0) { - ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_WARNING, - "JSON Objects are always returned as an associative array" - ); - } - /* Create a working array */ - pWorker = ph7_context_new_array(pDecoder->pCtx); - pKey = ph7_context_new_scalar(pDecoder->pCtx); - if(pWorker == 0 || pKey == 0) { - ph7_context_throw_error(pDecoder->pCtx, PH7_CTX_ERR, "PH7 is running out of memory"); - /* Abort the decoding operation immediately */ - return SXERR_ABORT; - } - /* Save the old consumer */ - xOld = pDecoder->xConsumer; - pOld = pDecoder->pUserData; - /* Set the new consumer */ - pDecoder->xConsumer = VmJsonArrayDecoder; - pDecoder->pUserData = pWorker; - /* Decode the object */ - for(;;) { - /* Jump trailing comma. Note that the standard PHP engine will not let you - * do this. - */ - while((pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA)) { - pDecoder->pIn++; - } - if(pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/) { - if(pDecoder->pIn < pDecoder->pEnd) { - pDecoder->pIn++; /* Jump the trailing ']' */ - } - break; - } - if((pDecoder->pIn->nType & JSON_TK_STR) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd - || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0) { - /* Syntax error,return immediately */ - *pDecoder->pErr = JSON_ERROR_SYNTAX; - return SXERR_ABORT; - } - /* Dequote the key */ - VmJsonDequoteString(&pDecoder->pIn->sData, pKey); - /* Jump the key and the colon */ - pDecoder->pIn += 2; - /* Recurse and decode the value */ - pDecoder->rec_count++; - rc = VmJsonDecode(pDecoder, pKey); - pDecoder->rec_count--; - if(rc == SXERR_ABORT) { - /* Abort processing immediately */ - return SXERR_ABORT; - } - /* Reset the internal buffer of the key */ - ph7_value_reset_string_cursor(pKey); - /*The cursor is automatically advanced by the VmJsonDecode() function */ - } - /* Restore the old consumer */ - pDecoder->xConsumer = xOld; - pDecoder->pUserData = pOld; - /* Invoke the old consumer on the decoded object*/ - xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); - /* Release the key */ - ph7_context_release_value(pDecoder->pCtx, pKey); - } else { - /* Unexpected token */ - return SXERR_ABORT; /* Abort immediately */ - } - /* Release the worker variable */ - ph7_context_release_value(pDecoder->pCtx, pWorker); - return SXRET_OK; -} -/* - * The following JSON decoder callback is invoked each time - * a JSON array representation [i.e: [15,"hello",FALSE] ] - * is being decoded. - */ -static int VmJsonArrayDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) { - ph7_value *pArray = (ph7_value *)pUserData; - /* Insert the entry */ - ph7_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */ - SXUNUSED(pCtx); /* cc warning */ - /* All done */ - return SXRET_OK; -} -/* - * Standard JSON decoder callback. - */ -static int VmJsonDefaultDecoder(ph7_context *pCtx, ph7_value *pKey, ph7_value *pWorker, void *pUserData) { - /* Return the value directly */ - ph7_result_value(pCtx, pWorker); /* Will make it's own copy */ - SXUNUSED(pKey); /* cc warning */ - SXUNUSED(pUserData); - /* All done */ - return SXRET_OK; -} -/* - * mixed json_decode(string $json[,bool $assoc = false[,int $depth = 32[,int $options = 0 ]]]) - * Takes a JSON encoded string and converts it into a PHP variable. - * Parameters - * $json - * The json string being decoded. - * $assoc - * When TRUE, returned objects will be converted into associative arrays. - * $depth - * User specified recursion depth. - * $options - * Bitmask of JSON decode options. Currently only JSON_BIGINT_AS_STRING is supported - * (default is to cast large integers as floats) - * Return - * The value encoded in json in appropriate PHP type. Values true, false and null (case-insensitive) - * are returned as TRUE, FALSE and NULL respectively. NULL is returned if the json cannot be decoded - * or if the encoded data is deeper than the recursion limit. - */ -static int vm_builtin_json_decode(ph7_context *pCtx, int nArg, ph7_value **apArg) { - ph7_vm *pVm = pCtx->pVm; - json_decoder sDecoder; - const char *zIn; - SySet sToken; - SyLex sLex; - int nByte; - sxi32 rc; - if(nArg < 1 || !ph7_value_is_string(apArg[0])) { - /* Missing/Invalid arguments, return NULL */ - ph7_result_null(pCtx); - return PH7_OK; - } - /* Extract the JSON string */ - zIn = ph7_value_to_string(apArg[0], &nByte); - if(nByte < 1) { - /* Empty string,return NULL */ - ph7_result_null(pCtx); - return PH7_OK; - } - /* Clear JSON error code */ - pVm->json_rc = JSON_ERROR_NONE; - /* Tokenize the input */ - SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken)); - SyLexInit(&sLex, &sToken, VmJsonTokenize, &pVm->json_rc); - SyLexTokenizeInput(&sLex, zIn, (sxu32)nByte, 0, 0, 0); - if(pVm->json_rc != JSON_ERROR_NONE) { - /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */ - SyLexRelease(&sLex); - SySetRelease(&sToken); - /* return NULL */ - ph7_result_null(pCtx); - return PH7_OK; - } - /* Fill the decoder */ - sDecoder.pCtx = pCtx; - sDecoder.pErr = &pVm->json_rc; - sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); - sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; - sDecoder.iFlags = 0; - if(nArg > 1 && ph7_value_to_bool(apArg[1]) != 0) { - /* Returned objects will be converted into associative arrays */ - sDecoder.iFlags |= JSON_DECODE_ASSOC; - } - sDecoder.rec_depth = 32; - if(nArg > 2 && ph7_value_is_int(apArg[2])) { - int nDepth = ph7_value_to_int(apArg[2]); - if(nDepth > 1 && nDepth < 32) { - sDecoder.rec_depth = nDepth; - } - } - sDecoder.rec_count = 0; - /* Set a default consumer */ - sDecoder.xConsumer = VmJsonDefaultDecoder; - sDecoder.pUserData = 0; - /* Decode the raw JSON input */ - rc = VmJsonDecode(&sDecoder, 0); - if(rc == SXERR_ABORT || pVm->json_rc != JSON_ERROR_NONE) { - /* - * Something goes wrong while decoding JSON input.Return NULL. - */ - ph7_result_null(pCtx); - } - /* Clean-up the mess left behind */ - SyLexRelease(&sLex); - SySetRelease(&sToken); - /* All done */ - return PH7_OK; -} #ifndef PH7_DISABLE_BUILTIN_FUNC /* * XML processing Functions. @@ -13798,10 +12982,6 @@ static const ph7_builtin_func aVmFunc[] = { {"utf8_decode", vm_builtin_utf8_decode}, /* Command line processing */ {"getopt", vm_builtin_getopt }, - /* JSON encoding/decoding */ - {"json_encode", vm_builtin_json_encode }, - {"json_last_error", vm_builtin_json_last_error}, - {"json_decode", vm_builtin_json_decode }, /* Module loading facility */ { "import", vm_builtin_import }, /* Files/URI inclusion facility */