-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Fix DOMElement::append() and DOMElement::prepend() hierarchy checks #11344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -255,10 +255,33 @@ static void dom_fragment_assign_parent_node(xmlNodePtr parentNode, xmlNodePtr fr | |||||
fragment->last = NULL; | ||||||
} | ||||||
|
||||||
static zend_result dom_hierarchy_node_list(xmlNodePtr parentNode, zval *nodes, int nodesc) | ||||||
{ | ||||||
for (int i = 0; i < nodesc; i++) { | ||||||
if (Z_TYPE(nodes[i]) == IS_OBJECT) { | ||||||
const zend_class_entry *ce = Z_OBJCE(nodes[i]); | ||||||
|
||||||
if (instanceof_function(ce, dom_node_class_entry)) { | ||||||
if (dom_hierarchy(parentNode, dom_object_get_node(Z_DOMOBJ_P(nodes + i))) != SUCCESS) { | ||||||
return FAILURE; | ||||||
} | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
return SUCCESS; | ||||||
} | ||||||
|
||||||
void dom_parent_node_append(dom_object *context, zval *nodes, int nodesc) | ||||||
{ | ||||||
xmlNode *parentNode = dom_object_get_node(context); | ||||||
xmlNodePtr newchild, prevsib; | ||||||
|
||||||
if (UNEXPECTED(dom_hierarchy_node_list(parentNode, nodes, nodesc) != SUCCESS)) { | ||||||
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(context->document)); | ||||||
return; | ||||||
} | ||||||
|
||||||
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); | ||||||
|
||||||
if (fragment == NULL) { | ||||||
|
@@ -296,6 +319,11 @@ void dom_parent_node_prepend(dom_object *context, zval *nodes, int nodesc) | |||||
return; | ||||||
} | ||||||
|
||||||
if (UNEXPECTED(dom_hierarchy_node_list(parentNode, nodes, nodesc) != SUCCESS)) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for using
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a habit of doing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Either is fine frankly, I think I was told about the shorter instruction once. I believe that one day |
||||||
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(context->document)); | ||||||
return; | ||||||
} | ||||||
|
||||||
xmlNodePtr newchild, nextsib; | ||||||
xmlNode *fragment = dom_zvals_to_fragment(context->document, parentNode, nodes, nodesc); | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
--TEST-- | ||
DOMElement::append() with hierarchy changes and errors | ||
--EXTENSIONS-- | ||
dom | ||
--FILE-- | ||
<?php | ||
|
||
$dom_original = new DOMDocument; | ||
$dom_original->loadXML('<p><b>hello</b><b><i>world</i></b></p>'); | ||
|
||
echo "-- Append hello with world --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
$b_hello->append($b_world); | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Append hello with world's child --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
$b_hello->append($b_world->firstChild); | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Append world's child with hello --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
$b_world->firstChild->append($b_hello); | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Append hello with itself --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
try { | ||
$b_hello->append($b_hello); | ||
} catch (\DOMException $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Append world's i tag with the parent --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
try { | ||
$b_world->firstChild->append($b_world); | ||
} catch (\DOMException $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Append from another document --\n"; | ||
$dom = clone $dom_original; | ||
$dom2 = new DOMDocument; | ||
$dom2->loadXML('<p>other</p>'); | ||
try { | ||
$dom->firstChild->firstChild->prepend($dom2->firstChild); | ||
} catch (\DOMException $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($dom2->saveHTML()); | ||
var_dump($dom->saveHTML()); | ||
|
||
?> | ||
--EXPECT-- | ||
-- Append hello with world -- | ||
string(39) "<p><b>hello<b><i>world</i></b></b></p> | ||
" | ||
-- Append hello with world's child -- | ||
string(39) "<p><b>hello<i>world</i></b><b></b></p> | ||
" | ||
-- Append world's child with hello -- | ||
string(39) "<p><b><i>world<b>hello</b></i></b></p> | ||
" | ||
-- Append hello with itself -- | ||
Hierarchy Request Error | ||
string(39) "<p><b>hello</b><b><i>world</i></b></p> | ||
" | ||
-- Append world's i tag with the parent -- | ||
Hierarchy Request Error | ||
string(39) "<p><b>hello</b><b><i>world</i></b></p> | ||
" | ||
-- Append from another document -- | ||
Wrong Document Error | ||
string(13) "<p>other</p> | ||
" | ||
string(39) "<p><b>hello</b><b><i>world</i></b></p> | ||
" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
--TEST-- | ||
DOMElement::prepend() with hierarchy changes and errors | ||
--EXTENSIONS-- | ||
dom | ||
--FILE-- | ||
<?php | ||
|
||
$dom_original = new DOMDocument; | ||
$dom_original->loadXML('<p><b>hello</b><b><i>world</i></b></p>'); | ||
|
||
echo "-- Prepend hello with world --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
$b_hello->prepend($b_world); | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Prepend hello with world's child --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
$b_hello->prepend($b_world->firstChild); | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Prepend world's child with hello --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
$b_world->firstChild->prepend($b_hello); | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Prepend hello with itself --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
try { | ||
$b_hello->prepend($b_hello); | ||
} catch (\DOMException $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Prepend world's i tag with the parent --\n"; | ||
$dom = clone $dom_original; | ||
$b_hello = $dom->firstChild->firstChild; | ||
$b_world = $b_hello->nextSibling; | ||
try { | ||
$b_world->firstChild->prepend($b_world); | ||
} catch (\DOMException $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($dom->saveHTML()); | ||
|
||
echo "-- Append from another document --\n"; | ||
$dom = clone $dom_original; | ||
$dom2 = new DOMDocument; | ||
$dom2->loadXML('<p>other</p>'); | ||
try { | ||
$dom->firstChild->firstChild->prepend($dom2->firstChild); | ||
} catch (\DOMException $e) { | ||
echo $e->getMessage(), "\n"; | ||
} | ||
var_dump($dom2->saveHTML()); | ||
var_dump($dom->saveHTML()); | ||
|
||
?> | ||
--EXPECT-- | ||
-- Prepend hello with world -- | ||
string(39) "<p><b><b><i>world</i></b>hello</b></p> | ||
" | ||
-- Prepend hello with world's child -- | ||
string(39) "<p><b><i>world</i>hello</b><b></b></p> | ||
" | ||
-- Prepend world's child with hello -- | ||
string(39) "<p><b><i><b>hello</b>world</i></b></p> | ||
" | ||
-- Prepend hello with itself -- | ||
Hierarchy Request Error | ||
string(39) "<p><b>hello</b><b><i>world</i></b></p> | ||
" | ||
-- Prepend world's i tag with the parent -- | ||
Hierarchy Request Error | ||
string(39) "<p><b>hello</b><b><i>world</i></b></p> | ||
" | ||
-- Append from another document -- | ||
Wrong Document Error | ||
string(13) "<p>other</p> | ||
" | ||
string(39) "<p><b>hello</b><b><i>world</i></b></p> | ||
" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, can a node not be an object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spec says you may insert strings, so it can be IS_STRING too. In that case we create a text node, but since that's a new object it cannot violate the dom hierarchy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I didn't think that the zval node list could contain anything other than objects, as I would have expected free strings to always be encoded as
DOMText
object. Just having wrong assumptions.