Skip to content

Commit b442f9d

Browse files
committed
Implement Document::$body setter
1 parent e8218eb commit b442f9d

File tree

6 files changed

+175
-11
lines changed

6 files changed

+175
-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: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,26 +1366,35 @@ zend_result dom_html_document_encoding_write(dom_object *obj, zval *newval)
13661366
return FAILURE;
13671367
}
13681368

1369-
zend_result dom_html_document_element_read_helper(dom_object *obj, zval *retval, bool (*accept)(const xmlChar *))
1369+
static const xmlNode *dom_html_document_element_read_raw(const xmlDoc *docp, bool (*accept)(const xmlChar *))
13701370
{
1371-
DOM_PROP_NODE(const xmlDoc *, docp, obj);
1372-
13731371
const xmlNode *root = xmlDocGetRootElement(docp);
13741372
if (root == NULL || !(php_dom_ns_is_fast(root, php_dom_ns_is_html_magic_token) && xmlStrEqual(root->name, BAD_CAST "html"))) {
1375-
ZVAL_NULL(retval);
1376-
return SUCCESS;
1373+
return NULL;
13771374
}
13781375

1379-
xmlNodePtr cur = root->children;
1376+
const xmlNode *cur = root->children;
13801377
while (cur != NULL) {
13811378
if (cur->type == XML_ELEMENT_NODE && php_dom_ns_is_fast(cur, php_dom_ns_is_html_magic_token) && accept(cur->name)) {
1382-
php_dom_create_object(cur, retval, obj);
1383-
return SUCCESS;
1379+
return cur;
13841380
}
13851381
cur = cur->next;
13861382
}
13871383

1388-
ZVAL_NULL(retval);
1384+
return NULL;
1385+
}
1386+
1387+
zend_result dom_html_document_element_read_helper(dom_object *obj, zval *retval, bool (*accept)(const xmlChar *))
1388+
{
1389+
DOM_PROP_NODE(const xmlDoc *, docp, obj);
1390+
1391+
const xmlNode *element = dom_html_document_element_read_raw(docp, accept);
1392+
if (element == NULL) {
1393+
ZVAL_NULL(retval);
1394+
} else {
1395+
php_dom_create_object((xmlNodePtr) element, retval, obj);
1396+
}
1397+
13891398
return SUCCESS;
13901399
}
13911400

@@ -1409,4 +1418,49 @@ zend_result dom_html_document_head_read(dom_object *obj, zval *retval)
14091418
return dom_html_document_element_read_helper(obj, retval, dom_accept_head_name);
14101419
}
14111420

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

ext/dom/php_dom.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ PHP_MINIT_FUNCTION(dom)
839839
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "firstElementChild", dom_parent_node_first_element_child_read, NULL);
840840
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "lastElementChild", dom_parent_node_last_element_child_read, NULL);
841841
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "childElementCount", dom_parent_node_child_element_count, NULL);
842-
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, NULL);
842+
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "body", dom_html_document_body_read, dom_html_document_body_write);
843843
DOM_REGISTER_PROP_HANDLER(&dom_abstract_base_document_prop_handlers, "head", dom_html_document_head_read, NULL);
844844
zend_hash_merge(&dom_abstract_base_document_prop_handlers, &dom_modern_node_prop_handlers, NULL, false);
845845
/* 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)