diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c
index 9910b2b43bbaa..9c60015985c6d 100644
--- a/ext/pgsql/pgsql.c
+++ b/ext/pgsql/pgsql.c
@@ -2076,6 +2076,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
ZVAL_COPY_VALUE(&dataset, return_value);
zend_result obj_initialized = object_init_ex(return_value, ce);
if (UNEXPECTED(obj_initialized == FAILURE)) {
+ zval_ptr_dtor(&dataset);
RETURN_THROWS();
}
if (!ce->default_properties_count && !ce->__set) {
diff --git a/ext/tidy/tests/018.phpt b/ext/tidy/tests/018.phpt
index b07770eebb2b1..e3934aa6d9fa0 100644
--- a/ext/tidy/tests/018.phpt
+++ b/ext/tidy/tests/018.phpt
@@ -7,7 +7,7 @@ tidy
$x = tidy_repair_string("
abra\0cadabra
",
array( 'show-body-only' => true,
'clean' => false,
- 'newline' => "\n")
+ 'newline' => "LF")
);
var_dump($x);
?>
diff --git a/ext/tidy/tests/gh20374.phpt b/ext/tidy/tests/gh20374.phpt
new file mode 100644
index 0000000000000..39c3ba06d315e
--- /dev/null
+++ b/ext/tidy/tests/gh20374.phpt
@@ -0,0 +1,175 @@
+--TEST--
+GH-20374 (PHP with tidy and custom-tags)
+--EXTENSIONS--
+tidy
+--CREDITS--
+franck-paul
+--FILE--
+ret;
+ }
+}
+
+class MyThrowingStringable {
+ public function __toString(): string {
+ throw new Error('no');
+ }
+}
+
+$values = [
+ 'string blocklevel' => 'blocklevel',
+ 'int' => 1,
+ 'double overflow' => (string) (2.0**80.0),
+ 'numeric string int 1' => '1',
+ 'numeric string double 1.0' => '1.0',
+ 'false' => false,
+ 'true' => true,
+ 'NAN' => NAN,
+ 'INF' => INF,
+ 'object with numeric string int 0' => new MyStringable('0'),
+ 'object with string blocklevel' => new MyStringable('blocklevel'),
+ 'object with string empty' => new MyStringable('empty'),
+ 'object with exception' => new MyThrowingStringable,
+];
+
+foreach ($values as $key => $value) {
+ echo "--- $key ---\n";
+ $str = 'test';
+
+ $config = [
+ 'custom-tags' => $value,
+ ];
+
+ $tidy = new tidy();
+ try {
+ $tidy->parseString($str, $config, 'utf8');
+ echo $tidy->value, "\n";
+ } catch (Throwable $e) {
+ echo $e::class, ": ", $e->getMessage(), "\n";
+ }
+}
+
+?>
+--EXPECTF--
+--- string blocklevel ---
+
+
+
+
+
+test
+
+
+--- int ---
+
+
+
+
+
+test
+
+
+--- double overflow ---
+
+Warning: The float-string "1.2089258196146E+24" is not representable as an int, cast occurred in %s on line %d
+
+
+
+
+
+test
+
+
+--- numeric string int 1 ---
+
+
+
+
+
+test
+
+
+--- numeric string double 1.0 ---
+
+
+
+
+
+test
+
+
+--- false ---
+
+
+
+
+
+test
+
+
+--- true ---
+
+
+
+
+
+test
+
+
+--- NAN ---
+
+Warning: The float NAN is not representable as an int, cast occurred in %s on line %d
+
+
+
+
+
+test
+
+
+--- INF ---
+
+Warning: The float INF is not representable as an int, cast occurred in %s on line %d
+
+
+
+
+
+test
+
+
+--- object with numeric string int 0 ---
+
+
+
+
+
+test
+
+
+--- object with string blocklevel ---
+
+
+
+
+
+test
+
+
+--- object with string empty ---
+
+
+
+
+
+
+test
+
+
+--- object with exception ---
+Error: no
diff --git a/ext/tidy/tidy.c b/ext/tidy/tidy.c
index cbe12904795e6..63820d1a3075b 100644
--- a/ext/tidy/tidy.c
+++ b/ext/tidy/tidy.c
@@ -719,6 +719,7 @@ static bool php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value,
{
TidyOption opt = tidyGetOptionByName(doc, optname);
zend_long lval;
+ zend_string *tmp_str;
if (!opt) {
zend_argument_value_error(arg, "Unknown Tidy configuration option \"%s\"", optname);
@@ -736,7 +737,6 @@ static bool php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value,
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)) {
@@ -744,9 +744,35 @@ static bool php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value,
}
zend_tmp_string_release(tmp_str);
return result;
- } else if (type == TidyInteger) {
- lval = zval_get_long(value);
- return tidyOptSetInt(doc, tidyOptGetId(opt), lval);
+ } else if (type == TidyInteger) { /* integer or enum */
+ ZVAL_DEREF(value);
+ /* Enum will correspond to a non-numeric string or object */
+ if (Z_TYPE_P(value) == IS_STRING || Z_TYPE_P(value) == IS_OBJECT) {
+ double dval;
+ bool result;
+ const zend_string *str = zval_try_get_tmp_string(value, &tmp_str);
+ if (UNEXPECTED(!str)) {
+ return false;
+ }
+ uint8_t type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &lval, &dval, true);
+ if (type == IS_DOUBLE) {
+ lval = zend_dval_to_lval_cap(dval, str);
+ type = IS_LONG;
+ }
+ if (type == IS_LONG) {
+ result = tidyOptSetInt(doc, tidyOptGetId(opt), lval);
+ } else {
+ 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 {
+ lval = zval_get_long(value);
+ return tidyOptSetInt(doc, tidyOptGetId(opt), lval);
+ }
} else {
ZEND_ASSERT(type == TidyBoolean);
lval = zval_get_long(value);