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

* Include from build dir first This fixes out of tree builds by ensuring that configure artifacts are included from the build dir. Before, out of tree builds would preferably include files from the src dir, as the include path was defined as follows (ignoring includes from ext/ and sapi/) : -I$(top_builddir)/main -I$(top_srcdir) -I$(top_builddir)/TSRM -I$(top_builddir)/Zend -I$(top_srcdir)/main -I$(top_srcdir)/Zend -I$(top_srcdir)/TSRM -I$(top_builddir)/ As a result, an out of tree build would include configure artifacts such as `main/php_config.h` from the src dir. After this change, the include path is defined as follows: -I$(top_builddir)/main -I$(top_builddir) -I$(top_srcdir)/main -I$(top_srcdir) -I$(top_builddir)/TSRM -I$(top_builddir)/Zend -I$(top_srcdir)/Zend -I$(top_srcdir)/TSRM * Fix extension include path for out of tree builds * Include config.h with the brackets form `#include "config.h"` searches in the directory containing the including-file before any other include path. This can include the wrong config.h when building out of tree and a config.h exists in the source tree. Using `#include <config.h>` uses exclusively the include path, and gives priority to the build dir.
369 lines
9.8 KiB
C
369 lines
9.8 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| 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: Omar Kilani <omar@php.net> |
|
|
| Jakub Zelenka <bukka@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "php.h"
|
|
#include "ext/standard/info.h"
|
|
#include "zend_smart_str.h"
|
|
#include "php_json.h"
|
|
#include "php_json_encoder.h"
|
|
#include "php_json_parser.h"
|
|
#include "json_arginfo.h"
|
|
#include <zend_exceptions.h>
|
|
|
|
static PHP_MINFO_FUNCTION(json);
|
|
|
|
PHP_JSON_API zend_class_entry *php_json_serializable_ce;
|
|
PHP_JSON_API zend_class_entry *php_json_exception_ce;
|
|
|
|
PHP_JSON_API ZEND_DECLARE_MODULE_GLOBALS(json)
|
|
|
|
static int php_json_implement_json_serializable(zend_class_entry *interface, zend_class_entry *class_type)
|
|
{
|
|
class_type->ce_flags |= ZEND_ACC_USE_GUARDS;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* {{{ MINIT */
|
|
static PHP_MINIT_FUNCTION(json)
|
|
{
|
|
php_json_serializable_ce = register_class_JsonSerializable();
|
|
php_json_serializable_ce->interface_gets_implemented = php_json_implement_json_serializable;
|
|
|
|
php_json_exception_ce = register_class_JsonException(zend_ce_exception);
|
|
|
|
register_json_symbols(module_number);
|
|
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_GINIT_FUNCTION */
|
|
static PHP_GINIT_FUNCTION(json)
|
|
{
|
|
#if defined(COMPILE_DL_JSON) && defined(ZTS)
|
|
ZEND_TSRMLS_CACHE_UPDATE();
|
|
#endif
|
|
json_globals->encoder_depth = 0;
|
|
json_globals->error_code = 0;
|
|
json_globals->encode_max_depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
|
|
}
|
|
/* }}} */
|
|
|
|
static PHP_RINIT_FUNCTION(json)
|
|
{
|
|
JSON_G(error_code) = 0;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/* {{{ json_module_entry */
|
|
zend_module_entry json_module_entry = {
|
|
STANDARD_MODULE_HEADER,
|
|
"json",
|
|
ext_functions,
|
|
PHP_MINIT(json),
|
|
NULL,
|
|
PHP_RINIT(json),
|
|
NULL,
|
|
PHP_MINFO(json),
|
|
PHP_JSON_VERSION,
|
|
PHP_MODULE_GLOBALS(json),
|
|
PHP_GINIT(json),
|
|
NULL,
|
|
NULL,
|
|
STANDARD_MODULE_PROPERTIES_EX
|
|
};
|
|
/* }}} */
|
|
|
|
#ifdef COMPILE_DL_JSON
|
|
#ifdef ZTS
|
|
ZEND_TSRMLS_CACHE_DEFINE()
|
|
#endif
|
|
ZEND_GET_MODULE(json)
|
|
#endif
|
|
|
|
/* {{{ PHP_MINFO_FUNCTION */
|
|
static PHP_MINFO_FUNCTION(json)
|
|
{
|
|
php_info_print_table_start();
|
|
php_info_print_table_row(2, "json support", "enabled");
|
|
php_info_print_table_end();
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_JSON_API zend_string *php_json_encode_string(const char *s, size_t len, int options)
|
|
{
|
|
smart_str buf = {0};
|
|
php_json_encoder encoder;
|
|
|
|
php_json_encode_init(&encoder);
|
|
|
|
if (php_json_escape_string(&buf, s, len, options, &encoder) == FAILURE) {
|
|
smart_str_free(&buf);
|
|
return NULL;
|
|
}
|
|
|
|
return smart_str_extract(&buf);
|
|
}
|
|
|
|
PHP_JSON_API zend_result php_json_encode_ex(smart_str *buf, zval *val, int options, zend_long depth) /* {{{ */
|
|
{
|
|
php_json_encoder encoder;
|
|
zend_result return_code;
|
|
|
|
php_json_encode_init(&encoder);
|
|
encoder.max_depth = depth;
|
|
|
|
return_code = php_json_encode_zval(buf, val, options, &encoder);
|
|
JSON_G(error_code) = encoder.error_code;
|
|
|
|
return return_code;
|
|
}
|
|
/* }}} */
|
|
|
|
PHP_JSON_API zend_result php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */
|
|
{
|
|
return php_json_encode_ex(buf, val, options, JSON_G(encode_max_depth));
|
|
}
|
|
/* }}} */
|
|
|
|
static const char *php_json_get_error_msg(php_json_error_code error_code) /* {{{ */
|
|
{
|
|
switch(error_code) {
|
|
case PHP_JSON_ERROR_NONE:
|
|
return "No error";
|
|
case PHP_JSON_ERROR_DEPTH:
|
|
return "Maximum stack depth exceeded";
|
|
case PHP_JSON_ERROR_STATE_MISMATCH:
|
|
return "State mismatch (invalid or malformed JSON)";
|
|
case PHP_JSON_ERROR_CTRL_CHAR:
|
|
return "Control character error, possibly incorrectly encoded";
|
|
case PHP_JSON_ERROR_SYNTAX:
|
|
return "Syntax error";
|
|
case PHP_JSON_ERROR_UTF8:
|
|
return "Malformed UTF-8 characters, possibly incorrectly encoded";
|
|
case PHP_JSON_ERROR_RECURSION:
|
|
return "Recursion detected";
|
|
case PHP_JSON_ERROR_INF_OR_NAN:
|
|
return "Inf and NaN cannot be JSON encoded";
|
|
case PHP_JSON_ERROR_UNSUPPORTED_TYPE:
|
|
return "Type is not supported";
|
|
case PHP_JSON_ERROR_INVALID_PROPERTY_NAME:
|
|
return "The decoded property name is invalid";
|
|
case PHP_JSON_ERROR_UTF16:
|
|
return "Single unpaired UTF-16 surrogate in unicode escape";
|
|
case PHP_JSON_ERROR_NON_BACKED_ENUM:
|
|
return "Non-backed enums have no default serialization";
|
|
default:
|
|
return "Unknown error";
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
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);
|
|
|
|
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_NULL();
|
|
return FAILURE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ */
|
|
PHP_JSON_API bool php_json_validate_ex(const char *str, size_t str_len, zend_long options, zend_long depth)
|
|
{
|
|
php_json_parser parser;
|
|
zval tmp;
|
|
const php_json_parser_methods* parser_validate_methods = php_json_get_validate_methods();
|
|
php_json_parser_init_ex(&parser, &tmp, str, str_len, (int)options, (int)depth, parser_validate_methods);
|
|
|
|
if (php_json_yyparse(&parser)) {
|
|
php_json_error_code error_code = php_json_parser_error_code(&parser);
|
|
JSON_G(error_code) = error_code;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Returns the JSON representation of a value */
|
|
PHP_FUNCTION(json_encode)
|
|
{
|
|
zval *parameter;
|
|
php_json_encoder encoder;
|
|
smart_str buf = {0};
|
|
zend_long options = 0;
|
|
zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 3)
|
|
Z_PARAM_ZVAL(parameter)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_LONG(options)
|
|
Z_PARAM_LONG(depth)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
php_json_encode_init(&encoder);
|
|
encoder.max_depth = (int)depth;
|
|
php_json_encode_zval(&buf, parameter, (int)options, &encoder);
|
|
|
|
if (!(options & PHP_JSON_THROW_ON_ERROR) || (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
|
|
JSON_G(error_code) = encoder.error_code;
|
|
if (encoder.error_code != PHP_JSON_ERROR_NONE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) {
|
|
smart_str_free(&buf);
|
|
RETURN_FALSE;
|
|
}
|
|
} else {
|
|
if (encoder.error_code != PHP_JSON_ERROR_NONE) {
|
|
smart_str_free(&buf);
|
|
zend_throw_exception(php_json_exception_ce, php_json_get_error_msg(encoder.error_code), encoder.error_code);
|
|
RETURN_THROWS();
|
|
}
|
|
}
|
|
|
|
RETURN_STR(smart_str_extract(&buf));
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Decodes the JSON representation into a PHP value */
|
|
PHP_FUNCTION(json_decode)
|
|
{
|
|
char *str;
|
|
size_t str_len;
|
|
bool assoc = 0; /* return JS objects as PHP objects by default */
|
|
bool assoc_null = 1;
|
|
zend_long depth = PHP_JSON_PARSER_DEFAULT_DEPTH;
|
|
zend_long options = 0;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 4)
|
|
Z_PARAM_STRING(str, str_len)
|
|
Z_PARAM_OPTIONAL
|
|
Z_PARAM_BOOL_OR_NULL(assoc, assoc_null)
|
|
Z_PARAM_LONG(depth)
|
|
Z_PARAM_LONG(options)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
if (!(options & PHP_JSON_THROW_ON_ERROR)) {
|
|
JSON_G(error_code) = PHP_JSON_ERROR_NONE;
|
|
}
|
|
|
|
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_NULL();
|
|
}
|
|
|
|
if (depth <= 0) {
|
|
zend_argument_value_error(3, "must be greater than 0");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (depth > INT_MAX) {
|
|
zend_argument_value_error(3, "must be less than %d", INT_MAX);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
/* For BC reasons, the bool $assoc overrides the long $options bit for PHP_JSON_OBJECT_AS_ARRAY */
|
|
if (!assoc_null) {
|
|
if (assoc) {
|
|
options |= PHP_JSON_OBJECT_AS_ARRAY;
|
|
} else {
|
|
options &= ~PHP_JSON_OBJECT_AS_ARRAY;
|
|
}
|
|
}
|
|
|
|
php_json_decode_ex(return_value, str, str_len, options, depth);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Validates if a string contains a valid json */
|
|
PHP_FUNCTION(json_validate)
|
|
{
|
|
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) && (options != PHP_JSON_INVALID_UTF8_IGNORE)) {
|
|
zend_argument_value_error(3, "must be a valid flag (allowed flags: JSON_INVALID_UTF8_IGNORE)");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (!str_len) {
|
|
JSON_G(error_code) = PHP_JSON_ERROR_SYNTAX;
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
RETURN_BOOL(php_json_validate_ex(str, str_len, options, depth));
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Returns the error code of the last json_encode() or json_decode() call. */
|
|
PHP_FUNCTION(json_last_error)
|
|
{
|
|
ZEND_PARSE_PARAMETERS_NONE();
|
|
|
|
RETURN_LONG(JSON_G(error_code));
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ Returns the error string of the last json_encode() or json_decode() call. */
|
|
PHP_FUNCTION(json_last_error_msg)
|
|
{
|
|
ZEND_PARSE_PARAMETERS_NONE();
|
|
|
|
RETURN_STRING(php_json_get_error_msg(JSON_G(error_code)));
|
|
}
|
|
/* }}} */
|