Skip to content

Commit 07aab0c

Browse files
committed
Merge branch 'PHP-8.3' into PHP-8.4
* PHP-8.3: Fix GH-20374: PHP with tidy and custom-tags pgsql: Fix memory leak when object init fails (#20387)
2 parents 57deb6c + fcc159b commit 07aab0c

File tree

4 files changed

+203
-4
lines changed

4 files changed

+203
-4
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.4.16
44

5+
- Tidy:
6+
. Fixed bug GH-20374 (PHP with tidy and custom-tags). (ndossche)
57

68
20 Nov 2025, PHP 8.4.15
79

ext/pgsql/pgsql.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2066,6 +2066,7 @@ static void php_pgsql_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, zend_long result_
20662066
ZVAL_COPY_VALUE(&dataset, return_value);
20672067
zend_result obj_initialized = object_init_ex(return_value, ce);
20682068
if (UNEXPECTED(obj_initialized == FAILURE)) {
2069+
zval_ptr_dtor(&dataset);
20692070
RETURN_THROWS();
20702071
}
20712072
if (!ce->default_properties_count && !ce->__set) {

ext/tidy/tests/gh20374.phpt

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
--TEST--
2+
GH-20374 (PHP with tidy and custom-tags)
3+
--EXTENSIONS--
4+
tidy
5+
--CREDITS--
6+
franck-paul
7+
--FILE--
8+
<?php
9+
10+
class MyStringable {
11+
public function __construct(private $ret) {}
12+
13+
public function __toString(): string {
14+
return $this->ret;
15+
}
16+
}
17+
18+
class MyThrowingStringable {
19+
public function __toString(): string {
20+
throw new Error('no');
21+
}
22+
}
23+
24+
$values = [
25+
'string blocklevel' => 'blocklevel',
26+
'int' => 1,
27+
'double overflow' => (string) (2.0**80.0),
28+
'numeric string int 1' => '1',
29+
'numeric string double 1.0' => '1.0',
30+
'false' => false,
31+
'true' => true,
32+
'NAN' => NAN,
33+
'INF' => INF,
34+
'object with numeric string int 0' => new MyStringable('0'),
35+
'object with string blocklevel' => new MyStringable('blocklevel'),
36+
'object with string empty' => new MyStringable('empty'),
37+
'object with exception' => new MyThrowingStringable,
38+
];
39+
40+
foreach ($values as $key => $value) {
41+
echo "--- $key ---\n";
42+
$str = '<custom-html-element>test</custom-html-element>';
43+
44+
$config = [
45+
'custom-tags' => $value,
46+
];
47+
48+
$tidy = new tidy();
49+
try {
50+
$tidy->parseString($str, $config, 'utf8');
51+
echo $tidy->value, "\n";
52+
} catch (Throwable $e) {
53+
echo $e::class, ": ", $e->getMessage(), "\n";
54+
}
55+
}
56+
57+
?>
58+
--EXPECT--
59+
--- string blocklevel ---
60+
<html>
61+
<head>
62+
<title></title>
63+
</head>
64+
<body>
65+
<custom-html-element>test</custom-html-element>
66+
</body>
67+
</html>
68+
--- int ---
69+
<html>
70+
<head>
71+
<title></title>
72+
</head>
73+
<body>
74+
<custom-html-element>test</custom-html-element>
75+
</body>
76+
</html>
77+
--- double overflow ---
78+
<html>
79+
<head>
80+
<title></title>
81+
</head>
82+
<body>
83+
test
84+
</body>
85+
</html>
86+
--- numeric string int 1 ---
87+
<html>
88+
<head>
89+
<title></title>
90+
</head>
91+
<body>
92+
<custom-html-element>test</custom-html-element>
93+
</body>
94+
</html>
95+
--- numeric string double 1.0 ---
96+
<html>
97+
<head>
98+
<title></title>
99+
</head>
100+
<body>
101+
<custom-html-element>test</custom-html-element>
102+
</body>
103+
</html>
104+
--- false ---
105+
<html>
106+
<head>
107+
<title></title>
108+
</head>
109+
<body>
110+
test
111+
</body>
112+
</html>
113+
--- true ---
114+
<html>
115+
<head>
116+
<title></title>
117+
</head>
118+
<body>
119+
<custom-html-element>test</custom-html-element>
120+
</body>
121+
</html>
122+
--- NAN ---
123+
<html>
124+
<head>
125+
<title></title>
126+
</head>
127+
<body>
128+
test
129+
</body>
130+
</html>
131+
--- INF ---
132+
<html>
133+
<head>
134+
<title></title>
135+
</head>
136+
<body>
137+
test
138+
</body>
139+
</html>
140+
--- object with numeric string int 0 ---
141+
<html>
142+
<head>
143+
<title></title>
144+
</head>
145+
<body>
146+
test
147+
</body>
148+
</html>
149+
--- object with string blocklevel ---
150+
<html>
151+
<head>
152+
<title></title>
153+
</head>
154+
<body>
155+
<custom-html-element>test</custom-html-element>
156+
</body>
157+
</html>
158+
--- object with string empty ---
159+
<custom-html-element>
160+
<html>
161+
<head>
162+
<title></title>
163+
</head>
164+
<body>
165+
test
166+
</body>
167+
</html>
168+
--- object with exception ---
169+
Error: no

ext/tidy/tidy.c

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,37 @@ static int _php_tidy_set_tidy_opt(TidyDoc doc, const char *optname, zval *value)
253253
zend_tmp_string_release(tmp_str);
254254
break;
255255

256-
case TidyInteger:
257-
lval = zval_get_long(value);
258-
if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
259-
return SUCCESS;
256+
case TidyInteger: /* integer or enum */
257+
ZVAL_DEREF(value);
258+
/* Enum will correspond to a non-numeric string or object */
259+
if (Z_TYPE_P(value) == IS_STRING || Z_TYPE_P(value) == IS_OBJECT) {
260+
double dval;
261+
str = zval_try_get_tmp_string(value, &tmp_str);
262+
if (UNEXPECTED(!str)) {
263+
return FAILURE;
264+
}
265+
uint8_t type = is_numeric_string(ZSTR_VAL(str), ZSTR_LEN(str), &lval, &dval, true);
266+
if (type == IS_DOUBLE) {
267+
lval = zend_dval_to_lval_cap(dval);
268+
type = IS_LONG;
269+
}
270+
if (type == IS_LONG) {
271+
if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
272+
zend_tmp_string_release(tmp_str);
273+
return SUCCESS;
274+
}
275+
} else {
276+
if (tidyOptSetValue(doc, tidyOptGetId(opt), ZSTR_VAL(str))) {
277+
zend_tmp_string_release(tmp_str);
278+
return SUCCESS;
279+
}
280+
}
281+
zend_tmp_string_release(tmp_str);
282+
} else {
283+
lval = zval_get_long(value);
284+
if (tidyOptSetInt(doc, tidyOptGetId(opt), lval)) {
285+
return SUCCESS;
286+
}
260287
}
261288
break;
262289

0 commit comments

Comments
 (0)