From 01dbee83e1eb7ff4a27b4b2eedbb04fe74a4ccdf Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 18 Jul 2025 03:39:51 +0100 Subject: [PATCH 1/8] ext/tidy: tidyOptGetType returns an enum that is fully checked --- ext/tidy/tidy.c | 83 +++++++++++++++---------------------------------- 1 file changed, 25 insertions(+), 58 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index fe2a0648af683..5fa4c77741762 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -219,7 +219,6 @@ static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_str static zend_result _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value, uint32_t arg) { TidyOption opt = tidyGetOptionByName(doc, optname); - zend_string *str, *tmp_str; zend_long lval; if (!opt) { @@ -236,36 +235,21 @@ static zend_result _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval return FAILURE; } - switch(tidyOptGetType(opt)) { - case TidyString: - str = zval_get_tmp_string(value, &tmp_str); - if (tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str))) { - zend_tmp_string_release(tmp_str); - return SUCCESS; - } - zend_tmp_string_release(tmp_str); - break; - - case TidyInteger: - lval = zval_get_long(value); - if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) { - return SUCCESS; - } - break; - - case TidyBoolean: - lval = zval_get_long(value); - if (tidyOptSetBool(doc, tidyOptGetId(opt), lval)) { - return SUCCESS; - } - break; - - default: - php_error_docref(NULL, E_WARNING, "Unable to determine type of configuration option"); - break; + TidyOptionType type = tidyOptGetType(opt); + if (type == TidyString) { + zend_string *tmp_str; + const zend_string *str = zval_get_tmp_string(value, &tmp_str); + const bool result = tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str)); + zend_tmp_string_release(tmp_str); + return result ? SUCCESS : FAILURE; + } else if (type == TidyInteger) { + lval = zval_get_long(value); + return tidyOptSetInt(doc, tidyOptGetId(opt), lval) ? SUCCESS : FAILURE; + } else { + ZEND_ASSERT(type == TidyBoolean); + lval = zval_get_long(value); + return tidyOptSetBool(doc, tidyOptGetId(opt), lval) ? SUCCESS : FAILURE; } - - return FAILURE; } static void tidy_create_node_object(zval *zv, PHPTidyDoc *ptdoc, TidyNode node) @@ -720,28 +704,19 @@ static void *php_tidy_get_opt_val(const PHPTidyDoc *ptdoc, TidyOption opt, TidyO { *type = tidyOptGetType(opt); - switch (*type) { - case TidyString: { - const char *val = tidyOptGetValue(ptdoc->doc, tidyOptGetId(opt)); - if (val) { - return (void *) zend_string_init(val, strlen(val), 0); - } else { - return (void *) ZSTR_EMPTY_ALLOC(); - } + if (*type == TidyString) { + const char *val = tidyOptGetValue(ptdoc->doc, tidyOptGetId(opt)); + if (val) { + return (void *) zend_string_init(val, strlen(val), 0); + } else { + return (void *) ZSTR_EMPTY_ALLOC(); } - break; - - case TidyInteger: - return (void *) (uintptr_t) tidyOptGetInt(ptdoc->doc, tidyOptGetId(opt)); - break; - - case TidyBoolean: - return (void *) tidyOptGetBool(ptdoc->doc, tidyOptGetId(opt)); - break; + } else if (*type == TidyInteger) { + return (void *) (uintptr_t) tidyOptGetInt(ptdoc->doc, tidyOptGetId(opt)); + } else { + ZEND_ASSERT(*type == TidyBoolean); + return (void *) tidyOptGetBool(ptdoc->doc, tidyOptGetId(opt)); } - - /* should not happen */ - return NULL; } static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetypes node_type) @@ -1322,18 +1297,10 @@ PHP_FUNCTION(tidy_getopt) case TidyInteger: RETURN_LONG((zend_long)optval); - break; case TidyBoolean: RETURN_BOOL(optval); - break; - - default: - php_error_docref(NULL, E_WARNING, "Unable to determine type of configuration option"); - break; } - - RETURN_FALSE; } /* }}} */ From 0a130caf587bdf2047a237ae1ff170845f5ba5b1 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 18 Jul 2025 03:45:47 +0100 Subject: [PATCH 2/8] ext/tidy: refactor php_tidy_set_tidy_opt() Bring closer to unique call site and return value of it --- ext/tidy/tests/031.phpt | 2 +- ext/tidy/tests/tidy_error1.phpt | 11 ++++- ext/tidy/tidy.c | 82 +++++++++++++++++---------------- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/ext/tidy/tests/031.phpt b/ext/tidy/tests/031.phpt index e41d94be2f794..3b0ac39b209e8 100644 --- a/ext/tidy/tests/031.phpt +++ b/ext/tidy/tests/031.phpt @@ -7,7 +7,7 @@ tidy --FILE-- '; -$config = array('doctype' => 'php'); +$config = array('doctype' => 'auto'); $tidy = tidy_parse_string($buffer, $config); var_dump(tidy_config_count($tidy)); diff --git a/ext/tidy/tests/tidy_error1.phpt b/ext/tidy/tests/tidy_error1.phpt index 0660f37437e2b..5e7593762a937 100644 --- a/ext/tidy/tests/tidy_error1.phpt +++ b/ext/tidy/tests/tidy_error1.phpt @@ -32,7 +32,15 @@ try { echo $e::class, ": ", $e->getMessage(), PHP_EOL; } -$config = ['doctype' => 'php', 0 => 'value2']; +$config = ['doctype' => 'php']; + +try { + var_dump($tidy->parseString($buffer, $config)); +} catch (\TypeError $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$config = ['doctype' => 'auto', 0 => 'value2']; try { var_dump($tidy->parseString($buffer, $config)); @@ -45,4 +53,5 @@ try { ValueError: tidy::parseString(): Argument #2 ($config) Unknown Tidy configuration option "bogus" TypeError: tidy::parseString(): Argument #2 ($config) must be of type array with keys as string ValueError: tidy::parseString(): Argument #2 ($config) Attempting to set read-only option "doctype-mode" +TypeError: tidy::parseString(): Argument #2 ($config) option "doctype" does not accept "php" as a value TypeError: tidy::parseString(): Argument #2 ($config) must be of type array with keys as string diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 5fa4c77741762..1290645dcf61f 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -124,7 +124,6 @@ static void tidy_doc_update_properties(PHPTidyObj *); static void tidy_add_node_default_properties(PHPTidyObj *); static void *php_tidy_get_opt_val(const PHPTidyDoc *, TidyOption, TidyOptionType *); static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetypes); -static zend_result _php_tidy_set_tidy_opt(TidyDoc, const char *, zval *, uint32_t arg); static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg); static PHP_INI_MH(php_tidy_set_clean_output); static void php_tidy_clean_output_start(const char *name, size_t name_len); @@ -216,42 +215,6 @@ static zend_result php_tidy_apply_config(TidyDoc doc, const zend_string *str_str return SUCCESS; } -static zend_result _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value, uint32_t arg) -{ - TidyOption opt = tidyGetOptionByName(doc, optname); - zend_long lval; - - if (!opt) { - zend_argument_value_error(arg, "Unknown Tidy configuration option \"%s\"", optname); - return FAILURE; - } - -#if defined(HAVE_TIDYOPTGETCATEGORY) - if (tidyOptGetCategory(opt) == TidyInternalCategory) { -#else - if (tidyOptIsReadOnly(opt)) { -#endif - zend_argument_value_error(arg, "Attempting to set read-only option \"%s\"", optname); - return FAILURE; - } - - TidyOptionType type = tidyOptGetType(opt); - if (type == TidyString) { - zend_string *tmp_str; - const zend_string *str = zval_get_tmp_string(value, &tmp_str); - const bool result = tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str)); - zend_tmp_string_release(tmp_str); - return result ? SUCCESS : FAILURE; - } else if (type == TidyInteger) { - lval = zval_get_long(value); - return tidyOptSetInt(doc, tidyOptGetId(opt), lval) ? SUCCESS : FAILURE; - } else { - ZEND_ASSERT(type == TidyBoolean); - lval = zval_get_long(value); - return tidyOptSetBool(doc, tidyOptGetId(opt), lval) ? SUCCESS : FAILURE; - } -} - static void tidy_create_node_object(zval *zv, PHPTidyDoc *ptdoc, TidyNode node) { object_init_ex(zv, tidy_ce_node); @@ -751,20 +714,61 @@ static void php_tidy_create_node(INTERNAL_FUNCTION_PARAMETERS, tidy_base_nodetyp tidy_create_node_object(return_value, obj->ptdoc, node); } + +static bool php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value, uint32_t arg) +{ + TidyOption opt = tidyGetOptionByName(doc, optname); + zend_long lval; + + if (!opt) { + zend_argument_value_error(arg, "Unknown Tidy configuration option \"%s\"", optname); + return false; + } + +#if defined(HAVE_TIDYOPTGETCATEGORY) + if (tidyOptGetCategory(opt) == TidyInternalCategory) { +#else + if (tidyOptIsReadOnly(opt)) { +#endif + zend_argument_value_error(arg, "Attempting to set read-only option \"%s\"", optname); + return false; + } + + TidyOptionType type = tidyOptGetType(opt); + if (type == TidyString) { + zend_string *tmp_str; + const zend_string *str = zval_get_tmp_string(value, &tmp_str); + const bool result = tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str)); + if (UNEXPECTED(!result)) { + zend_argument_type_error(arg, "option \"%s\" does not accept \"%s\" as a value", optname, ZSTR_VAL(str)); + } + zend_tmp_string_release(tmp_str); + return result; + } else if (type == TidyInteger) { + lval = zval_get_long(value); + return tidyOptSetInt(doc, tidyOptGetId(opt), lval); + } else { + ZEND_ASSERT(type == TidyBoolean); + lval = zval_get_long(value); + return tidyOptSetBool(doc, tidyOptGetId(opt), lval); + } +} + static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht_options, uint32_t arg) { zval *opt_val; zend_string *opt_name; if (!HT_IS_PACKED(ht_options)) { + bool has_failures = false; ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht_options, opt_name, opt_val) { if (opt_name == NULL) { zend_argument_type_error(arg, "must be of type array with keys as string"); return FAILURE; } - _php_tidy_set_tidy_opt(doc, ZSTR_VAL(opt_name), opt_val, arg); + has_failures = has_failures || !php_tidy_set_tidy_opt(doc, ZSTR_VAL(opt_name), opt_val, arg); } ZEND_HASH_FOREACH_END(); - return SUCCESS; + return has_failures ? FAILURE : SUCCESS; } else { zend_argument_type_error(arg, "must be of type array with keys as string"); return FAILURE; From 4433986e406be3883d0563aaa563d9d3ca059439 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 18 Jul 2025 03:53:19 +0100 Subject: [PATCH 3/8] ext/tidy: Refactor php_tidy_parse_string() --- ext/tidy/tidy.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index 1290645dcf61f..dd657c8bddb48 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -775,11 +775,13 @@ static zend_result _php_tidy_apply_config_array(TidyDoc doc, const HashTable *ht } } -static zend_result php_tidy_parse_string(PHPTidyObj *obj, const char *string, uint32_t len, const char *enc) +static zend_result php_tidy_parse_string(PHPTidyObj *obj, const zend_string *string, const char *enc) { TidyBuffer buf; - if(enc) { + ZEND_ASSERT(!ZEND_SIZE_T_UINT_OVFL(ZSTR_LEN(string))); + + if (enc) { if (tidySetCharEncoding(obj->ptdoc->doc, enc) < 0) { php_error_docref(NULL, E_WARNING, "Could not set encoding \"%s\"", enc); return FAILURE; @@ -789,9 +791,9 @@ static zend_result php_tidy_parse_string(PHPTidyObj *obj, const char *string, ui obj->ptdoc->initialized = true; tidyBufInit(&buf); - tidyBufAttach(&buf, (byte *) string, len); + tidyBufAttach(&buf, (byte *) ZSTR_VAL(string), (unsigned int) ZSTR_LEN(string)); if (tidyParseBuffer(obj->ptdoc->doc, &buf) < 0) { - php_error_docref(NULL, E_WARNING, "%s", obj->ptdoc->errbuf->bp); + php_error_docref(NULL, E_WARNING, "%s", (const char*) obj->ptdoc->errbuf->bp); return FAILURE; } tidy_doc_update_properties(obj); @@ -995,7 +997,7 @@ PHP_FUNCTION(tidy_parse_string) obj = Z_TIDY_P(return_value); if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS - || php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) != SUCCESS) { + || php_tidy_parse_string(obj, input, enc) != SUCCESS) { zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -1063,7 +1065,7 @@ PHP_FUNCTION(tidy_parse_file) obj = Z_TIDY_P(return_value); if (php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) != SUCCESS - || php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) != SUCCESS) { + || php_tidy_parse_string(obj, contents, enc) != SUCCESS) { zval_ptr_dtor(return_value); RETVAL_FALSE; } @@ -1348,7 +1350,7 @@ PHP_METHOD(tidy, __construct) } zend_restore_error_handling(&error_handling); - php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc); + php_tidy_parse_string(obj, contents, enc); zend_string_release_ex(contents, 0); } @@ -1385,7 +1387,7 @@ PHP_METHOD(tidy, parseFile) } RETVAL_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS - && php_tidy_parse_string(obj, ZSTR_VAL(contents), (uint32_t)ZSTR_LEN(contents), enc) == SUCCESS); + && php_tidy_parse_string(obj, contents, enc) == SUCCESS); zend_string_release_ex(contents, 0); } @@ -1413,7 +1415,7 @@ PHP_METHOD(tidy, parseString) obj = Z_TIDY_P(ZEND_THIS); RETURN_BOOL(php_tidy_apply_config(obj->ptdoc->doc, options_str, options_ht, 2) == SUCCESS - && php_tidy_parse_string(obj, ZSTR_VAL(input), (uint32_t)ZSTR_LEN(input), enc) == SUCCESS); + && php_tidy_parse_string(obj, input, enc) == SUCCESS); } From 419f6750c0efea4d1bd4d7f0add1024a5469d0e5 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 18 Jul 2025 04:24:11 +0100 Subject: [PATCH 4/8] ext/tidy: refactor php_tidy_file_to_mem() Pass zend_string* along --- ext/tidy/tidy.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c index dd657c8bddb48..16c09a96a75c2 100644 --- a/ext/tidy/tidy.c +++ b/ext/tidy/tidy.c @@ -114,7 +114,7 @@ static inline PHPTidyObj *php_tidy_fetch_object(zend_object *obj) { /* }}} */ /* {{{ ext/tidy prototypes */ -static zend_string *php_tidy_file_to_mem(const char *, bool); +static zend_string *php_tidy_file_to_mem(const zend_string *, bool); static void tidy_object_free_storage(zend_object *); static zend_object *tidy_object_new_node(zend_class_entry *); static zend_object *tidy_object_new_doc(zend_class_entry *); @@ -246,7 +246,7 @@ static void php_tidy_quick_repair(INTERNAL_FUNCTION_PARAMETERS, bool is_file) Z_PARAM_BOOL(use_include_path) ZEND_PARSE_PARAMETERS_END(); - if (!(data = php_tidy_file_to_mem(ZSTR_VAL(arg1), use_include_path))) { + if (!(data = php_tidy_file_to_mem(arg1, use_include_path))) { RETURN_FALSE; } } else { @@ -328,12 +328,12 @@ static void php_tidy_quick_repair(INTERNAL_FUNCTION_PARAMETERS, bool is_file) tidyRelease(doc); } -static zend_string *php_tidy_file_to_mem(const char *filename, bool use_include_path) +static zend_string *php_tidy_file_to_mem(const zend_string *filename, bool use_include_path) { php_stream *stream; zend_string *data = NULL; - if (!(stream = php_stream_open_wrapper(filename, "rb", (use_include_path ? USE_PATH : 0), NULL))) { + if (!(stream = php_stream_open_wrapper(ZSTR_VAL(filename), "rb", (use_include_path ? USE_PATH : 0), NULL))) { return NULL; } if ((data = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0)) == NULL) { @@ -1050,7 +1050,7 @@ PHP_FUNCTION(tidy_parse_file) Z_PARAM_BOOL(use_include_path) ZEND_PARSE_PARAMETERS_END(); - if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { + if (!(contents = php_tidy_file_to_mem(inputfile, use_include_path))) { php_error_docref(NULL, E_WARNING, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); RETURN_FALSE; } @@ -1330,7 +1330,7 @@ PHP_METHOD(tidy, __construct) obj = Z_TIDY_P(ZEND_THIS); if (inputfile) { - if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { + if (!(contents = php_tidy_file_to_mem(inputfile, use_include_path))) { zend_throw_error(zend_ce_exception, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); RETURN_THROWS(); } @@ -1375,7 +1375,7 @@ PHP_METHOD(tidy, parseFile) obj = Z_TIDY_P(ZEND_THIS); - if (!(contents = php_tidy_file_to_mem(ZSTR_VAL(inputfile), use_include_path))) { + if (!(contents = php_tidy_file_to_mem(inputfile, use_include_path))) { php_error_docref(NULL, E_WARNING, "Cannot load \"%s\" into memory%s", ZSTR_VAL(inputfile), (use_include_path) ? " (using include path)" : ""); RETURN_FALSE; } From 063d795599af576d06210ea7e8e31342770135aa Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Fri, 18 Jul 2025 14:10:40 +0100 Subject: [PATCH 5/8] ext/intl: using a bit more modern c++ memory management features. (#19163) not always possible (e.g. PHP objects) but when scoped we can manage here to simplify memory managament starting with IntlDateFormat. --- .../dateformat/dateformat_format_object.cpp | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/ext/intl/dateformat/dateformat_format_object.cpp b/ext/intl/dateformat/dateformat_format_object.cpp index 81490c62fd5e2..e60b16c1c2b6e 100644 --- a/ext/intl/dateformat/dateformat_format_object.cpp +++ b/ext/intl/dateformat/dateformat_format_object.cpp @@ -70,10 +70,10 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object) size_t locale_len; bool pattern = false; UDate date; - TimeZone *timeZone = NULL; + std::unique_ptr timeZone; UErrorCode status = U_ZERO_ERROR; - DateFormat *df = NULL; - Calendar *cal = NULL; + std::unique_ptr df; + std::unique_ptr cal; DateFormat::EStyle dateStyle = DateFormat::kDefault, timeStyle = DateFormat::kDefault; @@ -158,28 +158,28 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object) "not initialized properly", 0); RETURN_FALSE; } - timeZone = obj_cal->getTimeZone().clone(); + timeZone = std::unique_ptr(obj_cal->getTimeZone().clone()); date = obj_cal->getTime(status); if (U_FAILURE(status)) { intl_error_set(NULL, status, "datefmt_format_object: error obtaining instant from " "IntlCalendar", 0); - RETVAL_FALSE; - goto cleanup; + RETURN_FALSE; } - cal = obj_cal->clone(); + cal = std::unique_ptr(obj_cal->clone()); } else if (instanceof_function(instance_ce, php_date_get_interface_ce())) { - if (intl_datetime_decompose(object, &date, &timeZone, NULL, + TimeZone *tz; + if (intl_datetime_decompose(object, &date, &tz, NULL, "datefmt_format_object") == FAILURE) { RETURN_FALSE; } - cal = new GregorianCalendar(Locale::createFromName(locale_str), status); + timeZone = std::unique_ptr(tz); + cal = std::unique_ptr(new GregorianCalendar(Locale::createFromName(locale_str), status)); if (U_FAILURE(status)) { intl_error_set(NULL, status, "datefmt_format_object: could not create GregorianCalendar", 0); - RETVAL_FALSE; - goto cleanup; + RETURN_FALSE; } } else { intl_error_set(NULL, status, "datefmt_format_object: the passed object " @@ -190,36 +190,32 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object) if (pattern) { StringPiece sp(Z_STRVAL_P(format)); - df = new SimpleDateFormat( + df = std::unique_ptr(new SimpleDateFormat( UnicodeString::fromUTF8(sp), Locale::createFromName(locale_str), - status); + status)); if (U_FAILURE(status)) { intl_error_set(NULL, status, "datefmt_format_object: could not create SimpleDateFormat", 0); - RETVAL_FALSE; - goto cleanup; + RETURN_FALSE; } } else { - df = DateFormat::createDateTimeInstance(dateStyle, timeStyle, - Locale::createFromName(locale_str)); + df = std::unique_ptr(DateFormat::createDateTimeInstance(dateStyle, timeStyle, + Locale::createFromName(locale_str))); if (df == NULL) { /* according to ICU sources, this should never happen */ intl_error_set(NULL, status, "datefmt_format_object: could not create DateFormat", 0); - RETVAL_FALSE; - goto cleanup; + RETURN_FALSE; } } //must be in this order (or have the cal adopt the tz) - df->adoptCalendar(cal); - cal = NULL; - df->adoptTimeZone(timeZone); - timeZone = NULL; + df->adoptCalendar(cal.release()); + df->adoptTimeZone(timeZone.release()); { zend_string *u8str; @@ -231,15 +227,8 @@ U_CFUNC PHP_FUNCTION(datefmt_format_object) intl_error_set(NULL, status, "datefmt_format_object: error converting result to UTF-8", 0); - RETVAL_FALSE; - goto cleanup; + RETURN_FALSE; } RETVAL_STR(u8str); } - - -cleanup: - delete df; - delete timeZone; - delete cal; } From 5fdc02282f6d965a284a18935119a97761192469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Fri, 18 Jul 2025 17:38:44 +0200 Subject: [PATCH 6/8] uri: Improve exceptions for `Uri\Rfc3986\Uri` (#19161) * uri: Streamline implementation of `uriparser_parse_uri_ex()` Avoid the use of a macro and streamline the logic. * uri: Improve exceptions for `Uri\Rfc3986\Uri` * uri: Allow empty URIs for RFC3986 * NEWS * uri: Improve ext/uri/tests/004.phpt for empty URIs --- NEWS | 1 + ext/uri/php_uriparser.c | 71 ++++++++++++++++++++++------------------- ext/uri/tests/004.phpt | 45 +++++++++++++++++++++----- ext/uri/tests/055.phpt | 2 +- 4 files changed, 78 insertions(+), 41 deletions(-) diff --git a/NEWS b/NEWS index 0ff1a5bbe0a75..68cce637fc375 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,7 @@ PHP NEWS . Error handling of Uri\WhatWg\Url::withHost() is fixed when the input contains a port. Now, it triggers an exception; previously, the error was silently swallowed. (Máté Kocsis) + . Support empty URIs with Uri\Rfc3986\Uri. (timwolla) 17 Jul 2025, PHP 8.5.0alpha2 diff --git a/ext/uri/php_uriparser.c b/ext/uri/php_uriparser.c index ba6f1ce776cb6..337e453c6d4df 100644 --- a/ext/uri/php_uriparser.c +++ b/ext/uri/php_uriparser.c @@ -21,7 +21,6 @@ #include "Zend/zend_exceptions.h" static void uriparser_free_uri(void *uri); -static void throw_invalid_uri_exception(void); static void *uriparser_malloc(UriMemoryManager *memory_manager, size_t size) { @@ -293,51 +292,59 @@ static uriparser_uris_t *uriparser_create_uris(void) return uriparser_uris; } -static void throw_invalid_uri_exception(void) -{ - zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); -} - -#define PARSE_URI(dest_uri, uri_str, uriparser_uris, silent) \ - do { \ - if (ZSTR_LEN(uri_str) == 0 || \ - uriParseSingleUriExMmA(dest_uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS \ - ) { \ - efree(uriparser_uris); \ - if (!silent) { \ - throw_invalid_uri_exception(); \ - } \ - return NULL; \ - } \ - } while (0) - void *uriparser_parse_uri_ex(const zend_string *uri_str, const uriparser_uris_t *uriparser_base_urls, bool silent) { - uriparser_uris_t *uriparser_uris = uriparser_create_uris(); + UriUriA uri = {0}; - if (uriparser_base_urls == NULL) { - PARSE_URI(&uriparser_uris->uri, uri_str, uriparser_uris, silent); - uriMakeOwnerMmA(&uriparser_uris->uri, mm); - } else { - UriUriA uri; + /* Parse the URI. */ + if (uriParseSingleUriExMmA(&uri, ZSTR_VAL(uri_str), ZSTR_VAL(uri_str) + ZSTR_LEN(uri_str), NULL, mm) != URI_SUCCESS) { + if (!silent) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified URI is malformed", 0); + } - PARSE_URI(&uri, uri_str, uriparser_uris, silent); + goto fail; + } - if (uriAddBaseUriExMmA(&uriparser_uris->uri, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm) != URI_SUCCESS) { - efree(uriparser_uris); - uriFreeUriMembersMmA(&uri, mm); + if (uriparser_base_urls != NULL) { + UriUriA tmp = {0}; + + /* Combine the parsed URI with the base URI and store the result in 'tmp', + * since the target and source URLs must be distinct. */ + int result = uriAddBaseUriExMmA(&tmp, &uri, &uriparser_base_urls->uri, URI_RESOLVE_STRICTLY, mm); + if (result != URI_SUCCESS) { if (!silent) { - throw_invalid_uri_exception(); + switch (result) { + case URI_ERROR_ADDBASE_REL_BASE: + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified base URI must be absolute", 0); + break; + default: + /* This should be unreachable in practice. */ + zend_throw_exception(uri_invalid_uri_exception_ce, "Failed to resolve the specified URI against the base URI", 0); + break; + } } - return NULL; + goto fail; } - uriMakeOwnerMmA(&uriparser_uris->uri, mm); + /* Store the combined URI back into 'uri'. */ uriFreeUriMembersMmA(&uri, mm); + uri = tmp; } + /* Make the resulting URI independent of the 'uri_str'. */ + uriMakeOwnerMmA(&uri, mm); + + uriparser_uris_t *uriparser_uris = uriparser_create_uris(); + uriparser_uris->uri = uri; + return uriparser_uris; + + fail: + + uriFreeUriMembersMmA(&uri, mm); + + return NULL; } void *uriparser_parse_uri(const zend_string *uri_str, const void *base_url, zval *errors, bool silent) diff --git a/ext/uri/tests/004.phpt b/ext/uri/tests/004.phpt index 10e90dc584a8d..d22f52f30c48f 100644 --- a/ext/uri/tests/004.phpt +++ b/ext/uri/tests/004.phpt @@ -5,12 +5,7 @@ uri --FILE-- getMessage() . "\n"; -} - +var_dump(new Uri\Rfc3986\Uri("")); var_dump(Uri\Rfc3986\Uri::parse("")); try { @@ -29,8 +24,42 @@ var_dump(Uri\WhatWg\Url::parse("http://RuPaul's Drag Race All Stars 7 Winners Ca ?> --EXPECTF-- -The specified URI is malformed -NULL +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} +object(Uri\Rfc3986\Uri)#%d (%d) { + ["scheme"]=> + NULL + ["username"]=> + NULL + ["password"]=> + NULL + ["host"]=> + NULL + ["port"]=> + NULL + ["path"]=> + string(0) "" + ["query"]=> + NULL + ["fragment"]=> + NULL +} The specified URI is malformed (MissingSchemeNonRelativeUrl) NULL object(Uri\Rfc3986\Uri)#%d (%d) { diff --git a/ext/uri/tests/055.phpt b/ext/uri/tests/055.phpt index 95aac9d3a8b67..1b1684d350b09 100644 --- a/ext/uri/tests/055.phpt +++ b/ext/uri/tests/055.phpt @@ -12,4 +12,4 @@ try { } ?> --EXPECT-- -The specified URI is malformed +The specified base URI must be absolute From b48faee8502d73a1312b9181244f5b2065cf485f Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Fri, 18 Jul 2025 16:43:39 +0100 Subject: [PATCH 7/8] ext/snmp: no need to use a smart_string for error message (#19171) --- ext/snmp/snmp.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 915c4a46e1b12..db2d0ad87867e 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -1038,21 +1038,18 @@ static bool snmp_session_set_auth_protocol(struct snmp_session *s, zend_string * } #endif - smart_string err = {0}; - - smart_string_appends(&err, "Authentication protocol must be \"SHA\""); + zend_value_error( + "Authentication protocol must be \"SHA\"" #ifdef HAVE_SNMP_SHA256 - smart_string_appends(&err, " or \"SHA256\""); + " or \"SHA256\"" #endif #ifdef HAVE_SNMP_SHA512 - smart_string_appends(&err, " or \"SHA512\""); + " or \"SHA512\"" #endif #ifndef DISABLE_MD5 - smart_string_appends(&err, " or \"MD5\""); + " or \"MD5\"" #endif - smart_string_0(&err); - zend_value_error("%s", err.c); - smart_string_free(&err); + ); return false; } /* }}} */ From be09985c870a72d93e53295897679f49c0f11a58 Mon Sep 17 00:00:00 2001 From: Petr Sumbera Date: Fri, 18 Jul 2025 17:29:02 +0200 Subject: [PATCH 8/8] Fix GH-19169: ZEND_STATIC_ASSERT for -std=c++17 needs to define ZEND_STATIC_ASSERT to appropriate C++ static_assert instead of the C version. --- NEWS | 4 ++++ Zend/zend_portability.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index cbd68f4ce7484..cee68cfdd6266 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.25 +- Core: + . Fixed GH-19169 build issue with C++17 and ZEND_STATIC_ASSERT macro. + (psumbera) + - Hash: . Fix crash on clone failure. (nielsdos) diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index 670687973148f..2308d6cdb3057 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -761,7 +761,9 @@ extern "C++" { /** @deprecated */ #define ZEND_CGG_DIAGNOSTIC_IGNORED_END ZEND_DIAGNOSTIC_IGNORED_END -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ +#if defined(__cplusplus) +# define ZEND_STATIC_ASSERT(c, m) static_assert((c), m) +#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ # define ZEND_STATIC_ASSERT(c, m) _Static_assert((c), m) #else # define ZEND_STATIC_ASSERT(c, m)