Skip to content

Commit 1590b64

Browse files
committed
Implement Document::$body setter
1 parent c13f5e2 commit 1590b64

File tree

6 files changed

+176
-11
lines changed

6 files changed

+176
-11
lines changed

ext/dom/dom_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ zend_result dom_document_substitue_entities_write(dom_object *obj, zval *newval)
6565
/* html5 document properties */
6666
zend_result dom_html_document_encoding_write(dom_object *obj, zval *retval);
6767
zend_result dom_html_document_body_read(dom_object *obj, zval *retval);
68+
zend_result dom_html_document_body_write(dom_object *obj, zval *newval);
6869
zend_result dom_html_document_head_read(dom_object *obj, zval *retval);
6970

7071
/* documenttype properties */

ext/dom/html_document.c

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,26 +1359,35 @@ zend_result dom_html_document_encoding_write(dom_object *obj, zval *newval)
13591359
return SUCCESS;
13601360
}
13611361

1362-
zend_result dom_html_document_element_read_helper(dom_object *obj, zval *retval, bool (*accept)(const xmlChar *))
1362+
static const xmlNode *dom_html_document_element_read_raw(const xmlDoc *docp, bool (*accept)(const xmlChar *))
13631363
{
1364-
DOM_PROP_NODE(const xmlDoc *, docp, obj);
1365-
13661364
const xmlNode *root = xmlDocGetRootElement(docp);
13671365
if (root == NULL || !(php_dom_ns_is_fast(root, php_dom_ns_is_html_magic_token) && xmlStrEqual(root->name, BAD_CAST "html"))) {
1368-
ZVAL_NULL(retval);
1369-
return SUCCESS;
1366+
return NULL;
13701367
}
13711368

1372-
xmlNodePtr cur = root->children;
1369+
const xmlNode *cur = root->children;
13731370
while (cur != NULL) {
13741371
if (cur->type == XML_ELEMENT_NODE && php_dom_ns_is_fast(cur, php_dom_ns_is_html_magic_token) && accept(cur->name)) {
1375-
php_dom_create_object(cur, retval, obj);
1376-
return SUCCESS;
1372+
return cur;
13771373
}
13781374
cur = cur->next;
13791375
}
13801376

1381-
ZVAL_NULL(retval);
1377+
return NULL;
1378+
}
1379+
1380+
zend_result dom_html_document_element_read_helper(dom_object *obj, zval *retval, bool (*accept)(const xmlChar *))
1381+
{
1382+
DOM_PROP_NODE(const xmlDoc *, docp, obj);
1383+
1384+
const xmlNode *element = dom_html_document_element_read_raw(docp, accept);
1385+
if (element == NULL) {
1386+
ZVAL_NULL(retval);
1387+
} else {
1388+
php_dom_create_object((xmlNodePtr) element, retval, obj);
1389+
}
1390+
13821391
return SUCCESS;
13831392
}
13841393

@@ -1404,4 +1413,50 @@ zend_result dom_html_document_head_read(dom_object *obj, zval *retval)
14041413
return dom_html_document_element_read_helper(obj, retval, dom_accept_head_name);
14051414
}
14061415

1416+
/* https://html.spec.whatwg.org/#dom-document-body */
1417+
zend_result dom_html_document_body_write(dom_object *obj, zval *newval)
1418+
{
1419+
DOM_PROP_NODE(xmlDocPtr, docp, obj);
1420+
1421+
/* 1. If the new value is not a body or frameset element, then throw a "HierarchyRequestError" DOMException. */
1422+
if (Z_TYPE_P(newval) != IS_NULL) {
1423+
dom_object *newval_intern = Z_DOMOBJ_P(newval);
1424+
if (newval_intern->ptr != NULL) {
1425+
xmlNodePtr newval_node = ((php_libxml_node_ptr *) newval_intern->ptr)->node;
1426+
if (php_dom_ns_is_fast(newval_node, php_dom_ns_is_html_magic_token) && dom_accept_body_name(newval_node->name)) {
1427+
/* 2. If the new value is the same as the body element, return. */
1428+
const xmlNode *current_body_element = dom_html_document_element_read_raw(docp, dom_accept_body_name);
1429+
if (current_body_element == newval_node) {
1430+
return SUCCESS;
1431+
}
1432+
1433+
/* 3. If the body element is not null, then replace the body element with the new value within the body element's parent and return. */
1434+
if (current_body_element != NULL) {
1435+
php_dom_adopt_node(newval_node, obj, docp);
1436+
xmlNodePtr old = xmlReplaceNode((xmlNodePtr) current_body_element, newval_node);
1437+
if (old != NULL && old->_private == NULL) {
1438+
php_libxml_node_free_resource(old);
1439+
}
1440+
return SUCCESS;
1441+
}
1442+
1443+
/* 4. If there is no document element, throw a "HierarchyRequestError" DOMException. */
1444+
xmlNodePtr root = xmlDocGetRootElement(docp);
1445+
if (root == NULL) {
1446+
php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "A body can only be set if there is a document element", true);
1447+
return FAILURE;
1448+
}
1449+
1450+
/* 5. Append the new value to the document element. */
1451+
php_dom_adopt_node(newval_node, obj, docp);
1452+
xmlAddChild(root, newval_node);
1453+
return SUCCESS;
1454+
}
1455+
}
1456+
}
1457+
1458+
php_dom_throw_error_with_message(HIERARCHY_REQUEST_ERR, "The new body must either be a body or a frameset tag", true);
1459+
return FAILURE;
1460+
}
1461+
14071462
#endif /* HAVE_LIBXML && HAVE_DOM */

ext/dom/php_dom.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -844,7 +844,7 @@ PHP_MINIT_FUNCTION(dom)
844844
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
845845
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
846846
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
847-
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, NULL);
847+
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, dom_html_document_body_write);
848848
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "head", dom_html_document_head_read, NULL);
849849
zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
850850
/* No need to register in &classes because this is an abstract class handler. */

ext/dom/tests/modern/html/interactions/Document_body.phpt renamed to ext/dom/tests/modern/html/interactions/Document_body_getter.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Test DOM\Document::$body
2+
Test DOM\Document::$body getter
33
--EXTENSIONS--
44
dom
55
--FILE--
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
Test DOM\Document::$body setter
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
echo "--- Replace body with itself ---\n";
9+
$dom = DOM\HTMLDocument::createFromString('<p>foo</p>', LIBXML_NOERROR);
10+
$dom->body = $dom->body;
11+
var_dump($dom->body?->nodeName);
12+
13+
echo "--- Add body when there is no body yet ---\n";
14+
$dom = DOM\HTMLDocument::createFromString('<p>foo</p>', LIBXML_NOERROR);
15+
$dom->body->remove();
16+
$dom->body = $dom->createElementNS("http://www.w3.org/1999/xhtml", "prefix:body");
17+
var_dump($dom->body?->nodeName);
18+
19+
echo "--- Replace old body with new body ---\n";
20+
$dom = DOM\HTMLDocument::createFromString('<p>foo</p>', LIBXML_NOERROR);
21+
$dom->body = $dom->createElementNS("http://www.w3.org/1999/xhtml", "prefix:body");
22+
var_dump($dom->body?->nodeName);
23+
24+
echo "--- Replace old body with new body, while still having a reference to the old body ---\n";
25+
$dom = DOM\HTMLDocument::createFromString('<p>foo</p>', LIBXML_NOERROR);
26+
$old = $dom->body;
27+
$dom->body = $dom->createElementNS("http://www.w3.org/1999/xhtml", "prefix:body");
28+
var_dump($dom->body?->nodeName);
29+
var_dump($old->nodeName);
30+
31+
echo "--- Special note from the DOM spec ---\n";
32+
$dom = DOM\XMLDocument::createFromString('<svg xmlns="http://www.w3.org/2000/svg"/>');
33+
$dom->body = $dom->createElementNS("http://www.w3.org/1999/xhtml", "body");
34+
var_dump($dom->body?->nodeName);
35+
36+
?>
37+
--EXPECT--
38+
--- Replace body with itself ---
39+
string(4) "BODY"
40+
--- Add body when there is no body yet ---
41+
string(11) "PREFIX:BODY"
42+
--- Replace old body with new body ---
43+
string(11) "PREFIX:BODY"
44+
--- Replace old body with new body, while still having a reference to the old body ---
45+
string(11) "PREFIX:BODY"
46+
string(4) "BODY"
47+
--- Special note from the DOM spec ---
48+
NULL
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
--TEST--
2+
Test DOM\Document::$body setter errors
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
function testNormalReplace($cb)
9+
{
10+
$dom = DOM\HTMLDocument::createFromString('<p>foo</p>', LIBXML_NOERROR);
11+
var_dump($dom->body?->nodeName);
12+
try {
13+
$dom->body = $cb($dom);
14+
} catch (DOMException $e) {
15+
echo $e->getMessage(), "\n";
16+
}
17+
var_dump($dom->body?->nodeName);
18+
}
19+
20+
echo "--- Set body to NULL ---\n";
21+
testNormalReplace(fn ($dom) => NULL);
22+
23+
echo "--- Wrong element tag in right namespace ---\n";
24+
testNormalReplace(fn ($dom) => $dom->createElementNS("http://www.w3.org/1999/xhtml", "foo"));
25+
26+
echo "--- Right element tag in wrong namespace ---\n";
27+
testNormalReplace(fn ($dom) => $dom->createElementNS("urn:a", "body"));
28+
29+
echo "--- Right element tag in no namespace ---\n";
30+
testNormalReplace(fn ($dom) => $dom->createElementNS("", "frameset"));
31+
32+
echo "--- Set body without document element ---\n";
33+
$dom = DOM\XMLDocument::createEmpty();
34+
try {
35+
$dom->body = $dom->createElementNS("http://www.w3.org/1999/xhtml", "body");
36+
} catch (DOMException $e) {
37+
echo $e->getMessage(), "\n";
38+
}
39+
var_dump($dom->body?->nodeName);
40+
41+
?>
42+
--EXPECT--
43+
--- Set body to NULL ---
44+
string(4) "BODY"
45+
The new body must either be a body or a frameset tag
46+
string(4) "BODY"
47+
--- Wrong element tag in right namespace ---
48+
string(4) "BODY"
49+
The new body must either be a body or a frameset tag
50+
string(4) "BODY"
51+
--- Right element tag in wrong namespace ---
52+
string(4) "BODY"
53+
The new body must either be a body or a frameset tag
54+
string(4) "BODY"
55+
--- Right element tag in no namespace ---
56+
string(4) "BODY"
57+
The new body must either be a body or a frameset tag
58+
string(4) "BODY"
59+
--- Set body without document element ---
60+
A body can only be set if there is a document element
61+
NULL

0 commit comments

Comments
 (0)