summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/json/JSON_parser.c30
-rw-r--r--ext/json/JSON_parser.h10
-rw-r--r--ext/json/json.c49
-rw-r--r--ext/json/php_json.h10
-rw-r--r--ext/json/tests/007.phpt36
5 files changed, 107 insertions, 28 deletions
diff --git a/ext/json/JSON_parser.c b/ext/json/JSON_parser.c
index 95a7ffba1f..edb1adf005 100644
--- a/ext/json/JSON_parser.c
+++ b/ext/json/JSON_parser.c
@@ -194,13 +194,6 @@ enum modes {
MODE_OBJECT,
};
-enum error_codes {
- ERROR_DEPTH,
- ERROR_MISMATCH,
- ERROR_CTRL_CHAR,
- ERROR_SYNTAX,
-};
-
/*
Push a mode onto the stack. Return false if there is overflow.
@@ -210,7 +203,7 @@ push(JSON_parser jp, int mode)
{
jp->top += 1;
if (jp->top >= jp->depth) {
- jp->error = ERROR_DEPTH;
+ jp->error_code = PHP_JSON_ERROR_DEPTH;
return false;
}
jp->stack[jp->top] = mode;
@@ -226,7 +219,7 @@ static int
pop(JSON_parser jp, int mode)
{
if (jp->top < 0 || jp->stack[jp->top] != mode) {
- jp->error = ERROR_MISMATCH;
+ jp->error_code = PHP_JSON_ERROR_STATE_MISMATCH;
return false;
}
jp->top -= 1;
@@ -252,6 +245,7 @@ new_JSON_parser(int depth)
jp->state = GO;
jp->depth = depth;
jp->top = -1;
+ jp->error_code = PHP_JSON_ERROR_NONE;
jp->stack = (int*)ecalloc(depth, sizeof(int));
push(jp, MODE_DONE);
return jp;
@@ -428,7 +422,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
} else {
next_class = ascii_class[next_char];
if (next_class <= __) {
- jp->error = ERROR_CTRL_CHAR;
+ jp->error_code = PHP_JSON_ERROR_CTRL_CHAR;
FREE_BUFFERS();
return false;
}
@@ -511,9 +505,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
break;
/* } */
case -8:
- if (type != -1 &&
- (jp->stack[jp->top] == MODE_OBJECT ||
- jp->stack[jp->top] == MODE_ARRAY))
+ if (type != -1 && jp->stack[jp->top] == MODE_OBJECT)
{
zval *mval;
smart_str_0(&buf);
@@ -541,9 +533,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
/* ] */
case -7:
{
- if (type != -1 &&
- (jp->stack[jp->top] == MODE_OBJECT ||
- jp->stack[jp->top] == MODE_ARRAY))
+ if (type != -1 && jp->stack[jp->top] == MODE_ARRAY)
{
zval *mval;
smart_str_0(&buf);
@@ -702,7 +692,7 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
*/
default:
{
- jp->error = ERROR_SYNTAX;
+ jp->error_code = PHP_JSON_ERROR_SYNTAX;
FREE_BUFFERS();
return false;
}
@@ -711,8 +701,12 @@ parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int
}
FREE_BUFFERS();
+ if (jp->state == OK && pop(jp, MODE_DONE)) {
+ return true;
+ }
- return jp->state == OK && pop(jp, MODE_DONE);
+ jp->error_code = PHP_JSON_ERROR_SYNTAX;
+ return false;
}
diff --git a/ext/json/JSON_parser.h b/ext/json/JSON_parser.h
index 9aab62a912..0ed7e52a8f 100644
--- a/ext/json/JSON_parser.h
+++ b/ext/json/JSON_parser.h
@@ -12,12 +12,20 @@ typedef struct JSON_parser_struct {
int state;
int depth;
int top;
- int error;
+ int error_code;
int* stack;
zval *the_zstack[JSON_PARSER_MAX_DEPTH];
} * JSON_parser;
+enum error_codes {
+ PHP_JSON_ERROR_NONE = 0,
+ PHP_JSON_ERROR_DEPTH,
+ PHP_JSON_ERROR_STATE_MISMATCH,
+ PHP_JSON_ERROR_CTRL_CHAR,
+ PHP_JSON_ERROR_SYNTAX,
+};
+
extern JSON_parser new_JSON_parser(int depth);
extern int parse_JSON(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC);
extern int free_JSON_parser(JSON_parser jp);
diff --git a/ext/json/json.c b/ext/json/json.c
index e09027576b..b3049d002d 100644
--- a/ext/json/json.c
+++ b/ext/json/json.c
@@ -33,6 +33,7 @@
static PHP_MINFO_FUNCTION(json);
static PHP_FUNCTION(json_encode);
static PHP_FUNCTION(json_decode);
+static PHP_FUNCTION(json_last_error);
static const char digits[] = "0123456789abcdef";
@@ -41,6 +42,8 @@ static const char digits[] = "0123456789abcdef";
#define PHP_JSON_HEX_APOS (1<<2)
#define PHP_JSON_HEX_QUOT (1<<3)
+ZEND_DECLARE_MODULE_GLOBALS(json)
+
/* {{{ arginfo */
ZEND_BEGIN_ARG_INFO_EX(arginfo_json_encode, 0, 0, 1)
ZEND_ARG_INFO(0, value)
@@ -51,15 +54,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_json_decode, 0, 0, 1)
ZEND_ARG_INFO(0, json)
ZEND_ARG_INFO(0, assoc)
ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_json_last_error, 0)
+ZEND_END_ARG_INFO()
/* }}} */
-/* {{{ json_functions[]
- *
- * Every user visible function must have an entry in json_functions[].
- */
+/* {{{ json_functions[] */
static const function_entry json_functions[] = {
PHP_FE(json_encode, arginfo_json_encode)
PHP_FE(json_decode, arginfo_json_decode)
+ PHP_FE(json_last_error, arginfo_json_last_error)
{NULL, NULL, NULL}
};
/* }}} */
@@ -72,17 +76,29 @@ static PHP_MINIT_FUNCTION(json)
REGISTER_LONG_CONSTANT("JSON_HEX_APOS", PHP_JSON_HEX_APOS, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("JSON_HEX_QUOT", PHP_JSON_HEX_QUOT, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT);
+
return SUCCESS;
}
/* }}} */
+/* {{{ PHP_GINIT_FUNCTION
+*/
+static PHP_GINIT_FUNCTION(json)
+{
+ json_globals->error_code = 0;
+}
+/* }}} */
+
/* {{{ json_module_entry
*/
zend_module_entry json_module_entry = {
-#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
-#endif
"json",
json_functions,
PHP_MINIT(json),
@@ -90,10 +106,12 @@ zend_module_entry json_module_entry = {
NULL,
NULL,
PHP_MINFO(json),
-#if ZEND_MODULE_API_NO >= 20010901
PHP_JSON_VERSION,
-#endif
- STANDARD_MODULE_PROPERTIES
+ PHP_MODULE_GLOBALS(json),
+ PHP_GINIT(json),
+ NULL,
+ NULL,
+ STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
@@ -533,10 +551,23 @@ static PHP_FUNCTION(json_decode)
}
FREE_ZVAL(z);
efree(utf16);
+ JSON_G(error_code) = jp->error_code;
free_JSON_parser(jp);
}
/* }}} */
+/* {{{ proto int json_last_error()
+ Returns the error code of the last json_decode(). */
+static PHP_FUNCTION(json_last_error)
+{
+ if (zend_parse_parameters_none() == FAILURE) {
+ return;
+ }
+
+ RETURN_LONG(JSON_G(error_code));
+}
+/* }}} */
+
/*
* Local variables:
* tab-width: 4
diff --git a/ext/json/php_json.h b/ext/json/php_json.h
index dfc9427622..8b86a99f71 100644
--- a/ext/json/php_json.h
+++ b/ext/json/php_json.h
@@ -30,6 +30,16 @@ extern zend_module_entry json_module_entry;
#include "TSRM.h"
#endif
+ZEND_BEGIN_MODULE_GLOBALS(json)
+ int error_code;
+ZEND_END_MODULE_GLOBALS(json)
+
+#ifdef ZTS
+# define JSON_G(v) TSRMG(json_globals_id, zend_json_globals *, v)
+#else
+# define JSON_G(v) (json_globals.v)
+#endif
+
#endif /* PHP_JSON_H */
/*
diff --git a/ext/json/tests/007.phpt b/ext/json/tests/007.phpt
new file mode 100644
index 0000000000..9ee190a24c
--- /dev/null
+++ b/ext/json/tests/007.phpt
@@ -0,0 +1,36 @@
+--TEST--
+json_last_error() tests
+--SKIPIF--
+<?php if (!extension_loaded("json")) print "skip"; ?>
+--FILE--
+<?php
+var_dump(json_decode("[1]"));
+var_dump(json_last_error());
+var_dump(json_decode("[[1]]", false, 2));
+var_dump(json_last_error());
+var_dump(json_decode("[1}"));
+var_dump(json_last_error());
+var_dump(json_decode('["' . chr(0) . 'abcd"]'));
+var_dump(json_last_error());
+var_dump(json_decode("[1"));
+var_dump(json_last_error());
+
+
+echo "Done\n";
+?>
+--EXPECT--
+array(1) {
+ [0]=>
+ int(1)
+}
+int(0)
+NULL
+int(1)
+NULL
+int(2)
+NULL
+int(3)
+NULL
+int(4)
+Done
+