diff --git a/ext/dom/dom_properties.h b/ext/dom/dom_properties.h
index 32f2419323833..d5bee5e7c2f19 100644
--- a/ext/dom/dom_properties.h
+++ b/ext/dom/dom_properties.h
@@ -95,6 +95,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 14fa030ccda8b..c744a15e51424 100644
--- a/ext/dom/node.c
+++ b/ext/dom/node.c
@@ -225,24 +225,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;
}
@@ -251,6 +244,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 453b4d95ecc91..a4b4202ae7d66 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);
@@ -662,6 +663,7 @@ PHP_MINIT_FUNCTION(dom)
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "namespaceURI", sizeof("namespaceURI")-1, dom_node_namespace_uri_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 741f7bf1d9e10..db05df93848aa 100644
--- a/ext/dom/php_dom.stub.php
+++ b/ext/dom/php_dom.stub.php
@@ -305,6 +305,9 @@ class DOMNode
/** @readonly */
public ?DOMNode $parentNode;
+ /** @readonly */
+ public ?DOMElement $parentElement;
+
/** @readonly */
public DOMNodeList $childNodes;
@@ -417,6 +420,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 1459a9891bb08..590fc7048c376 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: 00d59fd45c44eb14bbf8f51ee4f61e0464786d69 */
+ * Stub hash: 1178fd913f9d1dbc919d76985bddd8c98e560711 */
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)
@@ -1064,6 +1064,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);
@@ -1204,6 +1211,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 0000000000000..dec4ac227f74c
--- /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 7655d203fa22f..37448307778b8 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 (21) {
+object(DOMText)#7 (22) {
["wholeText"]=>
string(3) "
"
@@ -52,6 +52,8 @@ object(DOMText)#%d (21) {
int(3)
["parentNode"]=>
NULL
+ ["parentElement"]=>
+ NULL
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
@@ -78,7 +80,7 @@ object(DOMText)#%d (21) {
string(3) "
"
}
-object(DOMElement)#7 (23) {
+object(DOMElement)#7 (24) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
@@ -105,6 +107,8 @@ object(DOMElement)#7 (23) {
int(1)
["parentNode"]=>
NULL
+ ["parentElement"]=>
+ NULL
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
@@ -134,7 +138,7 @@ object(DOMElement)#7 (23) {
Value C
"
}
-object(DOMText)#%d (21) {
+object(DOMText)#7 (22) {
["wholeText"]=>
string(1) "
"
@@ -156,6 +160,8 @@ object(DOMText)#%d (21) {
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 697097bf88287..3dd86a075aa02 100644
--- a/ext/dom/tests/bug70359.phpt
+++ b/ext/dom/tests/bug70359.phpt
@@ -59,6 +59,7 @@ DOMNameSpaceNode Object
[namespaceURI] => http://www.sitemaps.org/schemas/sitemap/0.9
[ownerDocument] => (object value omitted)
[parentNode] => (object value omitted)
+ [parentElement] => (object value omitted)
)
-- Test with parent and non-ns attribute --
int(2)
@@ -75,5 +76,6 @@ DOMNameSpaceNode Object
[namespaceURI] => fooooooooooooooooooooo
[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 2631efc1e206c..4d140f5ca5a60 100644
--- a/ext/dom/tests/bug78577.phpt
+++ b/ext/dom/tests/bug78577.phpt
@@ -13,7 +13,7 @@ var_dump($attr);
?>
--EXPECT--
-object(DOMNameSpaceNode)#3 (8) {
+object(DOMNameSpaceNode)#3 (9) {
["nodeName"]=>
string(5) "xmlns"
["nodeValue"]=>
@@ -30,4 +30,6 @@ object(DOMNameSpaceNode)#3 (8) {
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 f9bf67e778da5..72b60b80c11cc 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 (23) {
+object(DOMElement)#3 (24) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
@@ -44,6 +44,8 @@ object(DOMElement)#3 (23) {
int(1)
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
["childNodes"]=>
string(22) "(object value omitted)"
["firstChild"]=>
@@ -70,7 +72,7 @@ object(DOMElement)#3 (23) {
string(0) ""
}
barfoobaz
-object(DOMElement)#2 (23) {
+object(DOMElement)#2 (24) {
["schemaTypeInfo"]=>
NULL
["tagName"]=>
@@ -93,6 +95,8 @@ object(DOMElement)#2 (23) {
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 1841c702caf8d..647b95da04462 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 (8) {
+object(DOMNameSpaceNode)#3 (9) {
["nodeName"]=>
string(5) "xmlns"
["nodeValue"]=>
@@ -61,6 +61,8 @@ object(DOMNameSpaceNode)#3 (8) {
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 8318fc70ed17e..e90cfa0d8615c 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 97059c18e54da..1d483284b3c8b 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 (8) {
+object(DOMNameSpaceNode)#4 (9) {
["nodeName"]=>
string(9) "xmlns:xml"
["nodeValue"]=>
@@ -34,4 +34,6 @@ object(DOMNameSpaceNode)#4 (8) {
string(22) "(object value omitted)"
["parentNode"]=>
string(22) "(object value omitted)"
+ ["parentElement"]=>
+ string(22) "(object value omitted)"
}