mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00

Use the shared empty array from ZVAL_EMPTY_ARRAY For code that created an 10 arrays of 100000 empty arrays (has the same result with `$assoc=true` and `{}`) - This is the worst-case comparison, but I'd expect 0-length arrays to be fairly common in regular data for json_decode - The parser implementation was using function pointers so that third party extension developers could reuse the json parser for their own data structures, etc. (I think). This PR is meant to let those third party extensions continue working without changes. Before this patch: In 0.126 seconds: added 97.99 MiB After this patch: In 0.096 seconds: added 41.99 MiB ```php <?php $json = '[' . str_repeat('[],', 100000) . "null]"; $start_memory = memory_get_usage(); $start_time = microtime(true); $result = []; for ($i = 0; $i < 10; $i++) { $result[] = json_decode($json); } $end_memory = memory_get_usage(); $end_time = microtime(true); // Before this patch: In 0.126 seconds: added 97.99 MiB // After this patch: In 0.096 seconds: added 41.99 MiB printf("In %.3f seconds: added %.2f MiB\n", $end_time - $start_time, ($end_memory - $start_memory)/1000000); // For objects $json = '[' . str_repeat('{},', 100000) . "null]"; $start_memory = memory_get_usage(); $start_time = microtime(true); for ($i = 0; $i < 10; $i++) { $result[] = json_decode($json, true); } $end_memory = memory_get_usage(); $end_time = microtime(true); // Before this patch: In 0.126 seconds: added 97.99 MiB // After this patch: In 0.096 seconds: added 41.99 MiB printf("In %.3f seconds: added %.2f MiB (objects decoded as arrays) \n", $end_time - $start_time, ($end_memory - $start_memory)/1000000); ``` Closes GH-4861.
342 lines
7.7 KiB
Text
342 lines
7.7 KiB
Text
%code top {
|
|
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 7 |
|
|
+----------------------------------------------------------------------+
|
|
| 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: |
|
|
| http://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 <bukka@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#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.pure full
|
|
%define api.prefix {php_json_yy}
|
|
%lex-param { php_json_parser *parser }
|
|
%parse-param { php_json_parser *parser }
|
|
|
|
%union {
|
|
zval value;
|
|
struct {
|
|
zend_string *key;
|
|
zval val;
|
|
} pair;
|
|
}
|
|
|
|
|
|
%token <value> PHP_JSON_T_NUL
|
|
%token <value> PHP_JSON_T_TRUE
|
|
%token <value> PHP_JSON_T_FALSE
|
|
%token <value> PHP_JSON_T_INT
|
|
%token <value> PHP_JSON_T_DOUBLE
|
|
%token <value> PHP_JSON_T_STRING
|
|
%token <value> PHP_JSON_T_ESTRING
|
|
%token PHP_JSON_T_EOI
|
|
%token PHP_JSON_T_ERROR
|
|
|
|
%type <value> start object key value array
|
|
%type <value> members member elements element
|
|
%type <pair> pair
|
|
|
|
%destructor { zval_ptr_dtor_nogc(&$$); } <value>
|
|
%destructor { zend_string_release_ex($$.key, 0); zval_ptr_dtor_nogc(&$$.val); } <pair>
|
|
|
|
%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:
|
|
pair
|
|
{
|
|
parser->methods.object_create(parser, &$$);
|
|
if (parser->methods.object_update(parser, &$$, $1.key, &$1.val) == FAILURE) {
|
|
YYERROR;
|
|
}
|
|
}
|
|
| member ',' pair
|
|
{
|
|
if (parser->methods.object_update(parser, &$1, $3.key, &$3.val) == FAILURE) {
|
|
YYERROR;
|
|
}
|
|
ZVAL_COPY_VALUE(&$$, &$1);
|
|
}
|
|
;
|
|
|
|
pair:
|
|
key ':' value
|
|
{
|
|
$$.key = Z_STR($1);
|
|
ZVAL_COPY_VALUE(&$$.val, &$3);
|
|
}
|
|
;
|
|
|
|
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);
|
|
return SUCCESS;
|
|
} else {
|
|
return object_init(object);
|
|
}
|
|
}
|
|
|
|
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 {
|
|
zval zkey;
|
|
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;
|
|
}
|
|
ZVAL_NEW_STR(&zkey, key);
|
|
zend_std_write_property(object, &zkey, 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,
|
|
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,
|
|
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);
|
|
}
|