Skip to content

Commit ed1a6d4

Browse files
committed
Merge branch 'PHP-8.4' into PHP-8.5
* PHP-8.4: Fix GH-22825: DOM attribute methods on a DTD default attribute
2 parents 19af2e4 + 145e8c2 commit ed1a6d4

3 files changed

Lines changed: 112 additions & 3 deletions

File tree

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ PHP NEWS
55
- Date:
66
. Fixed leak on double DatePeriod::__construct() call. (ilutov)
77

8+
- DOM:
9+
. Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
10+
declares a default value for the attribute). (iliaal)
11+
812
- Opcache:
913
. Fixed GH-22693 (DT_TEXTREL in JIT-generated TLS access on x86_64).
1014
(David Carlier)

ext/dom/element.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,8 @@ PHP_METHOD(DOMElement, setAttribute)
466466
break;
467467
case XML_NAMESPACE_DECL:
468468
RETURN_FALSE;
469+
case XML_ATTRIBUTE_DECL:
470+
break;
469471
EMPTY_SWITCH_DEFAULT_CASE();
470472
}
471473
}
@@ -593,6 +595,8 @@ static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp)
593595

594596
break;
595597
}
598+
case XML_ATTRIBUTE_DECL:
599+
return false;
596600
EMPTY_SWITCH_DEFAULT_CASE();
597601
}
598602
return true;
@@ -722,7 +726,11 @@ static void dom_element_set_attribute_node_common(INTERNAL_FUNCTION_PARAMETERS,
722726
existattrp = xmlHasProp(nodep, attrp->name);
723727
}
724728

725-
if (existattrp != NULL && existattrp->type != XML_ATTRIBUTE_DECL) {
729+
if (existattrp != NULL && existattrp->type == XML_ATTRIBUTE_DECL) {
730+
existattrp = NULL;
731+
}
732+
733+
if (existattrp != NULL) {
726734
if ((oldobj = php_dom_object_get_data((xmlNodePtr) existattrp)) != NULL &&
727735
((php_libxml_node_ptr *)oldobj->ptr)->node == (xmlNodePtr) attrp)
728736
{
@@ -1908,8 +1916,7 @@ PHP_METHOD(DOMElement, toggleAttribute)
19081916

19091917
/* Step 5 */
19101918
if (force_is_null || !force) {
1911-
dom_remove_attribute(thisp, attribute);
1912-
retval = false;
1919+
retval = !dom_remove_attribute(thisp, attribute);
19131920
goto out;
19141921
}
19151922

ext/dom/tests/gh22825.phpt

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
--TEST--
2+
GH-22825 (DOMElement::setAttribute() reaches EMPTY_SWITCH_DEFAULT_CASE() with DTD #FIXED default attributes)
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
$cases = [
8+
['<!ATTLIST root A CDATA #FIXED "d">', '<root/>', 'A'],
9+
['<!ATTLIST root A CDATA "d">', '<root/>', 'A'],
10+
['<!ATTLIST root p:A CDATA #FIXED "d">', '<root xmlns:p="urn:p"/>', 'p:A'],
11+
];
12+
13+
function element(string $attlist, string $root): DOMElement {
14+
$doc = new DOMDocument();
15+
$doc->loadXML("<!DOCTYPE root [$attlist]>$root");
16+
return $doc->documentElement;
17+
}
18+
19+
foreach ($cases as [$attlist, $root, $name]) {
20+
echo "--- $attlist ---\n";
21+
22+
$el = element($attlist, $root);
23+
echo "hasAttribute: ";
24+
var_dump($el->hasAttribute($name));
25+
echo "getAttribute: ";
26+
var_dump($el->getAttribute($name));
27+
28+
$el = element($attlist, $root);
29+
$result = $el->setAttribute($name, 'v');
30+
echo "setAttribute: ", is_object($result) ? $result::class : var_export($result, true), "\n";
31+
echo "after setAttribute: ", trim($el->ownerDocument->saveXML($el)), "\n";
32+
33+
$el = element($attlist, $root);
34+
echo "removeAttribute: ";
35+
var_dump($el->removeAttribute($name));
36+
echo "still present after removeAttribute: ";
37+
var_dump($el->hasAttribute($name));
38+
39+
$el = element($attlist, $root);
40+
echo "toggleAttribute(false): ";
41+
var_dump($el->toggleAttribute($name, false));
42+
echo "still present after toggleAttribute(false): ";
43+
var_dump($el->hasAttribute($name));
44+
45+
$el = element($attlist, $root);
46+
echo "toggleAttribute(true): ";
47+
var_dump($el->toggleAttribute($name, true));
48+
echo "still present after toggleAttribute(true): ";
49+
var_dump($el->hasAttribute($name));
50+
51+
$el = element($attlist, $root);
52+
$attr = $el->ownerDocument->createAttribute($name);
53+
$attr->value = 'z';
54+
echo "setAttributeNode: ";
55+
var_dump($el->setAttributeNode($attr));
56+
echo "after setAttributeNode: ", trim($el->ownerDocument->saveXML($el)), "\n";
57+
}
58+
?>
59+
--EXPECT--
60+
--- <!ATTLIST root A CDATA #FIXED "d"> ---
61+
hasAttribute: bool(true)
62+
getAttribute: string(1) "d"
63+
setAttribute: DOMAttr
64+
after setAttribute: <root A="v"/>
65+
removeAttribute: bool(false)
66+
still present after removeAttribute: bool(true)
67+
toggleAttribute(false): bool(true)
68+
still present after toggleAttribute(false): bool(true)
69+
toggleAttribute(true): bool(true)
70+
still present after toggleAttribute(true): bool(true)
71+
setAttributeNode: NULL
72+
after setAttributeNode: <root A="z"/>
73+
--- <!ATTLIST root A CDATA "d"> ---
74+
hasAttribute: bool(true)
75+
getAttribute: string(1) "d"
76+
setAttribute: DOMAttr
77+
after setAttribute: <root A="v"/>
78+
removeAttribute: bool(false)
79+
still present after removeAttribute: bool(true)
80+
toggleAttribute(false): bool(true)
81+
still present after toggleAttribute(false): bool(true)
82+
toggleAttribute(true): bool(true)
83+
still present after toggleAttribute(true): bool(true)
84+
setAttributeNode: NULL
85+
after setAttributeNode: <root A="z"/>
86+
--- <!ATTLIST root p:A CDATA #FIXED "d"> ---
87+
hasAttribute: bool(true)
88+
getAttribute: string(1) "d"
89+
setAttribute: DOMAttr
90+
after setAttribute: <root xmlns:p="urn:p" p:A="v"/>
91+
removeAttribute: bool(false)
92+
still present after removeAttribute: bool(true)
93+
toggleAttribute(false): bool(true)
94+
still present after toggleAttribute(false): bool(true)
95+
toggleAttribute(true): bool(true)
96+
still present after toggleAttribute(true): bool(true)
97+
setAttributeNode: NULL
98+
after setAttributeNode: <root xmlns:p="urn:p" p:A="z"/>

0 commit comments

Comments
 (0)