PHP compat 5-8
[m6w6/ext-json_post] / php_json_post.c
index 6bc8c333a92324dce45e9e11ce0973ce5ba834d2..6922b5107045e7750e928f252157ff7cbb59e72f 100644 (file)
 ZEND_DECLARE_MODULE_GLOBALS(json_post);
 
 PHP_INI_BEGIN()
-    STD_PHP_INI_ENTRY("json_post.flags", "1", PHP_INI_PERDIR, OnUpdateLong, flags, zend_json_post_globals, json_post_globals)
+       STD_PHP_INI_ENTRY("json_post.flags", "1", PHP_INI_PERDIR, OnUpdateLong, flags, zend_json_post_globals, json_post_globals)
+       STD_PHP_INI_ENTRY("json_post.onerror.response", "0", PHP_INI_PERDIR, OnUpdateLong, onerror.response, zend_json_post_globals, json_post_globals)
+       STD_PHP_INI_ENTRY("json_post.onerror.exit", "0", PHP_INI_PERDIR, OnUpdateBool, onerror.exit, zend_json_post_globals, json_post_globals)
+       STD_PHP_INI_ENTRY("json_post.onerror.warning", "0", PHP_INI_PERDIR, OnUpdateBool, onerror.warning, zend_json_post_globals, json_post_globals)
 PHP_INI_END()
 
 static void php_json_post_init_globals(zend_json_post_globals *json_post_globals)
 {
+       memset(json_post_globals, 0, sizeof(*json_post_globals));
+#if PHP_VERSION_ID >= 50400
        json_post_globals->flags = PHP_JSON_OBJECT_AS_ARRAY;
+#else
+       json_post_globals->flags = 1;
+#endif
 }
 
+#ifndef TSRMLS_CC
+#      define TSRMLS_D
+#      define TSRMLS_C
+#      define TSRMLS_CC
+#endif
+
 PHP_MINFO_FUNCTION(json_post)
 {
        php_info_print_table_start();
@@ -42,52 +56,149 @@ PHP_MINFO_FUNCTION(json_post)
        DISPLAY_INI_ENTRIES();
 }
 
+#if PHP_VERSION_ID < 70000
+#      define JSON_POST_LAST_ERROR() json_post_last_error(TSRMLS_C)
+static inline int json_post_last_error(TSRMLS_D)
+{
+       zend_long l;
+       zval *zv, **zv_ptr = &zv;
+
+       ALLOC_ZVAL(zv);
+       JSON_POST_G(json_last_error)->internal_function.handler(0, zv, zv_ptr, NULL, 1);
+       l = Z_LVAL_P(zv);
+       FREE_ZVAL(zv);
+
+       return l;
+}
+#else
+#      define JSON_POST_LAST_ERROR() JSON_G(error_code)
+#endif
+
 static SAPI_POST_HANDLER_FUNC(php_json_post_handler)
 {
-       zval *zarg = arg;
-       char *json_str = NULL;
-       size_t json_len = 0;
+       int module_number = 0;
+
+#if PHP_VERSION_ID >= 70000
+       zend_string *json = NULL;
 
-#if PHP_VERSION_ID >= 50600
        if (SG(request_info).request_body) {
                /* FG(stream_wrappers) not initialized yet, so we cannot use php://input */
                php_stream_rewind(SG(request_info).request_body);
-               json_len = php_stream_copy_to_mem(SG(request_info).request_body, &json_str, PHP_STREAM_COPY_ALL, 0);
+               json = php_stream_copy_to_mem(SG(request_info).request_body, PHP_STREAM_COPY_ALL, 0);
        }
+
+       if (json) {
+               if (json->len) {
+                       zval tmp;
+                       long flags = JSON_POST_G(flags);
+
+#ifdef PHP_JSON_THROW_ON_ERROR
+                       /* there's no execute data, so we must ensure json_decode() is not throwing */
+                       flags &= ~PHP_JSON_THROW_ON_ERROR;
+#endif
+
+                       ZVAL_NULL(&tmp);
+                       php_json_decode_ex(&tmp, json->val, json->len, flags, PG(max_input_nesting_level));
+
+                       switch (Z_TYPE(tmp)) {
+                       case IS_OBJECT:
+                       case IS_ARRAY:
+                               if (zend_hash_num_elements(HASH_OF(&tmp))) {
+                                       zval_dtor(arg);
+                                       ZVAL_COPY_VALUE(&PG(http_globals)[TRACK_VARS_POST], &tmp);
+                               } else {
+                                       /* PHP-7.4 optimizes empty array */
+                                       zval_ptr_dtor(&tmp);
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               }
+               zend_string_release(json);
+       }
+
 #else
+
+       zval *zarg = arg;
+       char *json_str = NULL;
+       size_t json_len = 0;
+
+#      if PHP_VERSION_ID >= 50600
+       if (!SG(request_info).request_body) {
+               return;
+       }
+
+       /* FG(stream_wrappers) not initialized yet, so we cannot use php://input */
+       php_stream_rewind(SG(request_info).request_body);
+       json_len = php_stream_copy_to_mem(SG(request_info).request_body, &json_str, PHP_STREAM_COPY_ALL, 0);
+#      else
        json_str = SG(request_info).raw_post_data;
        json_len = SG(request_info).raw_post_data_length;
-#endif
+#      endif
 
        if (json_len) {
                zval zjson;
 
                INIT_ZVAL(zjson);
+
+#      if PHP_VERSION_ID >= 50400
                php_json_decode_ex(&zjson, json_str, json_len, JSON_POST_G(flags), PG(max_input_nesting_level) TSRMLS_CC);
-                       if (Z_TYPE(zjson) != IS_NULL) {
+               if (Z_TYPE(zjson) != IS_NULL) {
                        zval_dtor(zarg);
                        ZVAL_COPY_VALUE(zarg, (&zjson));
+#      else
+               php_json_decode(&zjson, json_str, json_len, (zend_bool)(JSON_POST_G(flags)&1), PG(max_input_nesting_level) TSRMLS_CC);
+               if (Z_TYPE(zjson) != IS_NULL) {
+                       zval_dtor(zarg);
+                       zarg->value = zjson.value;
+                       Z_TYPE_P(zarg) = Z_TYPE(zjson);
+#      endif
                }
        }
-#if PHP_VERSION_ID >= 50600
+#      if PHP_VERSION_ID >= 50600
        if (json_str) {
                efree(json_str);
        }
+#      endif
 #endif
+
+       REGISTER_LONG_CONSTANT("JSON_POST_ERROR", JSON_POST_LAST_ERROR(), CONST_CS);
+
+       if (JSON_POST_LAST_ERROR()) {
+               if (JSON_POST_G(onerror.response)) {
+                       sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) (zend_long) JSON_POST_G(onerror.response) TSRMLS_CC);
+               }
+               if (JSON_POST_G(onerror.warning)) {
+                       zend_error(E_WARNING, "json_post: json_decode failed with error code: %d", JSON_POST_LAST_ERROR());
+               }
+               if (JSON_POST_G(onerror.exit)) {
+                       sapi_send_headers(TSRMLS_C);
+                       zend_bailout();
+               }
+#if PHP_VERSION_ID >= 70000 && PHP_VERSION_ID < 80000
+               /* ext/json in PHP-7 fails to reset error_code in RINIT */
+               JSON_G(error_code) = 0;
+#endif
+       }
 }
 
 PHP_MINIT_FUNCTION(json_post)
 {
-       sapi_post_entry entry = {NULL, 0, NULL, NULL};
-
-       entry.post_reader = sapi_read_standard_form_data;
-       entry.post_handler = php_json_post_handler;
+       sapi_post_entry json_post_entries[] = {
+               { "text/json", sizeof("text/json")-1, sapi_read_standard_form_data, php_json_post_handler },
+               { "application/json", sizeof("application/json")-1, sapi_read_standard_form_data, php_json_post_handler },
+               { NULL, 0, NULL, NULL }
+       };
 
-       entry.content_type = "text/json";
-       entry.content_type_len = sizeof("text/json")-1;
-       sapi_register_post_entry(&entry TSRMLS_CC);
+       sapi_register_post_entries(json_post_entries TSRMLS_CC);
 
        ZEND_INIT_MODULE_GLOBALS(json_post, php_json_post_init_globals, NULL);
+
+#if PHP_VERSION_ID < 70000
+       zend_hash_find(EG(function_table), ZEND_STRS("json_last_error"), (void **) &JSON_POST_G(json_last_error));
+#endif
+
        REGISTER_INI_ENTRIES();
        return SUCCESS;
 }