From 274d23246c9701e081c91161ce7e64d82e00db20 Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Thu, 5 Nov 2026 20:05:37 +0000 Subject: [PATCH] Remove ENT_SUBSTITUTE from decode functions Fixes GH-9582 --- UPGRADING | 4 ++++ ext/standard/basic_functions.stub.php | 4 ++-- ext/standard/basic_functions_arginfo.h | 6 +++--- ext/standard/basic_functions_decl.h | 8 ++++---- ext/standard/html.c | 4 ++-- ext/standard/tests/strings/gh9582.phpt | 16 ++++++++++++++++ 6 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 ext/standard/tests/strings/gh9582.phpt diff --git a/UPGRADING b/UPGRADING index 88a91314afa0..e71818192291 100644 --- a/UPGRADING +++ b/UPGRADING @@ -105,6 +105,10 @@ PHP 8.7 UPGRADE NOTES ======================================== - Standard: + . The default $flags of htmlspecialchars_decode() and html_entity_decode() + is now ENT_QUOTES | ENT_HTML401. ENT_SUBSTITUTE was removed from the + default because the decoding functions ignore it. The behavior does not + change. . stream_select() no longer fails once a watched descriptor exceeds FD_SETSIZE. The descriptor set now grows on demand on both POSIX (bitset) and Windows (winsock fd_set), so code watching many streams works without diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 586d245903e3..d92b02679037 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2275,9 +2275,9 @@ function headers_list(): array {} function htmlspecialchars(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ?string $encoding = null, bool $double_encode = true): string {} -function htmlspecialchars_decode(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401): string {} +function htmlspecialchars_decode(string $string, int $flags = ENT_QUOTES | ENT_HTML401): string {} -function html_entity_decode(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ?string $encoding = null): string {} +function html_entity_decode(string $string, int $flags = ENT_QUOTES | ENT_HTML401, ?string $encoding = null): string {} function htmlentities(string $string, int $flags = ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, ?string $encoding = null, bool $double_encode = true): string {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 127858103c39..5b7668302900 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 5ccf5208c22244d77b47ac0f2363bf67eccb0e95 + * Stub hash: b43e1d8a8de35a14e52327d90a5773c2f0357d7a * Has decl header: yes */ #include "zend_attributes.h" @@ -788,12 +788,12 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_htmlspecialchars_decode, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "ENT_QUOTES | ENT_HTML401") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_html_entity_decode, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "ENT_QUOTES | ENT_HTML401") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, encoding, IS_STRING, 1, "null") ZEND_END_ARG_INFO() diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index a62459499d23..9ca62d2772dd 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: 5ccf5208c22244d77b47ac0f2363bf67eccb0e95 */ + * Stub hash: b43e1d8a8de35a14e52327d90a5773c2f0357d7a */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_5ccf5208c22244d77b47ac0f2363bf67eccb0e95_H -#define ZEND_BASIC_FUNCTIONS_DECL_5ccf5208c22244d77b47ac0f2363bf67eccb0e95_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_b43e1d8a8de35a14e52327d90a5773c2f0357d7a_H +#define ZEND_BASIC_FUNCTIONS_DECL_b43e1d8a8de35a14e52327d90a5773c2f0357d7a_H typedef enum zend_enum_SortDirection { ZEND_ENUM_SortDirection_Ascending = 1, @@ -20,4 +20,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_5ccf5208c22244d77b47ac0f2363bf67eccb0e95_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_b43e1d8a8de35a14e52327d90a5773c2f0357d7a_H */ diff --git a/ext/standard/html.c b/ext/standard/html.c index b621731a898d..40c4281eea11 100644 --- a/ext/standard/html.c +++ b/ext/standard/html.c @@ -1608,7 +1608,7 @@ PHP_FUNCTION(htmlspecialchars) PHP_FUNCTION(htmlspecialchars_decode) { zend_string *str; - zend_long quote_style = ENT_QUOTES|ENT_SUBSTITUTE; + zend_long quote_style = ENT_QUOTES; zend_string *replaced; ZEND_PARSE_PARAMETERS_START(1, 2) @@ -1626,7 +1626,7 @@ PHP_FUNCTION(htmlspecialchars_decode) PHP_FUNCTION(html_entity_decode) { zend_string *str, *hint_charset = NULL; - zend_long quote_style = ENT_QUOTES|ENT_SUBSTITUTE; + zend_long quote_style = ENT_QUOTES; zend_string *replaced; ZEND_PARSE_PARAMETERS_START(1, 3) diff --git a/ext/standard/tests/strings/gh9582.phpt b/ext/standard/tests/strings/gh9582.phpt new file mode 100644 index 000000000000..e8b8b16c58ad --- /dev/null +++ b/ext/standard/tests/strings/gh9582.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-9582 (htmlspecialchars_decode() ENT_SUBSTITUTE flag has no effect) +--FILE-- +getParameters()[1], "\n"; +echo (new ReflectionFunction(html_entity_decode(...)))->getParameters()[1], "\n"; + +$invalid = "\xC3&"'"; +var_dump(htmlspecialchars_decode($invalid) === htmlspecialchars_decode($invalid, ENT_QUOTES | ENT_SUBSTITUTE)); +var_dump(html_entity_decode($invalid) === html_entity_decode($invalid, ENT_QUOTES | ENT_SUBSTITUTE)); +?> +--EXPECT-- +Parameter #1 [ int $flags = ENT_QUOTES | ENT_HTML401 ] +Parameter #1 [ int $flags = ENT_QUOTES | ENT_HTML401 ] +bool(true) +bool(true)