Skip to content

Commit 4ae349f

Browse files
committed
Implement Document::$body setter
1 parent adefce1 commit 4ae349f

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
@@ -63,6 +63,7 @@ zend_result dom_document_substitue_entities_write(dom_object *obj, zval *newval)
6363
/* html5 document properties */
6464
zend_result dom_html_document_encoding_write(dom_object *obj, zval *retval);
6565
zend_result dom_html_document_body_read(dom_object *obj, zval *retval);
66+
zend_result dom_html_document_body_write(dom_object *obj, zval *newval);
6667
zend_result dom_html_document_head_read(dom_object *obj, zval *retval);
6768

6869
/* documenttype properties */

ext/dom/html_document.c

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

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

1371-
xmlNodePtr cur = root->children;
1368+
const xmlNode *cur = root->children;
13721369
while (cur != NULL) {
13731370
if (cur->type == XML_ELEMENT_NODE && php_dom_ns_is_fast(cur, php_dom_ns_is_html_magic_token) && accept(cur->name)) {
1374-
php_dom_create_object(cur, retval, obj);
1375-
return SUCCESS;
1371+
return cur;
13761372
}
13771373
cur = cur->next;
13781374
}
13791375

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

@@ -1403,4 +1412,50 @@ zend_result dom_html_document_head_read(dom_object *obj, zval *retval)
14031412
return dom_html_document_element_read_helper(obj, retval, dom_accept_head_name);
14041413
}
14051414

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

ext/dom/php_dom.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -849,7 +849,7 @@ PHP_MINIT_FUNCTION(dom)
849849
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
850850
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
851851
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
852-
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, NULL);
852+
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, dom_html_document_body_write);
853853
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "head", dom_html_document_head_read, NULL);
854854
zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
855855
/* 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)