From f4e2e91d4b6d28448104500819b68edf58bd263c Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 Aug 2025 21:16:55 +0100 Subject: [PATCH 1/5] core: Remove disable_classes INI setting RFC: https://wiki.php.net/rfc/deprecations_php_8_5#remove_disable_classes_ini_setting This took longer to merge than expected but the initial motivation from 2 years ago still applied: As described in the email to the PHP internals list [1] this feature is fundamentally broken and pointless. Only internal classes can be disable which brings the following observation. On a minimal build of PHP, with only the mandatory extensions enabled, there are 148 classes/interfaces/traits defined. [2] Other than the SPL ones (and even then), disabling any of these classes will cause issues within the engine. Moreover, the SPL ones are not a security concern. Therefore, any other class that can be disabled must come from an extension that can be disabled altogether. And "disabling" a class from an extension without disabling said extension will render it useless anyway. If a hosting provided is concerned about an extension, then it should not enable it in the first place. Not break it ad hoc. Considering the above, I cannot see how this functionality was ever useful. This is in stark contrast to the disable_functions INI setting, which can be used to selectively remove functionality of an extension without breaking it overall. What makes this setting particularly broken is that it does not unregister the class, it only overwrites the create CE handler to emit a warning and purge the properties and function hashtables. This leads to various use after free, segfaults, and broken expectations for the engine and extensions which define said classes. On top of that, it is possible to actually instantiate such a class (and even classes which actually disallow this like ext/imap) in userland, and pass it to function that are typed against said class without raising a TypeError. However, when trying to do anything with said object stuff is going to explode in countless ways. [1] https://news-web.php.net/php.internals/120896 [2] https://gist.github.com/Girgias/63d55ba1e50b580412b004046daed02b --- Zend/tests/bug77494.phpt | 23 -------- Zend/tests/disable_classes_warning.phpt | 27 ++++++++++ Zend/tests/errmsg/errmsg_021.phpt | 17 ------ Zend/zend_API.c | 71 ------------------------- Zend/zend_API.h | 1 - main/main.c | 45 ++-------------- main/php_globals.h | 1 - php.ini-development | 5 -- php.ini-production | 5 -- sapi/fpm/fpm/fpm_php.c | 36 ------------- sapi/fpm/www.conf.in | 5 +- sapi/fuzzer/fuzzer-sapi.c | 19 ++++++- 12 files changed, 49 insertions(+), 206 deletions(-) delete mode 100644 Zend/tests/bug77494.phpt create mode 100644 Zend/tests/disable_classes_warning.phpt delete mode 100644 Zend/tests/errmsg/errmsg_021.phpt diff --git a/Zend/tests/bug77494.phpt b/Zend/tests/bug77494.phpt deleted file mode 100644 index 7f808c2c0f52a..0000000000000 --- a/Zend/tests/bug77494.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -Bug #77494 (Disabling class causes segfault on member access) ---EXTENSIONS-- -curl ---INI-- -disable_classes=CURLFile,ErrorException ---FILE-- -name); -$b = new ErrorException(); -var_dump($b->message); -?> ---EXPECTF-- -Warning: CURLFile() has been disabled for security reasons in %sbug77494.php on line 2 - -Warning: Undefined property: CURLFile::$name in %s on line %d -NULL - -Warning: ErrorException() has been disabled for security reasons in %s on line %d - -Warning: Undefined property: ErrorException::$message in %s on line %d -NULL diff --git a/Zend/tests/disable_classes_warning.phpt b/Zend/tests/disable_classes_warning.phpt new file mode 100644 index 0000000000000..c3692bf52972f --- /dev/null +++ b/Zend/tests/disable_classes_warning.phpt @@ -0,0 +1,27 @@ +--TEST-- +Check that warning is emitted when disabling classes +--INI-- +disable_classes=Exception +--FILE-- + +--EXPECTF-- +object(Exception)#1 (7) { + ["message":protected]=> + string(0) "" + ["string":"Exception":private]=> + string(0) "" + ["code":protected]=> + int(0) + ["file":protected]=> + string(%d) "%s" + ["line":protected]=> + int(2) + ["trace":"Exception":private]=> + array(0) { + } + ["previous":"Exception":private]=> + NULL +} diff --git a/Zend/tests/errmsg/errmsg_021.phpt b/Zend/tests/errmsg/errmsg_021.phpt deleted file mode 100644 index 7514e68359044..0000000000000 --- a/Zend/tests/errmsg/errmsg_021.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -errmsg: disabled class ---INI-- -disable_classes=stdclass ---FILE-- - ---EXPECTF-- -Warning: test() has been disabled for security reasons in %s on line %d -Done diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 8ac140cdceffc..fa5365b776d2c 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3692,77 +3692,6 @@ ZEND_API void zend_disable_functions(const char *function_list) /* {{{ */ } /* }}} */ -#ifdef ZEND_WIN32 -#pragma optimize("", off) -#endif -static ZEND_COLD zend_object *display_disabled_class(zend_class_entry *class_type) /* {{{ */ -{ - zend_object *intern; - - intern = zend_objects_new(class_type); - - /* Initialize default properties */ - if (EXPECTED(class_type->default_properties_count != 0)) { - zval *p = intern->properties_table; - zval *end = p + class_type->default_properties_count; - do { - ZVAL_UNDEF(p); - p++; - } while (p != end); - } - - zend_error(E_WARNING, "%s() has been disabled for security reasons", ZSTR_VAL(class_type->name)); - return intern; -} -#ifdef ZEND_WIN32 -#pragma optimize("", on) -#endif -/* }}} */ - -static const zend_function_entry disabled_class_new[] = { - ZEND_FE_END -}; - -ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length) /* {{{ */ -{ - zend_class_entry *disabled_class; - zend_string *key; - zend_function *fn; - zend_property_info *prop; - - key = zend_string_alloc(class_name_length, 0); - zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length); - disabled_class = zend_hash_find_ptr(CG(class_table), key); - zend_string_release_ex(key, 0); - if (!disabled_class) { - return FAILURE; - } - - /* Will be reset by INIT_CLASS_ENTRY. */ - free(disabled_class->interfaces); - - INIT_CLASS_ENTRY_INIT_METHODS((*disabled_class), disabled_class_new); - disabled_class->create_object = display_disabled_class; - - ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->function_table, fn) { - if ((fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) && - fn->common.scope == disabled_class) { - zend_free_internal_arg_info(&fn->internal_function); - } - } ZEND_HASH_FOREACH_END(); - zend_hash_clean(&disabled_class->function_table); - ZEND_HASH_MAP_FOREACH_PTR(&disabled_class->properties_info, prop) { - if (prop->ce == disabled_class) { - zend_string_release(prop->name); - zend_type_release(prop->type, /* persistent */ 1); - free(prop); - } - } ZEND_HASH_FOREACH_END(); - zend_hash_clean(&disabled_class->properties_info); - return SUCCESS; -} -/* }}} */ - static zend_always_inline zend_class_entry *get_scope(zend_execute_data *frame) { return frame && frame->func ? frame->func->common.scope : NULL; diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 78a30d630ae6e..f4a6fc5f50e9d 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -405,7 +405,6 @@ static zend_always_inline zend_result zend_register_class_alias(const char *name zend_register_class_alias_ex(ZEND_NS_NAME(ns, name), sizeof(ZEND_NS_NAME(ns, name))-1, ce, 1) ZEND_API void zend_disable_functions(const char *function_list); -ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_name_length); ZEND_API ZEND_COLD void zend_wrong_param_count(void); ZEND_API ZEND_COLD void zend_wrong_property_read(zval *object, zval *property); diff --git a/main/main.c b/main/main.c index 8edf214bc3f9a..eb842474240fd 100644 --- a/main/main.c +++ b/main/main.c @@ -413,41 +413,6 @@ static PHP_INI_MH(OnSetLogFilter) } /* }}} */ -/* {{{ php_disable_classes */ -static void php_disable_classes(void) -{ - char *s = NULL, *e; - - if (!*(INI_STR("disable_classes"))) { - return; - } - - e = PG(disable_classes) = strdup(INI_STR("disable_classes")); - - while (*e) { - switch (*e) { - case ' ': - case ',': - if (s) { - *e = '\0'; - zend_disable_class(s, e-s); - s = NULL; - } - break; - default: - if (!s) { - s = e; - } - break; - } - e++; - } - if (s) { - zend_disable_class(s, e-s); - } -} -/* }}} */ - /* {{{ php_binary_init */ static void php_binary_init(void) { @@ -872,7 +837,6 @@ PHP_INI_BEGIN() PHP_INI_ENTRY("sendmail_path", DEFAULT_SENDMAIL_PATH, PHP_INI_SYSTEM, NULL) PHP_INI_ENTRY("mail.force_extra_parameters",NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnChangeMailForceExtra) PHP_INI_ENTRY("disable_functions", "", PHP_INI_SYSTEM, NULL) - PHP_INI_ENTRY("disable_classes", "", PHP_INI_SYSTEM, NULL) PHP_INI_ENTRY("max_file_uploads", "20", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL) PHP_INI_ENTRY("max_multipart_body_parts", "-1", PHP_INI_SYSTEM|PHP_INI_PERDIR, NULL) @@ -2105,9 +2069,6 @@ static void core_globals_dtor(php_core_globals *core_globals) ZEND_ASSERT(!core_globals->last_error_message); ZEND_ASSERT(!core_globals->last_error_file); - if (core_globals->disable_classes) { - free(core_globals->disable_classes); - } if (core_globals->php_binary) { free(core_globals->php_binary); } @@ -2372,9 +2333,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi } } - /* disable certain classes and functions as requested by php.ini */ + /* disable certain functions as requested by php.ini */ zend_disable_functions(INI_STR("disable_functions")); - php_disable_classes(); /* make core report what it should */ if ((module = zend_hash_str_find_ptr(&module_registry, "core", sizeof("core")-1)) != NULL) { @@ -2403,7 +2363,7 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi struct { const long error_level; const char *phrase; - const char *directives[18]; /* Remember to change this if the number of directives change */ + const char *directives[19]; /* Remember to change this if the number of directives change */ } directives[2] = { { E_DEPRECATED, @@ -2434,6 +2394,7 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi "safe_mode_protected_env_vars", "zend.ze1_compatibility_mode", "track_errors", + "disable_classes", NULL } } diff --git a/main/php_globals.h b/main/php_globals.h index 94e5aa44fc59f..61df82ac99f5f 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -143,7 +143,6 @@ struct _php_core_globals { char *php_sys_temp_dir; - char *disable_classes; zend_long max_input_nesting_level; zend_long max_input_vars; diff --git a/php.ini-development b/php.ini-development index 5cbf6a54ee44c..9e7039e9d1bf0 100644 --- a/php.ini-development +++ b/php.ini-development @@ -327,11 +327,6 @@ serialize_precision = -1 ; https://php.net/disable-functions disable_functions = -; This directive allows you to disable certain classes. -; It receives a comma-delimited list of class names. -; https://php.net/disable-classes -disable_classes = - ; Colors for Syntax Highlighting mode. Anything that's acceptable in ; would work. ; https://php.net/syntax-highlighting diff --git a/php.ini-production b/php.ini-production index 697c7073ebde0..17752f80918cb 100644 --- a/php.ini-production +++ b/php.ini-production @@ -327,11 +327,6 @@ serialize_precision = -1 ; https://php.net/disable-functions disable_functions = -; This directive allows you to disable certain classes. -; It receives a comma-delimited list of class names. -; https://php.net/disable-classes -disable_classes = - ; Colors for Syntax Highlighting mode. Anything that's acceptable in ; would work. ; https://php.net/syntax-highlighting diff --git a/sapi/fpm/fpm/fpm_php.c b/sapi/fpm/fpm/fpm_php.c index 55b0377c1ad06..97d35d42bfe66 100644 --- a/sapi/fpm/fpm/fpm_php.c +++ b/sapi/fpm/fpm/fpm_php.c @@ -48,35 +48,6 @@ static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_ } /* }}} */ -static void fpm_php_disable(char *value, int (*zend_disable)(const char *, size_t)) /* {{{ */ -{ - char *s = 0, *e = value; - - while (*e) { - switch (*e) { - case ' ': - case ',': - if (s) { - *e = '\0'; - zend_disable(s, e - s); - s = 0; - } - break; - default: - if (!s) { - s = e; - } - break; - } - e++; - } - - if (s) { - zend_disable(s, e - s); - } -} -/* }}} */ - #define FPM_PHP_INI_ALTERING_ERROR -1 #define FPM_PHP_INI_APPLIED 1 #define FPM_PHP_INI_EXTENSION_FAILED 0 @@ -121,13 +92,6 @@ int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */ return FPM_PHP_INI_APPLIED; } - if (!strcmp(name, "disable_classes") && *value) { - char *v = strdup(value); - PG(disable_classes) = v; - fpm_php_disable(v, zend_disable_class); - return FPM_PHP_INI_APPLIED; - } - return FPM_PHP_INI_APPLIED; } /* }}} */ diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in index 9689e4defab04..69df3e6630047 100644 --- a/sapi/fpm/www.conf.in +++ b/sapi/fpm/www.conf.in @@ -474,9 +474,8 @@ pm.max_spare_servers = 3 ; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. ; Defining 'extension' will load the corresponding shared extension from -; extension_dir. Defining 'disable_functions' or 'disable_classes' will not -; overwrite previously defined php.ini values, but will append the new value -; instead. +; extension_dir. Defining 'disable_functions' will not overwrite previously +; defined php.ini values, but will append the new value instead. ; Note: path INI options can be relative and will be expanded with the prefix ; (pool, global or @prefix@) diff --git a/sapi/fuzzer/fuzzer-sapi.c b/sapi/fuzzer/fuzzer-sapi.c index 3cffacae1d172..a81e533a6b5f0 100644 --- a/sapi/fuzzer/fuzzer-sapi.c +++ b/sapi/fuzzer/fuzzer-sapi.c @@ -56,8 +56,6 @@ static const char HARDCODED_INI[] = ",crypt" /* openlog() has a known memory-management issue. */ ",openlog" - /* Can cause long loops that bypass the executor step limit. */ - "\ndisable_classes=InfiniteIterator" ; static int startup(sapi_module_struct *sapi_module) @@ -128,6 +126,21 @@ static sapi_module_struct fuzzer_module = { STANDARD_SAPI_MODULE_PROPERTIES }; +static ZEND_COLD zend_object *disable_class_create_handler(zend_class_entry *class_type) /* {{{ */ +{ + zend_throw_error(NULL, "Cannot construct class %s, as it is disabled", ZSTR_VAL(class_type->name)); + return NULL; +} + +static void fuzzer_disable_classes(void) +{ + /* Overwrite built-in constructor for InfiniteIterator as it + * can cause long loops that bypass the executor step limit. */ + /* Lowercase as this is how the CE as stored */ + zend_class_entry *InfiniteIterator_class = zend_hash_str_find(CG(class_table), "infiniteiterator", strlen("infiniteiterator")); + InfiniteIterator_class->create_object = disable_class_create_handler; +} + int fuzzer_init_php(const char *extra_ini) { #ifdef __SANITIZE_ADDRESS__ @@ -183,6 +196,8 @@ int fuzzer_request_startup(void) SIGG(check) = 0; #endif + fuzzer_disable_classes(); + return SUCCESS; } From 1c1f08c282a37763dcf3ae5445c044ce92bb38f1 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 Aug 2025 22:27:17 +0200 Subject: [PATCH 2/5] Update NEWS/UPGRADING for disable_classes removal --- NEWS | 1 + UPGRADING | 3 +++ 2 files changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 1a2369ea2d073..fec36651e7f2e 100644 --- a/NEWS +++ b/NEWS @@ -29,6 +29,7 @@ PHP NEWS (ilutov) . Prohibit pipe & arrow function combination that leads to confusing parse trees. (ilutov) + . The disable_classes INI directive has been removed. (Girgias) - Filter: . Added support for configuring the URI parser for FILTER_VALIDATE_URL diff --git a/UPGRADING b/UPGRADING index dd37bb21b4527..7c6371062b17f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -55,6 +55,9 @@ PHP 8.5 UPGRADE NOTES ReflectionAttribute::newInstance() was called an error would be thrown. The error can be delayed from compilation to runtime using the new #[\DelayedTargetValidation] attribute. + . The disable_classes INI setting has been removed as it causes various + engine assumptions to be broken. + RFC: https://wiki.php.net/rfc/deprecations_php_8_5#remove_disable_classes_ini_setting - DOM: . Cloning a DOMNamedNodeMap, DOMNodeList, Dom\NamedNodeMap, Dom\NodeList, From e87767686c98abc78a5d2ac4510f1419f58aff3b Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 Aug 2025 22:28:39 +0200 Subject: [PATCH 3/5] Core changes should be first and then other extension are alphabetical --- UPGRADING | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UPGRADING b/UPGRADING index 7c6371062b17f..d42ab07626a3a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -19,12 +19,6 @@ PHP 8.5 UPGRADE NOTES 1. Backward Incompatible Changes ======================================== -- BZ2: - . bzcompress() now throws a ValueError when $block_size is not between - 1 and 9. - . bzcompress() now throws a ValueError when $work_factor is not between - 0 and 250. - - Core: . It is no longer possible to use "array" and "callable" as class alias names in class_alias(). @@ -59,6 +53,12 @@ PHP 8.5 UPGRADE NOTES engine assumptions to be broken. RFC: https://wiki.php.net/rfc/deprecations_php_8_5#remove_disable_classes_ini_setting +- BZ2: + . bzcompress() now throws a ValueError when $block_size is not between + 1 and 9. + . bzcompress() now throws a ValueError when $work_factor is not between + 0 and 250. + - DOM: . Cloning a DOMNamedNodeMap, DOMNodeList, Dom\NamedNodeMap, Dom\NodeList, Dom\HTMLCollection, and Dom\DtdNamedNodeMap now fails. From 8a5972fd83d801bdf7486178f5898d25de033a90 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 Aug 2025 22:43:26 +0100 Subject: [PATCH 4/5] ext/standard: Deprecate $http_response_header (#19464) RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_http_response_header_predefined_variable --- Zend/zend_compile.c | 37 ++++++++++++----- Zend/zend_compile.h | 1 + ext/standard/tests/http/bug75535.phpt | 2 +- ext/standard/tests/http/bug80838.phpt | 3 +- ext/standard/tests/http/gh9316.phpt | 3 +- .../tests/http/ghsa-52jp-hrpf-2jff-001.phpt | 2 +- .../tests/http/ghsa-52jp-hrpf-2jff-002.phpt | 2 +- .../tests/http/ghsa-hgf5-96fm-v528-001.phpt | 2 +- .../tests/http/ghsa-hgf5-96fm-v528-002.phpt | 2 +- .../tests/http/ghsa-hgf5-96fm-v528-003.phpt | 2 +- .../tests/http/ghsa-pcmh-g36c-qc44-001.phpt | 2 +- .../tests/http/ghsa-pcmh-g36c-qc44-002.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt | 2 +- .../tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt | 2 +- .../tests/http/http_response_header_01.phpt | 3 +- .../tests/http/http_response_header_02.phpt | 3 +- .../tests/http/http_response_header_03.phpt | 1 + .../tests/http/http_response_header_04.phpt | 4 +- ...ttp_response_header_deprecated_bypass.phpt | 40 +++++++++++++++++++ ...onse_header_deprecated_multiple_files.phpt | 33 +++++++++++++++ ...nse_header_deprecated_multiple_files_1.inc | 13 ++++++ ...nse_header_deprecated_multiple_files_2.inc | 13 ++++++ ..._header_deprecated_multiple_op_arrays.phpt | 20 ++++++++++ ...se_header_deprecated_nested_op_arrays.phpt | 16 ++++++++ 27 files changed, 189 insertions(+), 27 deletions(-) create mode 100644 ext/standard/tests/http/http_response_header_deprecated_bypass.phpt create mode 100644 ext/standard/tests/http/http_response_header_deprecated_multiple_files.phpt create mode 100644 ext/standard/tests/http/http_response_header_deprecated_multiple_files_1.inc create mode 100644 ext/standard/tests/http/http_response_header_deprecated_multiple_files_2.inc create mode 100644 ext/standard/tests/http/http_response_header_deprecated_multiple_op_arrays.phpt create mode 100644 ext/standard/tests/http/http_response_header_deprecated_nested_op_arrays.phpt diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index fae510bb2688c..0a7ab7da1bd5f 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -339,6 +339,7 @@ void zend_oparray_context_begin(zend_oparray_context *prev_context, zend_op_arra CG(context).try_catch_offset = -1; CG(context).current_brk_cont = -1; CG(context).last_brk_cont = 0; + CG(context).has_assigned_to_http_response_header = false; CG(context).brk_cont_array = NULL; CG(context).labels = NULL; CG(context).in_jmp_frameless_branch = false; @@ -2867,7 +2868,7 @@ static void zend_compile_class_ref(znode *result, zend_ast *name_ast, uint32_t f } /* }}} */ -static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ +static zend_result zend_try_compile_cv(znode *result, zend_ast *ast, uint32_t type) /* {{{ */ { zend_ast *name_ast = ast->child[0]; if (name_ast->kind == ZEND_AST_ZVAL) { @@ -2884,6 +2885,16 @@ static zend_result zend_try_compile_cv(znode *result, zend_ast *ast) /* {{{ */ return FAILURE; } + if (!CG(context).has_assigned_to_http_response_header && zend_string_equals_literal(name, "http_response_header")) { + if (type == BP_VAR_R) { + zend_error(E_DEPRECATED, + "The predefined locally scoped $http_response_header variable is deprecated," + " call http_get_last_response_headers() instead"); + } else if (type == BP_VAR_W) { + CG(context).has_assigned_to_http_response_header = true; + } + } + result->op_type = IS_CV; result->u.op.var = lookup_cv(name); @@ -2920,6 +2931,14 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint opline->extended_value = ZEND_FETCH_GLOBAL; } else { + // TODO: Have a test case for this? + if (name_node.op_type == IS_CONST + && type == BP_VAR_R + && zend_string_equals_literal(Z_STR(name_node.u.constant), "http_response_header")) { + zend_error(E_DEPRECATED, + "The predefined locally scoped $http_response_header variable is deprecated," + " call http_get_last_response_headers() instead"); + } opline->extended_value = ZEND_FETCH_LOCAL; } @@ -2991,7 +3010,7 @@ static zend_op *zend_compile_simple_var(znode *result, zend_ast *ast, uint32_t t result->op_type = IS_TMP_VAR; } return opline; - } else if (zend_try_compile_cv(result, ast) == FAILURE) { + } else if (zend_try_compile_cv(result, ast, type) == FAILURE) { return zend_compile_simple_var_no_cv(result, ast, type, delayed); } return NULL; @@ -3417,7 +3436,7 @@ static void zend_compile_expr_with_potential_assign_to_self( /* $a[0] = $a should evaluate the right $a first */ znode cv_node; - if (zend_try_compile_cv(&cv_node, expr_ast) == FAILURE) { + if (zend_try_compile_cv(&cv_node, expr_ast, BP_VAR_R) == FAILURE) { zend_compile_simple_var_no_cv(expr_node, expr_ast, BP_VAR_R, 0); } else { zend_emit_op_tmp(expr_node, ZEND_QM_ASSIGN, &cv_node, NULL); @@ -3507,7 +3526,7 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ /* list($a, $b) = $a should evaluate the right $a first */ znode cv_node; - if (zend_try_compile_cv(&cv_node, expr_ast) == FAILURE) { + if (zend_try_compile_cv(&cv_node, expr_ast, BP_VAR_R) == FAILURE) { zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0); } else { zend_emit_op_tmp(&expr_node, ZEND_QM_ASSIGN, &cv_node, NULL); @@ -3822,7 +3841,7 @@ static uint32_t zend_compile_args( opcode = ZEND_SEND_VAR_EX; CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS; break; - } else if (zend_try_compile_cv(&arg_node, arg) == SUCCESS) { + } else if (zend_try_compile_cv(&arg_node, arg, BP_VAR_R) == SUCCESS) { opcode = ZEND_SEND_VAR_EX; break; } @@ -5427,7 +5446,7 @@ static void zend_compile_global_var(zend_ast *ast) /* {{{ */ // TODO(GLOBALS) Forbid "global $GLOBALS"? if (is_this_fetch(var_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use $this as global variable"); - } else if (zend_try_compile_cv(&result, var_ast) == SUCCESS) { + } else if (zend_try_compile_cv(&result, var_ast, BP_VAR_R) == SUCCESS) { zend_op *opline = zend_emit_op(NULL, ZEND_BIND_GLOBAL, &result, &name_node); opline->extended_value = zend_alloc_cache_slot(); } else { @@ -5553,7 +5572,7 @@ static void zend_compile_unset(zend_ast *ast) /* {{{ */ case ZEND_AST_VAR: if (is_this_fetch(var_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot unset $this"); - } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { + } else if (zend_try_compile_cv(&var_node, var_ast, BP_VAR_UNSET) == SUCCESS) { opline = zend_emit_op(NULL, ZEND_UNSET_CV, &var_node, NULL); } else { opline = zend_compile_simple_var_no_cv(NULL, var_ast, BP_VAR_UNSET, 0); @@ -6116,7 +6135,7 @@ static void zend_compile_foreach(zend_ast *ast) /* {{{ */ if (is_this_fetch(value_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); } else if (value_ast->kind == ZEND_AST_VAR && - zend_try_compile_cv(&value_node, value_ast) == SUCCESS) { + zend_try_compile_cv(&value_node, value_ast, BP_VAR_R) == SUCCESS) { SET_NODE(opline->op2, &value_node); } else { opline->op2_type = IS_VAR; @@ -10842,7 +10861,7 @@ static void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */ if (is_this_fetch(var_ast)) { opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_THIS, NULL, NULL); CG(active_op_array)->fn_flags |= ZEND_ACC_USES_THIS; - } else if (zend_try_compile_cv(&var_node, var_ast) == SUCCESS) { + } else if (zend_try_compile_cv(&var_node, var_ast, BP_VAR_IS) == SUCCESS) { opline = zend_emit_op(result, ZEND_ISSET_ISEMPTY_CV, &var_node, NULL); } else { opline = zend_compile_simple_var_no_cv(result, var_ast, BP_VAR_IS, 0); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 0234f77775b33..6c444340a9e76 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -208,6 +208,7 @@ typedef struct _zend_oparray_context { zend_string *active_property_info_name; zend_property_hook_kind active_property_hook_kind; bool in_jmp_frameless_branch; + bool has_assigned_to_http_response_header; } zend_oparray_context; /* Class, property and method flags class|meth.|prop.|const*/ diff --git a/ext/standard/tests/http/bug75535.phpt b/ext/standard/tests/http/bug75535.phpt index 94348d1a027aa..6f16baa242ffb 100644 --- a/ext/standard/tests/http/bug75535.phpt +++ b/ext/standard/tests/http/bug75535.phpt @@ -15,7 +15,7 @@ $responses = array( ['pid' => $pid, 'uri' => $uri] = http_server($responses, $output); var_dump(file_get_contents($uri)); -var_dump($http_response_header); +var_dump(http_get_last_response_headers()); http_server_kill($pid); diff --git a/ext/standard/tests/http/bug80838.phpt b/ext/standard/tests/http/bug80838.phpt index 7171ad173fb6e..8e7578ca025de 100644 --- a/ext/standard/tests/http/bug80838.phpt +++ b/ext/standard/tests/http/bug80838.phpt @@ -33,7 +33,8 @@ var_dump(http_get_last_response_headers()); http_server_kill($pid); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 23 NULL array(3) { [0]=> diff --git a/ext/standard/tests/http/gh9316.phpt b/ext/standard/tests/http/gh9316.phpt index 2f4a637f83e3b..355769d5db1b2 100644 --- a/ext/standard/tests/http/gh9316.phpt +++ b/ext/standard/tests/http/gh9316.phpt @@ -30,7 +30,8 @@ for ($i = 0; $i < count($responses); ++$i) { http_server_kill($pid); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 17 http_get_last_response_headers() before stream layer call: NULL $http_response_header diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt index 744cff9cc72f2..73e1408c370bd 100644 --- a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt +++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt @@ -36,7 +36,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt index bc71fd4e41167..cde5aff0afe17 100644 --- a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt +++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt @@ -36,7 +36,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt index c40123560ef1e..0bd496ca72d00 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt @@ -33,7 +33,7 @@ $clientCode = <<<'CODE' ]; $ctx = stream_context_create($opts); var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx)))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt index f3302d77c1d63..d470cdda3f5c8 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt @@ -33,7 +33,7 @@ $clientCode = <<<'CODE' ]; $ctx = stream_context_create($opts); var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx)))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt index 30d20f855419c..594dc8d14e358 100644 --- a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt +++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt @@ -33,7 +33,7 @@ $clientCode = <<<'CODE' ]; $ctx = stream_context_create($opts); var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx)))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt index bb7945ce62d0e..471f44745ed32 100644 --- a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt +++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt index 1d0e4fa70a2c9..67ce5517ce3c1 100644 --- a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt +++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt index f935b5a02ca94..94439534d9a5c 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt index 078d605b6718f..6ad3e1bc2ca33 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt index ad5ddc879cead..497d6d92fbd26 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx))); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt index d0396e819fbd3..e6382420954f3 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt index 037d2002cc537..c0ee01671460b 100644 --- a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt +++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt @@ -32,7 +32,7 @@ $clientCode = <<<'CODE' $ctx = stream_context_create(); stream_context_set_params($ctx, array("notification" => "stream_notification_callback")); var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx)); - var_dump($http_response_header); + var_dump(http_get_last_response_headers()); CODE; include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__); diff --git a/ext/standard/tests/http/http_response_header_01.phpt b/ext/standard/tests/http/http_response_header_01.phpt index 4c494f0960ff3..324e00adf7702 100644 --- a/ext/standard/tests/http/http_response_header_01.phpt +++ b/ext/standard/tests/http/http_response_header_01.phpt @@ -24,7 +24,8 @@ var_dump(http_get_last_response_headers()); http_server_kill($pid); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 14 NULL string(4) "Body" array(3) { diff --git a/ext/standard/tests/http/http_response_header_02.phpt b/ext/standard/tests/http/http_response_header_02.phpt index 56eb2868b6d1b..779edfcac1eb3 100644 --- a/ext/standard/tests/http/http_response_header_02.phpt +++ b/ext/standard/tests/http/http_response_header_02.phpt @@ -26,7 +26,8 @@ var_dump(http_get_last_response_headers()); http_server_kill($pid); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 16 NULL string(4) "Body" array(5) { diff --git a/ext/standard/tests/http/http_response_header_03.phpt b/ext/standard/tests/http/http_response_header_03.phpt index f7fa7e00c60aa..5bddc304c7241 100644 --- a/ext/standard/tests/http/http_response_header_03.phpt +++ b/ext/standard/tests/http/http_response_header_03.phpt @@ -27,6 +27,7 @@ http_server_kill($pid); ?> --EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 16 NULL Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP request failed! HTTP/1.0 404 Not Found%a diff --git a/ext/standard/tests/http/http_response_header_04.phpt b/ext/standard/tests/http/http_response_header_04.phpt index a8a285fb7ad93..5cc6920b610ce 100644 --- a/ext/standard/tests/http/http_response_header_04.phpt +++ b/ext/standard/tests/http/http_response_header_04.phpt @@ -24,7 +24,9 @@ var_dump(http_get_last_response_headers()); http_server_kill($pid); ?> ---EXPECT-- + +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 14 NULL string(4) "Body" array(2) { diff --git a/ext/standard/tests/http/http_response_header_deprecated_bypass.phpt b/ext/standard/tests/http/http_response_header_deprecated_bypass.phpt new file mode 100644 index 0000000000000..91f1957737a6d --- /dev/null +++ b/ext/standard/tests/http/http_response_header_deprecated_bypass.phpt @@ -0,0 +1,40 @@ +--TEST-- +Ensure suggested cross-version compatible code for $http_reponse_header provided by the RFC works +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- + $pid, 'uri' => $uri] = http_server($responses, $output); + +var_dump(http_get_last_response_headers()); + +$f = file_get_contents($uri); +var_dump($f); + +if (function_exists('http_get_last_response_headers')) { + $http_response_header = http_get_last_response_headers(); +} +var_dump($http_response_header); + +http_server_kill($pid); + +?> +--EXPECT-- +NULL +string(4) "Body" +array(3) { + [0]=> + string(15) "HTTP/1.0 200 Ok" + [1]=> + string(12) "Some: Header" + [2]=> + string(12) "Some: Header" +} diff --git a/ext/standard/tests/http/http_response_header_deprecated_multiple_files.phpt b/ext/standard/tests/http/http_response_header_deprecated_multiple_files.phpt new file mode 100644 index 0000000000000..826ec554bf9b6 --- /dev/null +++ b/ext/standard/tests/http/http_response_header_deprecated_multiple_files.phpt @@ -0,0 +1,33 @@ +--TEST-- +$http_reponse_header should warn once per file +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +--FILE-- + +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %shttp_response_header_deprecated_multiple_files_1.inc on line %d +string(6) "Body11" +array(2) { + [0]=> + string(15) "HTTP/1.0 200 Ok" + [1]=> + string(13) "Some: Header1" +} + +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %shttp_response_header_deprecated_multiple_files_2.inc on line %d +string(5) "Body2" +array(2) { + [0]=> + string(15) "HTTP/1.0 200 Ok" + [1]=> + string(13) "Some: Header2" +} diff --git a/ext/standard/tests/http/http_response_header_deprecated_multiple_files_1.inc b/ext/standard/tests/http/http_response_header_deprecated_multiple_files_1.inc new file mode 100644 index 0000000000000..68d0ff31729a4 --- /dev/null +++ b/ext/standard/tests/http/http_response_header_deprecated_multiple_files_1.inc @@ -0,0 +1,13 @@ + $pid, 'uri' => $uri] = http_server($responses, $output); + +$f = file_get_contents($uri); +var_dump($f); +var_dump($http_response_header); + +http_server_kill($pid); diff --git a/ext/standard/tests/http/http_response_header_deprecated_multiple_files_2.inc b/ext/standard/tests/http/http_response_header_deprecated_multiple_files_2.inc new file mode 100644 index 0000000000000..ba00ff7a06be5 --- /dev/null +++ b/ext/standard/tests/http/http_response_header_deprecated_multiple_files_2.inc @@ -0,0 +1,13 @@ + $pid, 'uri' => $uri] = http_server($responses, $output); + +$f = file_get_contents($uri); +var_dump($f); +var_dump($http_response_header); + +http_server_kill($pid); diff --git a/ext/standard/tests/http/http_response_header_deprecated_multiple_op_arrays.phpt b/ext/standard/tests/http/http_response_header_deprecated_multiple_op_arrays.phpt new file mode 100644 index 0000000000000..aa2334dad6d71 --- /dev/null +++ b/ext/standard/tests/http/http_response_header_deprecated_multiple_op_arrays.phpt @@ -0,0 +1,20 @@ +--TEST-- +$http_reponse_header should warn once per oparray +--FILE-- + +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 4 + +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 9 diff --git a/ext/standard/tests/http/http_response_header_deprecated_nested_op_arrays.phpt b/ext/standard/tests/http/http_response_header_deprecated_nested_op_arrays.phpt new file mode 100644 index 0000000000000..0f05e689c3412 --- /dev/null +++ b/ext/standard/tests/http/http_response_header_deprecated_nested_op_arrays.phpt @@ -0,0 +1,16 @@ +--TEST-- +$http_reponse_header should warn once per oparray even if nested +--FILE-- + +--EXPECTF-- +Deprecated: The predefined locally scoped $http_response_header variable is deprecated, call http_get_last_response_headers() instead in %s on line 6 From 99068da2b1c90f893a96895d930e7df84a2f8d9f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 25 Aug 2025 23:46:11 +0200 Subject: [PATCH 5/5] Update NEWS/UPGRADING for $http_response_header deprecation --- NEWS | 2 ++ UPGRADING | 3 +++ 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index fec36651e7f2e..5921ce8febea3 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ PHP NEWS . Prohibit pipe & arrow function combination that leads to confusing parse trees. (ilutov) . The disable_classes INI directive has been removed. (Girgias) + . The locally predefined variable $http_response_header is deprecated. + (Girgias) - Filter: . Added support for configuring the URI parser for FILTER_VALIDATE_URL diff --git a/UPGRADING b/UPGRADING index d42ab07626a3a..9ffb1896917e7 100644 --- a/UPGRADING +++ b/UPGRADING @@ -464,6 +464,9 @@ PHP 8.5 UPGRADE NOTES . Passing integers outside the interval [0, 255] to chr() is now deprecated. This is because a byte can only hold a value within this interval. RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_passing_integers_outside_the_interval_0_255_to_chr + . Relying locally predefined variable $http_response_header is deprecated. + Instead one should call the http_get_last_response_headers() function. + RFC: https://wiki.php.net/rfc/deprecations_php_8_5#deprecate_the_http_response_header_predefined_variable - XML: . The xml_parser_free() function has been deprecated, as XMLParser objects