Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 88 additions & 2 deletions ext/json/json.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,12 @@ static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{
}
/* }}} */

PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth) /* {{{ */
/* {{{ */
PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth)
{
php_json_parser parser;

php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth);
php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth, NULL);

if (php_json_yyparse(&parser)) {
php_json_error_code error_code = php_json_parser_error_code(&parser);
Expand All @@ -178,6 +179,31 @@ PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str,
}
/* }}} */

/* {{{ */
PHP_JSON_API zend_result php_is_json_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth)
{
php_json_parser parser;


static const php_json_parser_methods parser_methods = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };

php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth, &parser_methods);

if (php_json_yyparse(&parser)) {
php_json_error_code error_code = php_json_parser_error_code(&parser);
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = error_code;
} else {
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code);
}
RETVAL_FALSE;
return FAILURE;
}

return SUCCESS;
}
/* }}} */

/* {{{ Returns the JSON representation of a value */
PHP_FUNCTION(json_encode)
{
Expand Down Expand Up @@ -216,6 +242,66 @@ PHP_FUNCTION(json_encode)
}
/* }}} */

/* {{{ Validates if an string contains a valid json */
PHP_FUNCTION(is_json)
{
char *str;
size_t str_len;
zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
zend_long options = 0;

ZEND_PARSE_PARAMETERS_START(1, 3)
Z_PARAM_STRING(str, str_len)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(depth)
Z_PARAM_LONG(options)
ZEND_PARSE_PARAMETERS_END();


if (options != 0) {
zend_long tmp_options = options;
tmp_options ^= (PHP_JSON_INVALID_UTF8_IGNORE | PHP_JSON_THROW_ON_ERROR);

if (tmp_options != 0) {
if (!((tmp_options == PHP_JSON_INVALID_UTF8_IGNORE) || (tmp_options == PHP_JSON_THROW_ON_ERROR))) {
zend_argument_value_error(3, "must be a valid flag (JSON_THROW_ON_ERROR, JSON_INVALID_UTF8_IGNORE)");
RETURN_THROWS();
}
}
}

if (!str_len) {
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
} else {
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX);
}

RETURN_FALSE;
}

if (!(options & PHP_JSON_THROW_ON_ERROR)) {
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
}

if (depth <= 0) {
zend_argument_value_error(2, "must be greater than 0");
RETURN_THROWS();
}

if (depth > INT_MAX) {
zend_argument_value_error(2, "must be less than %d", INT_MAX);
RETURN_THROWS();
}

if (php_is_json_ex(return_value, str, str_len, options, depth) == SUCCESS) {
RETURN_TRUE;
}

RETURN_FALSE;
}
/* }}} */

/* {{{ Decodes the JSON representation into a PHP value */
PHP_FUNCTION(json_decode)
{
Expand Down
2 changes: 2 additions & 0 deletions ext/json/json.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ function json_encode(mixed $value, int $flags = 0, int $depth = 512): string|fal

function json_decode(string $json, ?bool $associative = null, int $depth = 512, int $flags = 0): mixed {}

function is_json(string $json, int $depth = 512, int $flags = 0): bool {}

function json_last_error(): int {}

/** @refcount 1 */
Expand Down
10 changes: 9 additions & 1 deletion ext/json/json_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 49 additions & 14 deletions ext/json/json_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,14 @@ object_end:
members:
%empty
{
if ((parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) && parser->methods.object_create == php_json_parser_object_create) {
ZVAL_EMPTY_ARRAY(&$$);
if (parser->methods.object_create) {
if ((parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) && parser->methods.object_create == php_json_parser_object_create) {
ZVAL_EMPTY_ARRAY(&$$);
} else {
parser->methods.object_create(parser, &$$);
}
} else {
parser->methods.object_create(parser, &$$);
ZVAL_EMPTY_ARRAY(&$$);
}
}
| member
Expand All @@ -127,14 +131,17 @@ members:
member:
key ':' value
{
parser->methods.object_create(parser, &$$);
if (parser->methods.object_update(parser, &$$, Z_STR($1), &$3) == FAILURE) {
if (parser->methods.object_create) {
parser->methods.object_create(parser, &$$);
}

if (parser->methods.object_update && parser->methods.object_update(parser, &$$, Z_STR($1), &$3) == FAILURE) {
YYERROR;
}
}
| member ',' key ':' value
{
if (parser->methods.object_update(parser, &$1, Z_STR($3), &$5) == FAILURE) {
if (parser->methods.object_update && parser->methods.object_update(parser, &$1, Z_STR($3), &$5) == FAILURE) {
YYERROR;
}
ZVAL_COPY_VALUE(&$$, &$1);
Expand Down Expand Up @@ -171,10 +178,14 @@ array_end:
elements:
%empty
{
if (parser->methods.array_create == php_json_parser_array_create) {
ZVAL_EMPTY_ARRAY(&$$);
if (parser->methods.array_create) {
if (parser->methods.array_create == php_json_parser_array_create) {
ZVAL_EMPTY_ARRAY(&$$);
} else {
parser->methods.array_create(parser, &$$);
}
} else {
parser->methods.array_create(parser, &$$);
ZVAL_EMPTY_ARRAY(&$$);
}
}
| element
Expand All @@ -183,12 +194,19 @@ elements:
element:
value
{
parser->methods.array_create(parser, &$$);
parser->methods.array_append(parser, &$$, &$1);
if (parser->methods.array_create) {
parser->methods.array_create(parser, &$$);
}

if (parser->methods.array_append) {
parser->methods.array_append(parser, &$$, &$1);
}
}
| element ',' value
{
parser->methods.array_append(parser, &$1, &$3);
if (parser->methods.array_append) {
parser->methods.array_append(parser, &$1, &$3);
}
ZVAL_COPY_VALUE(&$$, &$1);
}
;
Expand Down Expand Up @@ -259,6 +277,18 @@ static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser)
{
int token = php_json_scan(&parser->scanner);
value->value = parser->scanner.value;

if (!parser->methods.array_create
&& !parser->methods.array_append
&& !parser->methods.array_start
&& !parser->methods.array_end
&& !parser->methods.object_create
&& !parser->methods.object_update
&& !parser->methods.object_start
&& !parser->methods.object_end) {
zval_ptr_dtor_str(&(parser->scanner.value));
}

return token;
}

Expand Down Expand Up @@ -307,16 +337,21 @@ PHP_JSON_API void php_json_parser_init(php_json_parser *parser,
const char *str,
size_t str_len,
int options,
int max_depth)
int max_depth,
const php_json_parser_methods *parser_methods)
{
if (parser_methods == NULL) {
parser_methods = &default_parser_methods;
}

php_json_parser_init_ex(
parser,
return_value,
str,
str_len,
options,
max_depth,
&default_parser_methods);
parser_methods);
}

PHP_JSON_API int php_json_parse(php_json_parser *parser)
Expand Down
7 changes: 5 additions & 2 deletions ext/json/php_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,13 @@ typedef enum {
#define PHP_JSON_PRESERVE_ZERO_FRACTION (1<<10)
#define PHP_JSON_UNESCAPED_LINE_TERMINATORS (1<<11)

/* json_decode() and json_encode() common options */
/* is_json(), json_decode() and json_encode() common options */
#define PHP_JSON_INVALID_UTF8_IGNORE (1<<20)
#define PHP_JSON_INVALID_UTF8_SUBSTITUTE (1<<21)
#define PHP_JSON_THROW_ON_ERROR (1<<22)

/* json_decode() and json_encode() common options */
#define PHP_JSON_INVALID_UTF8_SUBSTITUTE (1<<21)

/* Internal flags */
#define PHP_JSON_OUTPUT_ARRAY 0
#define PHP_JSON_OUTPUT_OBJECT 1
Expand All @@ -100,6 +102,7 @@ ZEND_TSRMLS_CACHE_EXTERN()
PHP_JSON_API zend_result php_json_encode_ex(smart_str *buf, zval *val, int options, zend_long depth);
PHP_JSON_API zend_result php_json_encode(smart_str *buf, zval *val, int options);
PHP_JSON_API zend_result php_json_decode_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth);
PHP_JSON_API zend_result php_is_json_ex(zval *return_value, const char *str, size_t str_len, zend_long options, zend_long depth);

static inline zend_result php_json_decode(zval *return_value, const char *str, size_t str_len, bool assoc, zend_long depth)
{
Expand Down
3 changes: 2 additions & 1 deletion ext/json/php_json_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ PHP_JSON_API void php_json_parser_init(
const char *str,
size_t str_len,
int options,
int max_depth);
int max_depth,
const php_json_parser_methods *parser_methods);

PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser);

Expand Down
41 changes: 41 additions & 0 deletions ext/json/tests/is_json_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
--TEST--
is_json() - General usage
--FILE--
<?php

var_dump(
is_json(""),
is_json("."),
is_json("<?>"),
is_json(";"),
is_json("руссиш"),
is_json("blah"),
is_json('{ "": "": "" } }'),
is_json('{ "": { "": "" }'),
is_json('{ "test": {} "foo": "bar" }, "test2": {"foo" : "bar" }, "test2": {"foo" : "bar" } }'),

is_json('{ "test": { "foo": "bar" } }'),
is_json('{ "test": { "foo": "" } }'),
is_json('{ "": { "foo": "" } }'),
is_json('{ "": { "": "" } }'),
is_json('{ "test": {"foo": "bar"}, "test2": {"foo" : "bar" }, "test2": {"foo" : "bar" } }'),
is_json('{ "test": {"foo": "bar"}, "test2": {"foo" : "bar" }, "test3": {"foo" : "bar" } }'),
);

?>
--EXPECTF--
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
Loading