Coverage Report

Created: 2025-06-13 06:43

/src/php-src/ext/json/json.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
  +----------------------------------------------------------------------+
3
  | Copyright (c) The PHP Group                                          |
4
  +----------------------------------------------------------------------+
5
  | This source file is subject to version 3.01 of the PHP license,      |
6
  | that is bundled with this package in the file LICENSE, and is        |
7
  | available through the world-wide-web at the following url:           |
8
  | https://www.php.net/license/3_01.txt                                 |
9
  | If you did not receive a copy of the PHP license and are unable to   |
10
  | obtain it through the world-wide-web, please send a note to          |
11
  | license@php.net so we can mail you a copy immediately.               |
12
  +----------------------------------------------------------------------+
13
  | Author: Omar Kilani <omar@php.net>                                   |
14
  |         Jakub Zelenka <bukka@php.net>                                |
15
  +----------------------------------------------------------------------+
16
*/
17
18
#ifdef HAVE_CONFIG_H
19
#include <config.h>
20
#endif
21
22
#include "php.h"
23
#include "ext/standard/info.h"
24
#include "zend_smart_str.h"
25
#include "php_json.h"
26
#include "php_json_encoder.h"
27
#include "php_json_parser.h"
28
#include "json_arginfo.h"
29
#include <zend_exceptions.h>
30
31
static PHP_MINFO_FUNCTION(json);
32
33
PHP_JSON_API zend_class_entry *php_json_serializable_ce;
34
PHP_JSON_API zend_class_entry *php_json_exception_ce;
35
36
PHP_JSON_API ZEND_DECLARE_MODULE_GLOBALS(json)
37
38
static int php_json_implement_json_serializable(zend_class_entry *interface, zend_class_entry *class_type)
39
78
{
40
78
  class_type->ce_flags |= ZEND_ACC_USE_GUARDS;
41
78
  return SUCCESS;
42
78
}
43
44
/* {{{ MINIT */
45
static PHP_MINIT_FUNCTION(json)
46
16
{
47
16
  php_json_serializable_ce = register_class_JsonSerializable();
48
16
  php_json_serializable_ce->interface_gets_implemented = php_json_implement_json_serializable;
49
50
16
  php_json_exception_ce = register_class_JsonException(zend_ce_exception);
51
52
16
  register_json_symbols(module_number);
53
54
16
  return SUCCESS;
55
16
}
56
/* }}} */
57
58
/* {{{ PHP_GINIT_FUNCTION */
59
static PHP_GINIT_FUNCTION(json)
60
16
{
61
#if defined(COMPILE_DL_JSON) && defined(ZTS)
62
  ZEND_TSRMLS_CACHE_UPDATE();
63
#endif
64
16
  json_globals->encoder_depth = 0;
65
16
  json_globals->error_code = 0;
66
16
  json_globals->encode_max_depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
67
16
}
68
/* }}} */
69
70
static PHP_RINIT_FUNCTION(json)
71
300k
{
72
300k
  JSON_G(error_code) = 0;
73
300k
  return SUCCESS;
74
300k
}
75
76
/* {{{ json_module_entry */
77
zend_module_entry json_module_entry = {
78
  STANDARD_MODULE_HEADER,
79
  "json",
80
  ext_functions,
81
  PHP_MINIT(json),
82
  NULL,
83
  PHP_RINIT(json),
84
  NULL,
85
  PHP_MINFO(json),
86
  PHP_JSON_VERSION,
87
  PHP_MODULE_GLOBALS(json),
88
  PHP_GINIT(json),
89
  NULL,
90
  NULL,
91
  STANDARD_MODULE_PROPERTIES_EX
92
};
93
/* }}} */
94
95
#ifdef COMPILE_DL_JSON
96
#ifdef ZTS
97
ZEND_TSRMLS_CACHE_DEFINE()
98
#endif
99
ZEND_GET_MODULE(json)
100
#endif
101
102
/* {{{ PHP_MINFO_FUNCTION */
103
static PHP_MINFO_FUNCTION(json)
104
5
{
105
5
  php_info_print_table_start();
106
5
  php_info_print_table_row(2, "json support", "enabled");
107
5
  php_info_print_table_end();
108
5
}
109
/* }}} */
110
111
PHP_JSON_API zend_string *php_json_encode_string(const char *s, size_t len, int options)
112
0
{
113
0
  smart_str buf = {0};
114
0
  php_json_encoder encoder;
115
116
0
  php_json_encode_init(&encoder);
117
118
0
  if (php_json_escape_string(&buf, s, len, options, &encoder) == FAILURE) {
119
0
    smart_str_free(&buf);
120
0
    return NULL;
121
0
  }
122
123
0
  return smart_str_extract(&buf);
124
0
}
125
126
PHP_JSON_API zend_result php_json_encode_ex(smart_str *buf, zval *val, int options, zend_long depth) /* {{{ */
127
0
{
128
0
  php_json_encoder encoder;
129
0
  zend_result return_code;
130
131
0
  php_json_encode_init(&encoder);
132
0
  encoder.max_depth = depth;
133
134
0
  return_code = php_json_encode_zval(buf, val, options, &encoder);
135
0
  JSON_G(error_code) = encoder.error_code;
136
137
0
  return return_code;
138
0
}
139
/* }}} */
140
141
PHP_JSON_API zend_result php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */
142
0
{
143
0
  return php_json_encode_ex(buf, val, options, JSON_G(encode_max_depth));
144
0
}
145
/* }}} */
146
147
static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */
148
296
{
149
296
  switch(error_code) {
150
151
    case PHP_JSON_ERROR_NONE:
151
151
      return "No error";
152
0
    case PHP_JSON_ERROR_DEPTH:
153
0
      return "Maximum stack depth exceeded";
154
0
    case PHP_JSON_ERROR_STATE_MISMATCH:
155
0
      return "State mismatch (invalid or malformed JSON)";
156
0
    case PHP_JSON_ERROR_CTRL_CHAR:
157
0
      return "Control character error, possibly incorrectly encoded";
158
0
    case PHP_JSON_ERROR_SYNTAX:
159
0
      return "Syntax error";
160
37
    case PHP_JSON_ERROR_UTF8:
161
37
      return "Malformed UTF-8 characters, possibly incorrectly encoded";
162
0
    case PHP_JSON_ERROR_RECURSION:
163
0
      return "Recursion detected";
164
0
    case PHP_JSON_ERROR_INF_OR_NAN:
165
0
      return "Inf and NaN cannot be JSON encoded";
166
0
    case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
167
0
      return "Type is not supported";
168
0
    case PHP_JSON_ERROR_INVALID_PROPERTY_NAME:
169
0
      return "The decoded property name is invalid";
170
0
    case PHP_JSON_ERROR_UTF16:
171
0
      return "Single unpaired UTF-16 surrogate in unicode escape";
172
108
    case PHP_JSON_ERROR_NON_BACKED_ENUM:
173
108
      return "Non-backed enums have no default serialization";
174
0
    default:
175
0
      return "Unknown error";
176
296
  }
177
296
}
178
/* }}} */
179
180
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) /* {{{ */
181
20
{
182
20
  php_json_parser parser;
183
184
20
  php_json_parser_init(&parser, return_value, str, str_len, (int)options, (int)depth);
185
186
20
  if (php_json_yyparse(&parser)) {
187
0
    php_json_error_code error_code = php_json_parser_error_code(&parser);
188
0
    if (!(options & PHP_JSON_THROW_ON_ERROR)) {
189
0
      JSON_G(error_code) = error_code;
190
0
    } else {
191
0
      zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(error_code), error_code);
192
0
    }
193
0
    RETVAL_NULL();
194
0
    return FAILURE;
195
0
  }
196
197
20
  return SUCCESS;
198
20
}
199
/* }}} */
200
201
/* {{{ */
202
PHP_JSON_API bool php_json_validate_ex(const char *str, size_t str_len, zend_long options, zend_long depth)
203
0
{
204
0
  php_json_parser parser;
205
0
  zval tmp;
206
0
  const php_json_parser_methods* parser_validate_methods = php_json_get_validate_methods();
207
0
  php_json_parser_init_ex(&parser, &tmp, str, str_len, (int)options, (int)depth, parser_validate_methods);
208
209
0
  if (php_json_yyparse(&parser)) {
210
0
    php_json_error_code error_code = php_json_parser_error_code(&parser);
211
0
    JSON_G(error_code) = error_code;
212
0
    return false;
213
0
  }
214
215
0
  return true;
216
0
}
217
/* }}} */
218
219
/* {{{ Returns the JSON representation of a value */
220
PHP_FUNCTION(json_encode)
221
3.10k
{
222
3.10k
  zval *parameter;
223
3.10k
  php_json_encoder encoder;
224
3.10k
  smart_str buf = {0};
225
3.10k
  zend_long options = 0;
226
3.10k
  zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
227
228
9.30k
  ZEND_PARSE_PARAMETERS_START(1, 3)
229
12.4k
    Z_PARAM_ZVAL(parameter)
230
12.4k
    Z_PARAM_OPTIONAL
231
12.4k
    Z_PARAM_LONG(options)
232
4.64k
    Z_PARAM_LONG(depth)
233
3.10k
  ZEND_PARSE_PARAMETERS_END();
234
235
3.10k
  php_json_encode_init(&encoder);
236
3.10k
  encoder.max_depth = (int)depth;
237
3.10k
  php_json_encode_zval(&buf, parameter, (int)options, &encoder);
238
239
3.10k
  if (!(options & PHP_JSON_THROW_ON_ERROR) || (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
240
3.06k
    JSON_G(error_code) = encoder.error_code;
241
3.06k
    if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
242
122
      smart_str_free(&buf);
243
122
      RETURN_FALSE;
244
122
    }
245
3.06k
  } else {
246
41
    if (encoder.error_code != PHP_JSON_ERROR_NONE) {
247
15
      smart_str_free(&buf);
248
15
      zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(encoder.error_code), encoder.error_code);
249
15
      RETURN_THROWS();
250
15
    }
251
41
  }
252
253
2.96k
  RETURN_STR(smart_str_extract(&buf));
254
2.96k
}
255
/* }}} */
256
257
/* {{{ Decodes the JSON representation into a PHP value */
258
PHP_FUNCTION(json_decode)
259
20
{
260
20
  char *str;
261
20
  size_t str_len;
262
20
  bool assoc = 0; /* return JS objects as PHP objects by default */
263
20
  bool assoc_null = 1;
264
20
  zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
265
20
  zend_long options = 0;
266
267
60
  ZEND_PARSE_PARAMETERS_START(1, 4)
268
80
    Z_PARAM_STRING(str, str_len)
269
20
    Z_PARAM_OPTIONAL
270
40
    Z_PARAM_BOOL_OR_NULL(assoc, assoc_null)
271
0
    Z_PARAM_LONG(depth)
272
0
    Z_PARAM_LONG(options)
273
20
  ZEND_PARSE_PARAMETERS_END();
274
275
20
  if (!(options & PHP_JSON_THROW_ON_ERROR)) {
276
20
    JSON_G(error_code) = PHP_JSON_ERROR_NONE;
277
20
  }
278
279
20
  if (!str_len) {
280
0
    if (!(options & PHP_JSON_THROW_ON_ERROR)) {
281
0
      JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
282
0
    } else {
283
0
      zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(PHP_JSON_ERROR_SYNTAX), PHP_JSON_ERROR_SYNTAX);
284
0
    }
285
0
    RETURN_NULL();
286
0
  }
287
288
20
  if (depth <= 0) {
289
0
    zend_argument_value_error(3, "must be greater than 0");
290
0
    RETURN_THROWS();
291
0
  }
292
293
20
  if (depth > INT_MAX) {
294
0
    zend_argument_value_error(3, "must be less than %d", INT_MAX);
295
0
    RETURN_THROWS();
296
0
  }
297
298
  /* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
299
20
  if (!assoc_null) {
300
0
    if (assoc) {
301
0
      options |=  PHP_JSON_OBJECT_AS_ARRAY;
302
0
    } else {
303
0
      options &= ~PHP_JSON_OBJECT_AS_ARRAY;
304
0
    }
305
0
  }
306
307
20
  php_json_decode_ex(return_value, str, str_len, options, depth);
308
20
}
309
/* }}} */
310
311
/* {{{ Validates if a string contains a valid json */
312
PHP_FUNCTION(json_validate)
313
0
{
314
0
  char *str;
315
0
  size_t str_len;
316
0
  zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
317
0
  zend_long options = 0;
318
319
0
  ZEND_PARSE_PARAMETERS_START(1, 3)
320
0
    Z_PARAM_STRING(str, str_len)
321
0
    Z_PARAM_OPTIONAL
322
0
    Z_PARAM_LONG(depth)
323
0
    Z_PARAM_LONG(options)
324
0
  ZEND_PARSE_PARAMETERS_END();
325
326
327
0
  if ((options != 0) && (options != PHP_JSON_INVALID_UTF8_IGNORE)) {
328
0
    zend_argument_value_error(3, "must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE)");
329
0
    RETURN_THROWS();
330
0
  }
331
332
0
  if (!str_len) {
333
0
    JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
334
0
    RETURN_FALSE;
335
0
  }
336
337
0
  JSON_G(error_code) = PHP_JSON_ERROR_NONE;
338
339
0
  if (depth <= 0) {
340
0
    zend_argument_value_error(2, "must be greater than 0");
341
0
    RETURN_THROWS();
342
0
  }
343
344
0
  if (depth > INT_MAX) {
345
0
    zend_argument_value_error(2, "must be less than %d", INT_MAX);
346
0
    RETURN_THROWS();
347
0
  }
348
349
0
  RETURN_BOOL(php_json_validate_ex(str, str_len, options, depth));
350
0
}
351
/* }}} */
352
353
/* {{{ Returns the error code of the last json_encode() or json_decode() call. */
354
PHP_FUNCTION(json_last_error)
355
77
{
356
77
  ZEND_PARSE_PARAMETERS_NONE();
357
358
77
  RETURN_LONG(JSON_G(error_code));
359
77
}
360
/* }}} */
361
362
/* {{{ Returns the error string of the last json_encode() or json_decode() call. */
363
PHP_FUNCTION(json_last_error_msg)
364
281
{
365
281
  ZEND_PARSE_PARAMETERS_NONE();
366
367
281
  RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
368
281
}
369
/* }}} */