%require "3.0" %code top { /* +----------------------------------------------------------------------+ | Copyright (c) The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | https://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Jakub Zelenka | +----------------------------------------------------------------------+ */ #include "php.h" #include "php_json.h" #include "php_json_parser.h" #define YYDEBUG 0 #if YYDEBUG int json_yydebug = 1; #endif #ifdef _MSC_VER #define YYMALLOC malloc #define YYFREE free #endif #define PHP_JSON_DEPTH_DEC --parser->depth #define PHP_JSON_DEPTH_INC \ if (parser->max_depth && parser->depth >= parser->max_depth) { \ parser->scanner.errcode = PHP_JSON_ERROR_DEPTH; \ YYERROR; \ } \ ++parser->depth } %define api.prefix {php_json_yy} %define api.pure full %param { php_json_parser *parser } %union { zval value; } %token PHP_JSON_T_NUL %token PHP_JSON_T_TRUE %token PHP_JSON_T_FALSE %token PHP_JSON_T_INT %token PHP_JSON_T_DOUBLE %token PHP_JSON_T_STRING %token PHP_JSON_T_ESTRING %token PHP_JSON_T_EOI %token PHP_JSON_T_ERROR %type start object key value array %type members member elements element %destructor { zval_ptr_dtor_nogc(&$$); } %code { static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser); static void php_json_yyerror(php_json_parser *parser, char const *msg); static int php_json_parser_array_create(php_json_parser *parser, zval *array); static int php_json_parser_object_create(php_json_parser *parser, zval *array); } %% /* Rules */ start: value PHP_JSON_T_EOI { ZVAL_COPY_VALUE(&$$, &$1); ZVAL_COPY_VALUE(parser->return_value, &$1); YYACCEPT; } ; object: '{' { PHP_JSON_DEPTH_INC; if (parser->methods.object_start && FAILURE == parser->methods.object_start(parser)) { YYERROR; } } members object_end { ZVAL_COPY_VALUE(&$$, &$3); PHP_JSON_DEPTH_DEC; if (parser->methods.object_end && FAILURE == parser->methods.object_end(parser, &$$)) { YYERROR; } } ; object_end: '}' | ']' { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } ; members: %empty { 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, &$$); } } | member ; member: key ':' value { parser->methods.object_create(parser, &$$); if (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) { YYERROR; } ZVAL_COPY_VALUE(&$$, &$1); } ; array: '[' { PHP_JSON_DEPTH_INC; if (parser->methods.array_start && FAILURE == parser->methods.array_start(parser)) { YYERROR; } } elements array_end { ZVAL_COPY_VALUE(&$$, &$3); PHP_JSON_DEPTH_DEC; if (parser->methods.array_end && FAILURE == parser->methods.array_end(parser, &$$)) { YYERROR; } } ; array_end: ']' | '}' { parser->scanner.errcode = PHP_JSON_ERROR_STATE_MISMATCH; YYERROR; } ; elements: %empty { if (parser->methods.array_create == php_json_parser_array_create) { ZVAL_EMPTY_ARRAY(&$$); } else { parser->methods.array_create(parser, &$$); } } | element ; element: value { parser->methods.array_create(parser, &$$); parser->methods.array_append(parser, &$$, &$1); } | element ',' value { parser->methods.array_append(parser, &$1, &$3); ZVAL_COPY_VALUE(&$$, &$1); } ; key: PHP_JSON_T_STRING | PHP_JSON_T_ESTRING ; value: object | array | PHP_JSON_T_STRING | PHP_JSON_T_ESTRING | PHP_JSON_T_INT | PHP_JSON_T_DOUBLE | PHP_JSON_T_NUL | PHP_JSON_T_TRUE | PHP_JSON_T_FALSE ; %% /* Functions */ static int php_json_parser_array_create(php_json_parser *parser, zval *array) { array_init(array); return SUCCESS; } static int php_json_parser_array_append(php_json_parser *parser, zval *array, zval *zvalue) { zend_hash_next_index_insert(Z_ARRVAL_P(array), zvalue); return SUCCESS; } static int php_json_parser_object_create(php_json_parser *parser, zval *object) { if (parser->scanner.options & PHP_JSON_OBJECT_AS_ARRAY) { array_init(object); } else { object_init(object); } return SUCCESS; } static int php_json_parser_object_update(php_json_parser *parser, zval *object, zend_string *key, zval *zvalue) { /* if JSON_OBJECT_AS_ARRAY is set */ if (Z_TYPE_P(object) == IS_ARRAY) { zend_symtable_update(Z_ARRVAL_P(object), key, zvalue); } else { if (ZSTR_LEN(key) > 0 && ZSTR_VAL(key)[0] == '\0') { parser->scanner.errcode = PHP_JSON_ERROR_INVALID_PROPERTY_NAME; zend_string_release_ex(key, 0); zval_ptr_dtor_nogc(zvalue); zval_ptr_dtor_nogc(object); return FAILURE; } zend_std_write_property(Z_OBJ_P(object), key, zvalue, NULL); Z_TRY_DELREF_P(zvalue); } zend_string_release_ex(key, 0); return SUCCESS; } static int php_json_yylex(union YYSTYPE *value, php_json_parser *parser) { int token = php_json_scan(&parser->scanner); value->value = parser->scanner.value; return token; } static void php_json_yyerror(php_json_parser *parser, char const *msg) { if (!parser->scanner.errcode) { parser->scanner.errcode = PHP_JSON_ERROR_SYNTAX; } } PHP_JSON_API php_json_error_code php_json_parser_error_code(const php_json_parser *parser) { return parser->scanner.errcode; } static const php_json_parser_methods default_parser_methods = { php_json_parser_array_create, php_json_parser_array_append, NULL, NULL, php_json_parser_object_create, php_json_parser_object_update, NULL, NULL, }; PHP_JSON_API void php_json_parser_init_ex(php_json_parser *parser, zval *return_value, const char *str, size_t str_len, int options, int max_depth, const php_json_parser_methods *parser_methods) { memset(parser, 0, sizeof(php_json_parser)); php_json_scanner_init(&parser->scanner, str, str_len, options); parser->depth = 1; parser->max_depth = max_depth; parser->return_value = return_value; memcpy(&parser->methods, parser_methods, sizeof(php_json_parser_methods)); } PHP_JSON_API void php_json_parser_init(php_json_parser *parser, zval *return_value, const char *str, size_t str_len, int options, int max_depth) { php_json_parser_init_ex( parser, return_value, str, str_len, options, max_depth, &default_parser_methods); } PHP_JSON_API int php_json_parse(php_json_parser *parser) { return php_json_yyparse(parser); }