From d04f48b6ac6125fa69539c44796bb2fdb88e0193 Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+nielsdos@users.noreply.github.com>
Date: Tue, 11 Jul 2023 19:43:37 +0200
Subject: [PATCH] Implement DOMNode::parentElement and
DOMNameSpaceNode::parentElement
ref: https://dom.spec.whatwg.org/#parent-element
Closes GH-11679.
---
NEWS | 2 +
UPGRADING | 1 +
ext/dom/dom_properties.h | 1 +
ext/dom/node.c | 37 ++++++++----
ext/dom/php_dom.c | 2 +
ext/dom/php_dom.stub.php | 6 ++
ext/dom/php_dom_arginfo.h | 16 +++++-
...OMNode_DOMNameSpaceNode_parentElement.phpt | 57 +++++++++++++++++++
ext/dom/tests/bug69846.phpt | 14 +++--
ext/dom/tests/bug70359.phpt | 2 +
ext/dom/tests/bug78577.phpt | 4 +-
ext/dom/tests/bug80602_3.phpt | 8 ++-
ext/dom/tests/clone_nodes.phpt | 4 +-
ext/dom/tests/domobject_debug_handler.phpt | 1 +
ext/dom/tests/xpath_domnamespacenode.phpt | 4 +-
15 files changed, 138 insertions(+), 21 deletions(-)
create mode 100644 ext/dom/tests/DOMNode_DOMNameSpaceNode_parentElement.phpt
diff --git a/NEWS b/NEWS
index 58a8bbf7b08..6d9df6bc389 100644
--- a/NEWS
+++ b/NEWS
@@ -26,6 +26,8 @@ PHP NEWS
. Added DOMElement::className and DOMElement::id. (nielsdos)
. Added DOMParentNode::replaceChildren(). (nielsdos)
. Added DOMNode::isConnected and DOMNameSpaceNode::isConnected. (nielsdos)
+ . Added DOMNode::parentElement and DOMNameSpaceNode::parentElement.
+ (nielsdos)
- FPM:
. Added warning to log when fpm socket was not registered on the expected
diff --git a/UPGRADING b/UPGRADING
index 59f60415211..d551d68eb3c 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -269,6 +269,7 @@ PHP 8.3 UPGRADE NOTES
libxml2.
. Added DOMParentNode::replaceChildren().
. Added DOMNode::isConnected and DOMNameSpaceNode::isConnected.
+ . Added DOMNode::parentElement and DOMNameSpaceNode::parentElement.
- JSON:
. Added json_validate(), which returns whether the json is valid for
diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h
index 6e86d3897b8..09e35514a5e 100644
--- a/ext/dom/dom_properties.h
+++ b/ext/dom/dom_properties.h
@@ -99,6 +99,7 @@ int dom_node_node_value_read(dom_object *obj, zval *retval);
int dom_node_node_value_write(dom_object *obj, zval *newval);
int dom_node_node_type_read(dom_object *obj, zval *retval);
int dom_node_parent_node_read(dom_object *obj, zval *retval);
+zend_result dom_node_parent_element_read(dom_object *obj, zval *retval);
int dom_node_child_nodes_read(dom_object *obj, zval *retval);
int dom_node_first_child_read(dom_object *obj, zval *retval);
int dom_node_last_child_read(dom_object *obj, zval *retval);
diff --git a/ext/dom/node.c b/ext/dom/node.c
index 386b787d826..647c84c7748 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -237,24 +237,17 @@ int dom_node_node_type_read(dom_object *obj, zval *retval)
/* }}} */
-/* {{{ parentNode DomNode
-readonly=yes
-URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1060184317
-Since:
-*/
-int dom_node_parent_node_read(dom_object *obj, zval *retval)
+static zend_result dom_node_parent_get(dom_object *obj, zval *retval, bool only_element)
{
- xmlNode *nodep, *nodeparent;
-
- nodep = dom_object_get_node(obj);
+ xmlNodePtr nodep = dom_object_get_node(obj);
if (nodep == NULL) {
php_dom_throw_error(INVALID_STATE_ERR, 1);
return FAILURE;
}
- nodeparent = nodep->parent;
- if (!nodeparent) {
+ xmlNodePtr nodeparent = nodep->parent;
+ if (!nodeparent || (only_element && nodeparent->type != XML_ELEMENT_NODE)) {
ZVAL_NULL(retval);
return SUCCESS;
}
@@ -263,6 +256,28 @@ int dom_node_parent_node_read(dom_object *obj, zval *retval)
return SUCCESS;
}
+/* {{{ parentNode ?DomNode
+readonly=yes
+URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-1060184317
+Since:
+*/
+int dom_node_parent_node_read(dom_object *obj, zval *retval)
+{
+ return dom_node_parent_get(obj, retval, false);
+}
+
+/* }}} */
+
+/* {{{ parentElement ?DomElement
+readonly=yes
+URL: https://dom.spec.whatwg.org/#parent-element
+Since:
+*/
+zend_result dom_node_parent_element_read(dom_object *obj, zval *retval)
+{
+ return dom_node_parent_get(obj, retval, true);
+}
+
/* }}} */
/* {{{ childNodes DomNodeList
diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c
index b9edaa2d187..5a066db8d70 100644
--- a/ext/dom/php_dom.c
+++ b/ext/dom/php_dom.c
@@ -636,6 +636,7 @@ PHP_MINIT_FUNCTION(dom)
dom_register_prop_handler(&dom_node_prop_handlers, "nodeValue", sizeof("nodeValue")-1, dom_node_node_value_read, dom_node_node_value_write);
dom_register_prop_handler(&dom_node_prop_handlers, "nodeType", sizeof("nodeType")-1, dom_node_node_type_read, NULL);
dom_register_prop_handler(&dom_node_prop_handlers, "parentNode", sizeof("parentNode")-1, dom_node_parent_node_read, NULL);
+ dom_register_prop_handler(&dom_node_prop_handlers, "parentElement", sizeof("parentElement")-1, dom_node_parent_element_read, NULL);
dom_register_prop_handler(&dom_node_prop_handlers, "childNodes", sizeof("childNodes")-1, dom_node_child_nodes_read, NULL);
dom_register_prop_handler(&dom_node_prop_handlers, "firstChild", sizeof("firstChild")-1, dom_node_first_child_read, NULL);
dom_register_prop_handler(&dom_node_prop_handlers, "lastChild", sizeof("lastChild")-1, dom_node_last_child_read, NULL);
@@ -664,6 +665,7 @@ PHP_MINIT_FUNCTION(dom)
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "isConnected", sizeof("isConnected")-1, dom_node_is_connected_read, NULL);
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "ownerDocument", sizeof("ownerDocument")-1, dom_node_owner_document_read, NULL);
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "parentNode", sizeof("parentNode")-1, dom_node_parent_node_read, NULL);
+ dom_register_prop_handler(&dom_namespace_node_prop_handlers, "parentElement", sizeof("parentElement")-1, dom_node_parent_element_read, NULL);
zend_hash_add_ptr(&classes, dom_namespace_node_class_entry->name, &dom_namespace_node_prop_handlers);
dom_documentfragment_class_entry = register_class_DOMDocumentFragment(dom_node_class_entry, dom_parentnode_class_entry);
diff --git a/ext/dom/php_dom.stub.php b/ext/dom/php_dom.stub.php
index bb6b918d5cd..b637f211e75 100644
--- a/ext/dom/php_dom.stub.php
+++ b/ext/dom/php_dom.stub.php
@@ -308,6 +308,9 @@ class DOMNode
/** @readonly */
public ?DOMNode $parentNode;
+ /** @readonly */
+ public ?DOMElement $parentElement;
+
/** @readonly */
public DOMNodeList $childNodes;
@@ -430,6 +433,9 @@ class DOMNameSpaceNode
/** @readonly */
public ?DOMNode $parentNode;
+
+ /** @readonly */
+ public ?DOMElement $parentElement;
}
class DOMImplementation
diff --git a/ext/dom/php_dom_arginfo.h b/ext/dom/php_dom_arginfo.h
index d71ad1310b2..dddca2c3394 100644
--- a/ext/dom/php_dom_arginfo.h
+++ b/ext/dom/php_dom_arginfo.h
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
- * Stub hash: 78e4089fa17aa0faa371917aeb48fa0dfe1819b0 */
+ * Stub hash: 02e8c582a3a7d88fc32ef8931c23c0c6de5a94e2 */
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_dom_import_simplexml, 0, 1, DOMElement, 0)
ZEND_ARG_TYPE_INFO(0, node, IS_OBJECT, 0)
@@ -1096,6 +1096,13 @@ static zend_class_entry *register_class_DOMNode(void)
zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOMNode, 0, MAY_BE_NULL));
zend_string_release(property_parentNode_name);
+ zval property_parentElement_default_value;
+ ZVAL_UNDEF(&property_parentElement_default_value);
+ zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1);
+ zend_string *property_parentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1);
+ zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOMElement, 0, MAY_BE_NULL));
+ zend_string_release(property_parentElement_name);
+
zval property_childNodes_default_value;
ZVAL_UNDEF(&property_childNodes_default_value);
zend_string *property_childNodes_name = zend_string_init("childNodes", sizeof("childNodes") - 1, 1);
@@ -1248,6 +1255,13 @@ static zend_class_entry *register_class_DOMNameSpaceNode(void)
zend_declare_typed_property(class_entry, property_parentNode_name, &property_parentNode_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentNode_class_DOMNode, 0, MAY_BE_NULL));
zend_string_release(property_parentNode_name);
+ zval property_parentElement_default_value;
+ ZVAL_UNDEF(&property_parentElement_default_value);
+ zend_string *property_parentElement_name = zend_string_init("parentElement", sizeof("parentElement") - 1, 1);
+ zend_string *property_parentElement_class_DOMElement = zend_string_init("DOMElement", sizeof("DOMElement")-1, 1);
+ zend_declare_typed_property(class_entry, property_parentElement_name, &property_parentElement_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_parentElement_class_DOMElement, 0, MAY_BE_NULL));
+ zend_string_release(property_parentElement_name);
+
return class_entry;
}
diff --git a/ext/dom/tests/DOMNode_DOMNameSpaceNode_parentElement.phpt b/ext/dom/tests/DOMNode_DOMNameSpaceNode_parentElement.phpt
new file mode 100644
index 00000000000..dec4ac227f7
--- /dev/null
+++ b/ext/dom/tests/DOMNode_DOMNameSpaceNode_parentElement.phpt
@@ -0,0 +1,57 @@
+--TEST--
+DOMNode::parentElement and DOMNameSpaceNode::parentElement
+--EXTENSIONS--
+dom
+--FILE--
+loadXML(<<
+
+
+
+XML);
+
+echo "--- body test ---\n";
+
+$body = $dom->documentElement->firstElementChild;
+var_dump($body->parentNode->localName);
+var_dump($body->parentElement->localName);
+
+echo "--- document test ---\n";
+
+var_dump(get_class($dom->documentElement->parentNode));
+var_dump($dom->documentElement->parentElement);
+
+var_dump(get_class($dom->doctype->parentNode));
+var_dump($dom->doctype->parentElement);
+
+echo "--- fragment test ---\n";
+
+$fragment = $dom->createDocumentFragment();
+$p = $fragment->appendChild($dom->createElement('p'));
+
+var_dump(get_class($p->parentNode));
+var_dump($p->parentElement);
+
+$body->appendChild($fragment);
+
+var_dump($p->parentNode->localName);
+var_dump($p->parentElement->localName);
+
+?>
+--EXPECT--
+--- body test ---
+string(4) "html"
+string(4) "html"
+--- document test ---
+string(11) "DOMDocument"
+NULL
+string(11) "DOMDocument"
+NULL
+--- fragment test ---
+string(19) "DOMDocumentFragment"
+NULL
+string(4) "body"
+string(4) "body"
diff --git a/ext/dom/tests/bug69846.phpt b/ext/dom/tests/bug69846.phpt
index 92b20d0fd34..04631444e89 100644
--- a/ext/dom/tests/bug69846.phpt
+++ b/ext/dom/tests/bug69846.phpt
@@ -28,9 +28,9 @@ foreach ($dataNodes AS $node) {
}
?>
---EXPECTF--
+--EXPECT--
int(3)
-object(DOMText)#%d (22) {
+object(DOMText)#7 (23) {
["wholeText"]=>
string(3) "
"
@@ -52,6 +52,8 @@ object(DOMText)#%d (22) {
int(3)
["parentNode"]=>
NULL
+ ["parentElement"]=>
+ NULL
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
@@ -80,7 +82,7 @@ object(DOMText)#%d (22) {
string(3) "
"
}
-object(DOMElement)#7 (26) {
+object(DOMElement)#7 (27) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
@@ -111,6 +113,8 @@ object(DOMElement)#7 (26) {
int(1)
["parentNode"]=>
NULL
+ ["parentElement"]=>
+ NULL
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
@@ -142,7 +146,7 @@ object(DOMElement)#7 (26) {
Value C
"
}
-object(DOMText)#%d (22) {
+object(DOMText)#7 (23) {
["wholeText"]=>
string(1) "
"
@@ -164,6 +168,8 @@ object(DOMText)#%d (22) {
int(3)
["parentNode"]=>
NULL
+ ["parentElement"]=>
+ NULL
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
diff --git a/ext/dom/tests/bug70359.phpt b/ext/dom/tests/bug70359.phpt
index 442c1c5bc84..d42e96047ba 100644
--- a/ext/dom/tests/bug70359.phpt
+++ b/ext/dom/tests/bug70359.phpt
@@ -60,6 +60,7 @@ DOMNameSpaceNode Object
[isConnected] => 1
[ownerDocument] => (object value omitted)
[parentNode] => (object value omitted)
+ [parentElement] => (object value omitted)
)
-- Test with parent and non-ns attribute --
int(2)
@@ -77,5 +78,6 @@ DOMNameSpaceNode Object
[isConnected] => 1
[ownerDocument] => (object value omitted)
[parentNode] => (object value omitted)
+ [parentElement] => (object value omitted)
)
string(3) "url"
diff --git a/ext/dom/tests/bug78577.phpt b/ext/dom/tests/bug78577.phpt
index e4f97bb1bb6..574500809f8 100644
--- a/ext/dom/tests/bug78577.phpt
+++ b/ext/dom/tests/bug78577.phpt
@@ -13,7 +13,7 @@ var_dump($attr);
?>
--EXPECT--
-object(DOMNameSpaceNode)#3 (9) {
+object(DOMNameSpaceNode)#3 (10) {
["nodeName"]=>
string(5) "xmlns"
["nodeValue"]=>
@@ -32,4 +32,6 @@ object(DOMNameSpaceNode)#3 (9) {
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
}
diff --git a/ext/dom/tests/bug80602_3.phpt b/ext/dom/tests/bug80602_3.phpt
index 6b8fc9839ab..ea494bf504c 100644
--- a/ext/dom/tests/bug80602_3.phpt
+++ b/ext/dom/tests/bug80602_3.phpt
@@ -21,7 +21,7 @@ var_dump($target);
?>
--EXPECTF--
barfoobaz
-object(DOMElement)#3 (26) {
+object(DOMElement)#3 (27) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
@@ -48,6 +48,8 @@ object(DOMElement)#3 (26) {
int(1)
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
@@ -76,7 +78,7 @@ object(DOMElement)#3 (26) {
string(0) ""
}
barfoobaz
-object(DOMElement)#2 (26) {
+object(DOMElement)#2 (27) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
@@ -103,6 +105,8 @@ object(DOMElement)#2 (26) {
int(1)
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
diff --git a/ext/dom/tests/clone_nodes.phpt b/ext/dom/tests/clone_nodes.phpt
index b76b67e911d..92b716f98c9 100644
--- a/ext/dom/tests/clone_nodes.phpt
+++ b/ext/dom/tests/clone_nodes.phpt
@@ -44,7 +44,7 @@ var_dump($barClone->parentNode);
?>
--EXPECT--
-- Clone DOMNameSpaceNode --
-object(DOMNameSpaceNode)#3 (9) {
+object(DOMNameSpaceNode)#3 (10) {
["nodeName"]=>
string(5) "xmlns"
["nodeValue"]=>
@@ -63,6 +63,8 @@ object(DOMNameSpaceNode)#3 (9) {
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
}
string(19) "http://php.net/test"
string(3) "foo"
diff --git a/ext/dom/tests/domobject_debug_handler.phpt b/ext/dom/tests/domobject_debug_handler.phpt
index 5140026b16e..f951b9d4b49 100644
--- a/ext/dom/tests/domobject_debug_handler.phpt
+++ b/ext/dom/tests/domobject_debug_handler.phpt
@@ -48,6 +48,7 @@ DOMDocument Object
[nodeValue] =>
[nodeType] => 9
[parentNode] =>
+ [parentElement] =>
[childNodes] => (object value omitted)
[firstChild] => (object value omitted)
[lastChild] => (object value omitted)
diff --git a/ext/dom/tests/xpath_domnamespacenode.phpt b/ext/dom/tests/xpath_domnamespacenode.phpt
index c15ee6adf63..b7c8196ce64 100644
--- a/ext/dom/tests/xpath_domnamespacenode.phpt
+++ b/ext/dom/tests/xpath_domnamespacenode.phpt
@@ -17,7 +17,7 @@ var_dump($nodes->item(0));
?>
--EXPECT--
-object(DOMNameSpaceNode)#4 (9) {
+object(DOMNameSpaceNode)#4 (10) {
["nodeName"]=>
string(9) "xmlns:xml"
["nodeValue"]=>
@@ -36,4 +36,6 @@ object(DOMNameSpaceNode)#4 (9) {
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
}