Skip to content

Commit

Permalink
Set DOMAttr::$value without expanding entities
Browse files Browse the repository at this point in the history
The manual refers to the DOM Level 3 Core spec which says:

"On setting, this creates a Text node with the unparsed contents of the
string. I.e. any characters that an XML processor would recognize as
markup are instead treated as literal text."

PHP is expanding entities when DOMAttr::value is set, which is
non-compliant and is a difference in behaviour compared to browser DOM
implementations.

So, when value is set, remove all children of the attribute node. Then
create a single text node and insert that as the only child of the
attribute.

Add tests.
  • Loading branch information
tstarling authored and nielsdos committed Jun 5, 2023
1 parent 61e1f8a commit 50fdad8
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 1 deletion.
11 changes: 10 additions & 1 deletion ext/dom/attr.c
Expand Up @@ -136,6 +136,7 @@ int dom_attr_value_write(dom_object *obj, zval *newval)
{
zend_string *str;
xmlAttrPtr attrp = (xmlAttrPtr) dom_object_get_node(obj);
xmlNodePtr node, next;

if (attrp == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
Expand All @@ -149,9 +150,17 @@ int dom_attr_value_write(dom_object *obj, zval *newval)

if (attrp->children) {
node_list_unlink(attrp->children);
node = attrp->children;
while (node) {
next = node->next;
xmlUnlinkNode(node);
xmlFreeNode(node);
node = next;
}
}

xmlNodeSetContentLen((xmlNodePtr) attrp, (xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str) + 1);
node = xmlNewTextLen((xmlChar *) ZSTR_VAL(str), ZSTR_LEN(str));
xmlAddChild((xmlNodePtr) attrp, node);

zend_string_release_ex(str, 0);
return SUCCESS;
Expand Down
30 changes: 30 additions & 0 deletions ext/dom/tests/DOMAttr_entity_expansion.phpt
@@ -0,0 +1,30 @@
--TEST--
DOMAttr entity expansion
--FILE--
<?php
$doc = new DOMDocument;
$elt = $doc->createElement('elt');
$doc->appendChild($elt);
$elt->setAttribute('a','&');
print $doc->saveXML($elt) . "\n";

$attr = $elt->getAttributeNode('a');
$attr->value = '&amp;';
print $doc->saveXML($elt) . "\n";

$attr->removeChild($attr->firstChild);
print $doc->saveXML($elt) . "\n";

$elt->setAttributeNS('http://www.w3.org/2000/svg', 'svg:id','&amp;');
print $doc->saveXML($elt) . "\n";

$attr = $elt->getAttributeNodeNS('http://www.w3.org/2000/svg', 'id');
$attr->value = '&lt;&amp;';
print $doc->saveXML($elt) . "\n";

--EXPECT--
<elt a="&amp;"/>
<elt a="&amp;amp;"/>
<elt a=""/>
<elt xmlns:svg="http://www.w3.org/2000/svg" a="" svg:id="&amp;amp;"/>
<elt xmlns:svg="http://www.w3.org/2000/svg" a="" svg:id="&amp;lt;&amp;amp;"/>

0 comments on commit 50fdad8

Please sign in to comment.