1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Fix GH-20374: PHP with tidy and custom-tags

Both enums and integers map to TidyInteger, however, in the TidyInteger
case we always used zval_get_long(). So for a non-numeric string, this
would get turned into 0. 0 is the first enum value in that case, so the
wrong enum value could be selected.

To solve this, add special handling for strings and (stringable) objects
such that we can explicitly check for numeric strings, and if they're
not, handle them as normal strings instead of as 0.

Closes GH-20387.
This commit is contained in:
Niels Dossche
2025-11-03 21:26:14 +01:00
parent 04323955c1
commit fcc159b4f6
3 changed files with 202 additions and 4 deletions

2
NEWS
View File

@@ -2,6 +2,8 @@ PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
?? ??? ????, PHP 8.3.29
- Tidy:
. Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche)
20 Nov 2025, PHP 8.3.28

169
ext/tidy/tests/gh20374.phpt Normal file
View File

@@ -0,0 +1,169 @@
--TEST--
GH-20374 (PHP with tidy and custom-tags)
--EXTENSIONS--
tidy
--CREDITS--
franck-paul
--FILE--
<?php
class MyStringable {
public function __construct(private $ret) {}
public function __toString(): string {
return $this->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 = '<custom-html-element>test</custom-html-element>';
$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";
}
}
?>
--EXPECT--
--- string blocklevel ---
<html>
<head>
<title></title>
</head>
<body>
<custom-html-element>test</custom-html-element>
</body>
</html>
--- int ---
<html>
<head>
<title></title>
</head>
<body>
<custom-html-element>test</custom-html-element>
</body>
</html>
--- double overflow ---
<html>
<head>
<title></title>
</head>
<body>
test
</body>
</html>
--- numeric string int 1 ---
<html>
<head>
<title></title>
</head>
<body>
<custom-html-element>test</custom-html-element>
</body>
</html>
--- numeric string double 1.0 ---
<html>
<head>
<title></title>
</head>
<body>
<custom-html-element>test</custom-html-element>
</body>
</html>
--- false ---
<html>
<head>
<title></title>
</head>
<body>
test
</body>
</html>
--- true ---
<html>
<head>
<title></title>
</head>
<body>
<custom-html-element>test</custom-html-element>
</body>
</html>
--- NAN ---
<html>
<head>
<title></title>
</head>
<body>
test
</body>
</html>
--- INF ---
<html>
<head>
<title></title>
</head>
<body>
test
</body>
</html>
--- object with numeric string int 0 ---
<html>
<head>
<title></title>
</head>
<body>
test
</body>
</html>
--- object with string blocklevel ---
<html>
<head>
<title></title>
</head>
<body>
<custom-html-element>test</custom-html-element>
</body>
</html>
--- object with string empty ---
<custom-html-element>
<html>
<head>
<title></title>
</head>
<body>
test
</body>
</html>
--- object with exception ---
Error: no

View File

@@ -251,10 +251,37 @@ static int _php_tidy_set_tidy_opt(TidyDoc doc, char *optname, zval *value)
zend_tmp_string_release(tmp_str);
break;
case TidyInteger:
lval = zval_get_long(value);
if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
return SUCCESS;
case 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;
str = zval_try_get_tmp_string(value, &tmp_str);
if (UNEXPECTED(!str)) {
return FAILURE;
}
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);
type = IS_LONG;
}
if (type == IS_LONG) {
if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
zend_tmp_string_release(tmp_str);
return SUCCESS;
}
} else {
if (tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str))) {
zend_tmp_string_release(tmp_str);
return SUCCESS;
}
}
zend_tmp_string_release(tmp_str);
} else {
lval = zval_get_long(value);
if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
return SUCCESS;
}
}
break;