Skip to content

Commit 0eb2350

Browse files
committed
1 parent 591f3f6 commit 0eb2350

File tree

7 files changed

+318
-16
lines changed

7 files changed

+318
-16
lines changed

ext/dom/document.c

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,26 @@ static void php_dom_transfer_document_ref(xmlNodePtr node, dom_object *dom_objec
10631063
}
10641064
}
10651065

1066+
bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document)
1067+
{
1068+
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
1069+
if (nodep->doc != new_document) {
1070+
php_libxml_invalidate_node_list_cache_from_doc(new_document);
1071+
1072+
/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
1073+
* and since this unlink it; the owner element will be unset (i.e. parentNode). */
1074+
int ret = xmlDOMWrapAdoptNode(NULL, nodep->doc, nodep, new_document, NULL, /* options, unused */ 0);
1075+
if (UNEXPECTED(ret != 0)) {
1076+
return false;
1077+
}
1078+
1079+
php_dom_transfer_document_ref(nodep, dom_object_new_document, new_document);
1080+
} else {
1081+
xmlUnlinkNode(nodep);
1082+
}
1083+
return true;
1084+
}
1085+
10661086
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-Document3-adoptNode
10671087
Since: DOM Level 3
10681088
Modern spec URL: https://dom.spec.whatwg.org/#dom-document-adoptnode
@@ -1093,21 +1113,8 @@ PHP_METHOD(DOMDocument, adoptNode)
10931113
zval *new_document_zval = ZEND_THIS;
10941114
DOM_GET_OBJ(new_document, new_document_zval, xmlDocPtr, dom_object_new_document);
10951115

1096-
php_libxml_invalidate_node_list_cache_from_doc(nodep->doc);
1097-
1098-
if (nodep->doc != new_document) {
1099-
php_libxml_invalidate_node_list_cache_from_doc(new_document);
1100-
1101-
/* Note for ATTRIBUTE_NODE: specified is always true in ext/dom,
1102-
* and since this unlink it; the owner element will be unset (i.e. parentNode). */
1103-
int ret = xmlDOMWrapAdoptNode(NULL, nodep->doc, nodep, new_document, NULL, /* options, unused */ 0);
1104-
if (UNEXPECTED(ret != 0)) {
1105-
RETURN_FALSE;
1106-
}
1107-
1108-
php_dom_transfer_document_ref(nodep, dom_object_new_document, new_document);
1109-
} else {
1110-
xmlUnlinkNode(nodep);
1116+
if (!php_dom_adopt_node(nodep, dom_object_new_document, new_document)) {
1117+
RETURN_FALSE;
11111118
}
11121119

11131120
RETURN_OBJ_COPY(&dom_object_nodep->std);

ext/dom/element.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1246,4 +1246,118 @@ PHP_METHOD(DOMElement, replaceWith)
12461246
}
12471247
/* }}} end DOMElement::prepend */
12481248

1249+
#define INSERT_ADJACENT_RES_FAILED ((void*) -1)
1250+
1251+
static xmlNodePtr dom_insert_adjacent(const zend_string *where, xmlNodePtr thisp, dom_object *this_intern, xmlNodePtr otherp)
1252+
{
1253+
if (zend_string_equals_literal_ci(where, "beforebegin")) {
1254+
if (thisp->parent == NULL) {
1255+
return NULL;
1256+
}
1257+
if (dom_hierarchy(thisp->parent, otherp) == FAILURE) {
1258+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1259+
return INSERT_ADJACENT_RES_FAILED;
1260+
}
1261+
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1262+
return INSERT_ADJACENT_RES_FAILED;
1263+
}
1264+
otherp = xmlAddPrevSibling(thisp, otherp);
1265+
} else if (zend_string_equals_literal_ci(where, "afterbegin")) {
1266+
if (dom_hierarchy(thisp, otherp) == FAILURE) {
1267+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1268+
return INSERT_ADJACENT_RES_FAILED;
1269+
}
1270+
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1271+
return INSERT_ADJACENT_RES_FAILED;
1272+
}
1273+
if (thisp->children == NULL) {
1274+
otherp = xmlAddChild(thisp, otherp);
1275+
} else {
1276+
otherp = xmlAddPrevSibling(thisp->children, otherp);
1277+
}
1278+
} else if (zend_string_equals_literal_ci(where, "beforeend")) {
1279+
if (dom_hierarchy(thisp, otherp) == FAILURE) {
1280+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1281+
return INSERT_ADJACENT_RES_FAILED;
1282+
}
1283+
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1284+
return INSERT_ADJACENT_RES_FAILED;
1285+
}
1286+
otherp = xmlAddChild(thisp, otherp);
1287+
} else if (zend_string_equals_literal_ci(where, "afterend")) {
1288+
if (thisp->parent == NULL) {
1289+
return NULL;
1290+
}
1291+
if (dom_hierarchy(thisp->parent, otherp) == FAILURE) {
1292+
php_dom_throw_error(HIERARCHY_REQUEST_ERR, dom_get_strict_error(this_intern->document));
1293+
return INSERT_ADJACENT_RES_FAILED;
1294+
}
1295+
if (!php_dom_adopt_node(otherp, this_intern, thisp->doc)) {
1296+
return INSERT_ADJACENT_RES_FAILED;
1297+
}
1298+
otherp = xmlAddNextSibling(thisp, otherp);
1299+
} else {
1300+
php_dom_throw_error(SYNTAX_ERR, dom_get_strict_error(this_intern->document));
1301+
return INSERT_ADJACENT_RES_FAILED;
1302+
}
1303+
dom_reconcile_ns(thisp->doc, otherp);
1304+
return otherp;
1305+
}
1306+
1307+
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
1308+
Since:
1309+
*/
1310+
PHP_METHOD(DOMElement, insertAdjacentElement)
1311+
{
1312+
zend_string *where;
1313+
zval *element_zval, *id;
1314+
xmlNodePtr thisp, otherp;
1315+
dom_object *this_intern, *other_intern;
1316+
int ret;
1317+
1318+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SO", &where, &element_zval, dom_element_class_entry) == FAILURE) {
1319+
RETURN_THROWS();
1320+
}
1321+
1322+
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1323+
DOM_GET_OBJ(otherp, element_zval, xmlNodePtr, other_intern);
1324+
1325+
xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1326+
if (result == NULL) {
1327+
RETURN_NULL();
1328+
} else if (result != INSERT_ADJACENT_RES_FAILED) {
1329+
DOM_RET_OBJ(otherp, &ret, other_intern);
1330+
}
1331+
}
1332+
/* }}} end DOMElement::insertAdjacentElement */
1333+
1334+
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
1335+
Since:
1336+
*/
1337+
PHP_METHOD(DOMElement, insertAdjacentText)
1338+
{
1339+
zend_string *where, *data;
1340+
dom_object *this_intern;
1341+
zval *id;
1342+
xmlNodePtr thisp;
1343+
1344+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &where, &data) == FAILURE) {
1345+
RETURN_THROWS();
1346+
}
1347+
1348+
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1349+
1350+
if (UNEXPECTED(ZEND_SIZE_T_INT_OVFL(ZSTR_LEN(data)))) {
1351+
zend_argument_value_error(2, "is too long");
1352+
RETURN_THROWS();
1353+
}
1354+
1355+
xmlNodePtr otherp = xmlNewDocTextLen(thisp->doc, (const xmlChar *) ZSTR_VAL(data), ZSTR_LEN(data));
1356+
xmlNodePtr result = dom_insert_adjacent(where, thisp, this_intern, otherp);
1357+
if (result == NULL || result == INSERT_ADJACENT_RES_FAILED) {
1358+
xmlFreeNode(otherp);
1359+
}
1360+
}
1361+
/* }}} end DOMElement::insertAdjacentText */
1362+
12491363
#endif

ext/dom/php_dom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr origina
150150
void php_dom_get_content_into_zval(const xmlNode *nodep, zval *target, bool default_is_null);
151151
zend_string *dom_node_concatenated_name_helper(size_t name_len, const char *name, size_t prefix_len, const char *prefix);
152152
zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep);
153+
bool php_dom_adopt_node(xmlNodePtr nodep, dom_object *dom_object_new_document, xmlDocPtr new_document);
153154

154155
void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc);
155156
void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc);

ext/dom/php_dom.stub.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,10 @@ public function append(...$nodes): void {}
634634

635635
/** @param DOMNode|string $nodes */
636636
public function prepend(...$nodes): void {}
637+
638+
public function insertAdjacentElement(string $where, DOMElement $element): ?DOMElement {}
639+
640+
public function insertAdjacentText(string $where, string $data): void {}
637641
}
638642

639643
class DOMDocument extends DOMNode implements DOMParentNode

ext/dom/php_dom_arginfo.h

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
--TEST--
2+
DOMElement::insertAdjacentElement()
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = new DOMDocument();
9+
$dom->loadXML('<?xml version="1.0"?><container><p>foo</p></container>');
10+
$container = $dom->documentElement;
11+
$p = $container->firstElementChild;
12+
13+
echo "--- Edge cases ---\n";
14+
15+
var_dump($dom->createElement('free')->insertAdjacentElement("beforebegin", $dom->createElement('element')));
16+
var_dump($dom->createElement('free')->insertAdjacentElement("afterend", $dom->createElement('element')));
17+
18+
try {
19+
var_dump($dom->createElement('free')->insertAdjacentElement("bogus", $dom->createElement('element')));
20+
} catch (DOMException $e) {
21+
echo $e->getMessage(), "\n";
22+
}
23+
24+
echo "--- Hierarchy test ---\n";
25+
26+
$element = $dom->createElement('free');
27+
$child = $element->appendChild($dom->createElement('child'));
28+
foreach (['beforebegin', 'afterbegin', 'beforeend', 'afterend'] as $where) {
29+
try {
30+
var_dump($child->insertAdjacentElement($where, $element)->tagName);
31+
} catch (DOMException $e) {
32+
echo $e->getMessage(), "\n";
33+
}
34+
}
35+
36+
echo "--- Normal cases ---\n";
37+
38+
var_dump($p->insertAdjacentElement("beforebegin", $dom->createElement('A'))->tagName);
39+
echo $dom->saveXML();
40+
41+
var_dump($p->insertAdjacentElement("afterbegin", $dom->createElement('B'))->tagName);
42+
echo $dom->saveXML();
43+
44+
var_dump($p->insertAdjacentElement("beforeend", $dom->createElement('C'))->tagName);
45+
echo $dom->saveXML();
46+
47+
var_dump($p->insertAdjacentElement("afterend", $dom->createElement('D'))->tagName);
48+
echo $dom->saveXML();
49+
50+
$empty = $dom->createElement('empty');
51+
var_dump($empty->insertAdjacentElement("afterbegin", $dom->createElement('A'))->tagName);
52+
echo $dom->saveXML($empty), "\n";
53+
54+
echo "--- Namespace test ---\n";
55+
56+
$dom->loadXML('<?xml version="1.0"?><container xmlns:foo="some:ns"/>');
57+
$dom->documentElement->insertAdjacentElement("afterbegin", $dom->createElementNS("some:ns", "bar"));
58+
echo $dom->saveXML();
59+
60+
echo "--- Two document test ---\n";
61+
62+
$dom1 = new DOMDocument();
63+
$dom1->loadXML('<?xml version="1.0"?><container><div/></container>');
64+
$dom2 = new DOMDocument();
65+
$dom2->loadXML('<?xml version="1.0"?><container><p/></container>');
66+
$dom1->documentElement->firstChild->insertAdjacentElement('afterbegin', $dom2->documentElement->firstChild);
67+
echo $dom1->saveXML();
68+
echo $dom2->saveXML();
69+
70+
?>
71+
--EXPECT--
72+
--- Edge cases ---
73+
NULL
74+
NULL
75+
Syntax Error
76+
--- Hierarchy test ---
77+
Hierarchy Request Error
78+
Hierarchy Request Error
79+
Hierarchy Request Error
80+
Hierarchy Request Error
81+
--- Normal cases ---
82+
string(1) "A"
83+
<?xml version="1.0"?>
84+
<container><A/><p>foo</p></container>
85+
string(1) "B"
86+
<?xml version="1.0"?>
87+
<container><A/><p><B/>foo</p></container>
88+
string(1) "C"
89+
<?xml version="1.0"?>
90+
<container><A/><p><B/>foo<C/></p></container>
91+
string(1) "D"
92+
<?xml version="1.0"?>
93+
<container><A/><p><B/>foo<C/></p><D/></container>
94+
string(1) "A"
95+
<empty><A/></empty>
96+
--- Namespace test ---
97+
<?xml version="1.0"?>
98+
<container xmlns:foo="some:ns"><foo:bar/></container>
99+
--- Two document test ---
100+
<?xml version="1.0"?>
101+
<container><div><p/></div></container>
102+
<?xml version="1.0"?>
103+
<container/>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
DOMElement::insertAdjacentText()
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = new DOMDocument();
9+
$dom->loadXML('<?xml version="1.0"?><container><p>foo</p></container>');
10+
$container = $dom->documentElement;
11+
$p = $container->firstElementChild;
12+
13+
echo "--- Edge cases ---\n";
14+
15+
try {
16+
$dom->createElement('free')->insertAdjacentText("bogus", "bogus");
17+
} catch (DOMException $e) {
18+
echo $e->getMessage(), "\n";
19+
}
20+
21+
echo "--- Normal cases ---\n";
22+
23+
$p->insertAdjacentText("beforebegin", 'A');
24+
echo $dom->saveXML();
25+
26+
$p->insertAdjacentText("afterbegin", 'B');
27+
echo $dom->saveXML();
28+
29+
$p->insertAdjacentText("beforeend", 'C');
30+
echo $dom->saveXML();
31+
32+
$p->insertAdjacentText("afterend", 'D');
33+
echo $dom->saveXML();
34+
35+
$empty = $dom->createElement('empty');
36+
$empty->insertAdjacentText("afterbegin", 'A');
37+
echo $dom->saveXML($empty), "\n";
38+
39+
$AText = $empty->firstChild;
40+
$empty->insertAdjacentText("afterbegin", 'B');
41+
echo $dom->saveXML($empty), "\n";
42+
var_dump($AText->textContent);
43+
44+
?>
45+
--EXPECT--
46+
--- Edge cases ---
47+
Syntax Error
48+
--- Normal cases ---
49+
<?xml version="1.0"?>
50+
<container>A<p>foo</p></container>
51+
<?xml version="1.0"?>
52+
<container>A<p>Bfoo</p></container>
53+
<?xml version="1.0"?>
54+
<container>A<p>BfooC</p></container>
55+
<?xml version="1.0"?>
56+
<container>A<p>BfooC</p>D</container>
57+
<empty>A</empty>
58+
<empty>BA</empty>
59+
string(2) "BA"

0 commit comments

Comments
 (0)