Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit a3b27c0

Browse files
Add Dom\Element::insertAdjacentHTML() (#16614)
1 parent 963511b commit a3b27c0

12 files changed

+417
-2
lines changed

‎NEWS‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ PHP NEWS
1414

1515
- DOM:
1616
. Added Dom\Element::$outerHTML. (nielsdos)
17+
. Added Dom\Element::insertAdjacentHTML(). (nielsdos)
1718

1819
- Output:
1920
. Fixed calculation of aligned buffer size. (cmb)

‎UPGRADING‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ PHP 8.5 UPGRADE NOTES
9292
attached to a CurlMultiHandle. This includes both handles added using
9393
curl_multi_add_handle() and handles accepted by CURLMOPT_PUSHFUNCTION.
9494

95+
- DOM:
96+
. Added Dom\Element::insertAdjacentHTML().
97+
9598
- PGSQL:
9699
. pg_close_stmt offers an alternative way to close a prepared
97100
statement from the DEALLOCATE sql command in that we can reuse

‎ext/dom/element.c‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,6 +1715,98 @@ PHP_METHOD(Dom_Element, insertAdjacentText)
17151715
}
17161716
/* }}} end DOMElement::insertAdjacentText */
17171717

1718+
/* https://html.spec.whatwg.org/#dom-element-insertadjacenthtml */
1719+
PHP_METHOD(Dom_Element, insertAdjacentHTML)
1720+
{
1721+
zval *where_zv;
1722+
zend_string *string;
1723+
1724+
dom_object *this_intern;
1725+
zval *id;
1726+
xmlNodePtr thisp;
1727+
1728+
bool created_context = false;
1729+
1730+
ZEND_PARSE_PARAMETERS_START(2, 2)
1731+
Z_PARAM_OBJECT_OF_CLASS(where_zv, dom_adjacent_position_class_entry)
1732+
Z_PARAM_STR(string)
1733+
ZEND_PARSE_PARAMETERS_END();
1734+
1735+
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, this_intern);
1736+
1737+
const zend_string *where = Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(where_zv)));
1738+
1739+
/* 1. We don't do injection sinks. */
1740+
1741+
/* 2. Let context be NULL */
1742+
xmlNodePtr context = NULL;
1743+
1744+
/* 3. Use the first matching item from this list: (...) */
1745+
switch (ZSTR_LEN(where) + ZSTR_VAL(where)[2]) {
1746+
case sizeof("BeforeBegin") - 1 + 'f':
1747+
case sizeof("AfterEnd") - 1 + 't':
1748+
/* 1. Set context to this's parent. */
1749+
context = thisp->parent;
1750+
1751+
/* 2. If context is null or a Document, throw a "NoModificationAllowedError" DOMException. */
1752+
if (context == NULL || context->type == XML_DOCUMENT_NODE || context->type == XML_HTML_DOCUMENT_NODE) {
1753+
php_dom_throw_error(NO_MODIFICATION_ALLOWED_ERR, true);
1754+
RETURN_THROWS();
1755+
}
1756+
break;
1757+
case sizeof("AfterBegin") - 1 + 't':
1758+
case sizeof("BeforeEnd") - 1 + 'f':
1759+
/* Set context to this. */
1760+
context = thisp;
1761+
break;
1762+
EMPTY_SWITCH_DEFAULT_CASE();
1763+
}
1764+
1765+
/* 4. If context is not an Element or all of the following are true: (...) */
1766+
if (context->type != XML_ELEMENT_NODE
1767+
|| (php_dom_ns_is_html_and_document_is_html(context) && xmlStrEqual(context->name, BAD_CAST "html"))) {
1768+
/* set context to the result of creating an element given this's node document, body, and the HTML namespace. */
1769+
xmlNsPtr html_ns = php_dom_libxml_ns_mapper_ensure_html_ns(php_dom_get_ns_mapper(this_intern));
1770+
1771+
context = xmlNewDocNode(thisp->doc, html_ns, BAD_CAST "body", NULL);
1772+
created_context = true;
1773+
if (UNEXPECTED(context == NULL)) {
1774+
php_dom_throw_error(INVALID_STATE_ERR, true);
1775+
goto err;
1776+
}
1777+
}
1778+
1779+
/* 5. Let fragment be the result of invoking the fragment parsing algorithm steps with context and compliantString. */
1780+
xmlNodePtr fragment = dom_parse_fragment(this_intern, context, string);
1781+
if (fragment == NULL) {
1782+
goto err;
1783+
}
1784+
1785+
php_libxml_invalidate_node_list_cache(this_intern->document);
1786+
1787+
/* 6. Use the first matching item from this list: (...) */
1788+
switch (ZSTR_LEN(where) + ZSTR_VAL(where)[2]) {
1789+
case sizeof("BeforeBegin") - 1 + 'f':
1790+
php_dom_pre_insert(this_intern->document, fragment, thisp->parent, thisp);
1791+
break;
1792+
case sizeof("AfterEnd") - 1 + 't':
1793+
php_dom_pre_insert(this_intern->document, fragment, thisp->parent, thisp->next);
1794+
break;
1795+
case sizeof("AfterBegin") - 1 + 't':
1796+
php_dom_pre_insert(this_intern->document, fragment, thisp, thisp->children);
1797+
break;
1798+
case sizeof("BeforeEnd") - 1 + 'f':
1799+
php_dom_node_append(this_intern->document, fragment, thisp);
1800+
break;
1801+
EMPTY_SWITCH_DEFAULT_CASE();
1802+
}
1803+
1804+
err:
1805+
if (created_context) {
1806+
xmlFreeNode(context);
1807+
}
1808+
}
1809+
17181810
/* {{{ URL: https://dom.spec.whatwg.org/#dom-element-toggleattribute
17191811
Since:
17201812
*/

‎ext/dom/inner_outer_html_mixin.c‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ static xmlNodePtr dom_xml_fragment_parsing_algorithm(dom_object *obj, const xmlN
342342
}
343343

344344
/* https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm */
345-
staticxmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input)
345+
xmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input)
346346
{
347347
if (context_node->doc->type == XML_DOCUMENT_NODE) {
348348
return dom_xml_fragment_parsing_algorithm(obj, context_node, input);

‎ext/dom/php_dom.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *
211211
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
212212
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
213213
void dom_element_closest(xmlNodePtr thisp, dom_object *intern, zval *return_value, const zend_string *selectors_str);
214+
xmlNodePtr dom_parse_fragment(dom_object *obj, xmlNodePtr context_node, const zend_string *input);
214215

215216
/* nodemap and nodelist APIs */
216217
xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform);

‎ext/dom/php_dom.stub.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,7 @@ public function getElementsByTagNameNS(?string $namespace, string $localName): H
16321632

16331633
public function insertAdjacentElement(AdjacentPosition $where, Element $element): ?Element {}
16341634
public function insertAdjacentText(AdjacentPosition $where, string $data): void {}
1635+
public function insertAdjacentHTML(AdjacentPosition $where, string $string): void {}
16351636

16361637
/**
16371638
* @readonly

‎ext/dom/php_dom_arginfo.h‎

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
--TEST--
2+
Dom\Element::insertAdjacentHTML() with HTML nodes
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
const POSITIONS = [
9+
Dom\AdjacentPosition::BeforeBegin,
10+
Dom\AdjacentPosition::AfterBegin,
11+
Dom\AdjacentPosition::BeforeEnd,
12+
Dom\AdjacentPosition::AfterEnd,
13+
];
14+
15+
function test(string $html) {
16+
echo "=== HTML ($html) ===\n";
17+
18+
foreach (POSITIONS as $position) {
19+
echo "--- Position ", $position->name, " ---\n";
20+
21+
$dom = Dom\HTMLDocument::createFromString("<div></div>", LIBXML_NOERROR);
22+
$div = $dom->body->firstChild;
23+
$div->append("Sample text");
24+
25+
$div->insertAdjacentHTML($position, $html);
26+
27+
echo $dom->saveXML(), "\n";
28+
echo $dom->saveHTML(), "\n";
29+
var_dump($div->childNodes->length);
30+
var_dump($dom->body->childNodes->length);
31+
}
32+
}
33+
34+
test("<p>foo</p><p>bar</p>");
35+
test("text");
36+
test("");
37+
38+
?>
39+
--EXPECT--
40+
=== HTML (<p>foo</p><p>bar</p>) ===
41+
--- Position BeforeBegin ---
42+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
43+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><p>foo</p><p>bar</p><div>Sample text</div></body></html>
44+
<html><head></head><body><p>foo</p><p>bar</p><div>Sample text</div></body></html>
45+
int(1)
46+
int(3)
47+
--- Position AfterBegin ---
48+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
49+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div><p>foo</p><p>bar</p>Sample text</div></body></html>
50+
<html><head></head><body><div><p>foo</p><p>bar</p>Sample text</div></body></html>
51+
int(3)
52+
int(1)
53+
--- Position BeforeEnd ---
54+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
55+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text<p>foo</p><p>bar</p></div></body></html>
56+
<html><head></head><body><div>Sample text<p>foo</p><p>bar</p></div></body></html>
57+
int(3)
58+
int(1)
59+
--- Position AfterEnd ---
60+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
61+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text</div><p>foo</p><p>bar</p></body></html>
62+
<html><head></head><body><div>Sample text</div><p>foo</p><p>bar</p></body></html>
63+
int(1)
64+
int(3)
65+
=== HTML (text) ===
66+
--- Position BeforeBegin ---
67+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
68+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>text<div>Sample text</div></body></html>
69+
<html><head></head><body>text<div>Sample text</div></body></html>
70+
int(1)
71+
int(2)
72+
--- Position AfterBegin ---
73+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
74+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>textSample text</div></body></html>
75+
<html><head></head><body><div>textSample text</div></body></html>
76+
int(2)
77+
int(1)
78+
--- Position BeforeEnd ---
79+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
80+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample texttext</div></body></html>
81+
<html><head></head><body><div>Sample texttext</div></body></html>
82+
int(2)
83+
int(1)
84+
--- Position AfterEnd ---
85+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
86+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text</div>text</body></html>
87+
<html><head></head><body><div>Sample text</div>text</body></html>
88+
int(1)
89+
int(2)
90+
=== HTML () ===
91+
--- Position BeforeBegin ---
92+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
93+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text</div></body></html>
94+
<html><head></head><body><div>Sample text</div></body></html>
95+
int(1)
96+
int(1)
97+
--- Position AfterBegin ---
98+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
99+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text</div></body></html>
100+
<html><head></head><body><div>Sample text</div></body></html>
101+
int(1)
102+
int(1)
103+
--- Position BeforeEnd ---
104+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
105+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text</div></body></html>
106+
<html><head></head><body><div>Sample text</div></body></html>
107+
int(1)
108+
int(1)
109+
--- Position AfterEnd ---
110+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
111+
<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body><div>Sample text</div></body></html>
112+
<html><head></head><body><div>Sample text</div></body></html>
113+
int(1)
114+
int(1)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Dom\Element::insertAdjacentHTML() with HTML nodes - edge case
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = Dom\HTMLDocument::createFromString("", LIBXML_NOERROR);
9+
10+
$fragment = $dom->createDocumentFragment();
11+
$node = $fragment->appendChild($dom->createElement("node"));
12+
13+
$node->insertAdjacentHTML(Dom\AdjacentPosition::BeforeBegin, "<p>foo</p>");
14+
15+
echo $dom->saveHtml($fragment), "\n";
16+
17+
$dom->firstChild->insertAdjacentHTML(Dom\AdjacentPosition::AfterBegin, $node->outerHTML);
18+
19+
echo $dom->saveHtml(), "\n";
20+
21+
?>
22+
--EXPECT--
23+
<p>foo</p><node></node>
24+
<html><node></node><head></head><body></body></html>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
--TEST--
2+
Dom\Element::insertAdjacentHTML() with HTML nodes - error conditions
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = Dom\HTMLDocument::createEmpty();
9+
$element = $dom->createElement('root');
10+
11+
echo "--- BeforeBegin no parent ---\n";
12+
13+
try {
14+
$element->insertAdjacentHTML(Dom\AdjacentPosition::BeforeBegin, "test");
15+
} catch (DOMException $e) {
16+
echo $e->getMessage(), "\n";
17+
}
18+
19+
echo "--- AfterEnd no parent ---\n";
20+
21+
try {
22+
$element->insertAdjacentHTML(Dom\AdjacentPosition::AfterEnd, "test");
23+
} catch (DOMException $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
27+
$dom->appendChild($element);
28+
29+
echo "--- BeforeBegin document parent ---\n";
30+
31+
try {
32+
$element->insertAdjacentHTML(Dom\AdjacentPosition::BeforeBegin, "test");
33+
} catch (DOMException $e) {
34+
echo $e->getMessage(), "\n";
35+
}
36+
37+
echo "--- AfterEnd document parent ---\n";
38+
39+
try {
40+
$element->insertAdjacentHTML(Dom\AdjacentPosition::AfterEnd, "test");
41+
} catch (DOMException $e) {
42+
echo $e->getMessage(), "\n";
43+
}
44+
45+
?>
46+
--EXPECT--
47+
--- BeforeBegin no parent ---
48+
No Modification Allowed Error
49+
--- AfterEnd no parent ---
50+
No Modification Allowed Error
51+
--- BeforeBegin document parent ---
52+
No Modification Allowed Error
53+
--- AfterEnd document parent ---
54+
No Modification Allowed Error

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /