Skip to content

Commit d38cc9b

Browse files
committed
Implement DOMNode::isConnected and DOMNameSpaceNode::isConnected
ref: https://dom.spec.whatwg.org/#dom-node-isconnected Closes GH-11677.
1 parent 9c5cf65 commit d38cc9b

17 files changed

+144
-23
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PHP NEWS
2525
. Added DOMNode::getRootNode(). (nielsdos)
2626
. Added DOMElement::className and DOMElement::id. (nielsdos)
2727
. Added DOMParentNode::replaceChildren(). (nielsdos)
28+
. Added DOMNode::isConnected and DOMNameSpaceNode::isConnected. (nielsdos)
2829

2930
- FPM:
3031
. Added warning to log when fpm socket was not registered on the expected

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ PHP 8.3 UPGRADE NOTES
268268
This is not binary-safe at the moment because of underlying limitations of
269269
libxml2.
270270
. Added DOMParentNode::replaceChildren().
271+
. Added DOMNode::isConnected and DOMNameSpaceNode::isConnected.
271272

272273
- JSON:
273274
. Added json_validate(), which returns whether the json is valid for

ext/dom/document.c

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -994,19 +994,6 @@ PHP_METHOD(DOMDocument, getElementsByTagNameNS)
994994
}
995995
/* }}} end dom_document_get_elements_by_tag_name_ns */
996996

997-
static bool php_dom_is_node_attached(const xmlNode *node)
998-
{
999-
ZEND_ASSERT(node != NULL);
1000-
node = node->parent;
1001-
while (node != NULL) {
1002-
if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
1003-
return true;
1004-
}
1005-
node = node->parent;
1006-
}
1007-
return false;
1008-
}
1009-
1010997
/* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-getElBId
1011998
Since: DOM Level 2
1012999
*/
@@ -1035,7 +1022,7 @@ PHP_METHOD(DOMDocument, getElementById)
10351022
* ingrained in the library, and uses the cache for various purposes, it seems like a bad
10361023
* idea and lost cause to fight it. Instead, we'll simply walk the tree upwards to check
10371024
* if the node is attached to the document. */
1038-
if (attrp && attrp->parent && php_dom_is_node_attached(attrp->parent)) {
1025+
if (attrp && attrp->parent && php_dom_is_node_connected(attrp->parent)) {
10391026
DOM_RET_OBJ((xmlNodePtr) attrp->parent, &ret, intern);
10401027
} else {
10411028
RETVAL_NULL();

ext/dom/dom_properties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ int dom_node_next_sibling_read(dom_object *obj, zval *retval);
107107
int dom_node_previous_element_sibling_read(dom_object *obj, zval *retval);
108108
int dom_node_next_element_sibling_read(dom_object *obj, zval *retval);
109109
int dom_node_attributes_read(dom_object *obj, zval *retval);
110+
zend_result dom_node_is_connected_read(dom_object *obj, zval *retval);
110111
int dom_node_owner_document_read(dom_object *obj, zval *retval);
111112
int dom_node_namespace_uri_read(dom_object *obj, zval *retval);
112113
int dom_node_prefix_read(dom_object *obj, zval *retval);

ext/dom/node.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep)
5252
}
5353
}
5454

55+
bool php_dom_is_node_connected(const xmlNode *node)
56+
{
57+
ZEND_ASSERT(node != NULL);
58+
do {
59+
if (node->type == XML_DOCUMENT_NODE || node->type == XML_HTML_DOCUMENT_NODE) {
60+
return true;
61+
}
62+
node = node->parent;
63+
} while (node != NULL);
64+
return false;
65+
}
66+
5567
/* {{{ nodeName string
5668
readonly=yes
5769
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-F68D095
@@ -488,6 +500,25 @@ int dom_node_attributes_read(dom_object *obj, zval *retval)
488500

489501
/* }}} */
490502

503+
/* {{{ isConnected boolean
504+
readonly=yes
505+
URL: https://dom.spec.whatwg.org/#dom-node-isconnected
506+
Since:
507+
*/
508+
zend_result dom_node_is_connected_read(dom_object *obj, zval *retval)
509+
{
510+
xmlNode *nodep = dom_object_get_node(obj);
511+
512+
if (nodep == NULL) {
513+
php_dom_throw_error(INVALID_STATE_ERR, 1);
514+
return FAILURE;
515+
}
516+
517+
ZVAL_BOOL(retval, php_dom_is_node_connected(nodep));
518+
return SUCCESS;
519+
}
520+
/* }}} */
521+
491522
/* {{{ ownerDocument DomDocument
492523
readonly=yes
493524
URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-node-ownerDoc

ext/dom/php_dom.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ PHP_MINIT_FUNCTION(dom)
642642
dom_register_prop_handler(&dom_node_prop_handlers, "previousSibling", sizeof("previousSibling")-1, dom_node_previous_sibling_read, NULL);
643643
dom_register_prop_handler(&dom_node_prop_handlers, "nextSibling", sizeof("nextSibling")-1, dom_node_next_sibling_read, NULL);
644644
dom_register_prop_handler(&dom_node_prop_handlers, "attributes", sizeof("attributes")-1, dom_node_attributes_read, NULL);
645+
dom_register_prop_handler(&dom_node_prop_handlers, "isConnected", sizeof("isConnected")-1, dom_node_is_connected_read, NULL);
645646
dom_register_prop_handler(&dom_node_prop_handlers, "ownerDocument", sizeof("ownerDocument")-1, dom_node_owner_document_read, NULL);
646647
dom_register_prop_handler(&dom_node_prop_handlers, "namespaceURI", sizeof("namespaceURI")-1, dom_node_namespace_uri_read, NULL);
647648
dom_register_prop_handler(&dom_node_prop_handlers, "prefix", sizeof("prefix")-1, dom_node_prefix_read, dom_node_prefix_write);
@@ -660,6 +661,7 @@ PHP_MINIT_FUNCTION(dom)
660661
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "prefix", sizeof("prefix")-1, dom_node_prefix_read, NULL);
661662
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "localName", sizeof("localName")-1, dom_node_local_name_read, NULL);
662663
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "namespaceURI", sizeof("namespaceURI")-1, dom_node_namespace_uri_read, NULL);
664+
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "isConnected", sizeof("isConnected")-1, dom_node_is_connected_read, NULL);
663665
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "ownerDocument", sizeof("ownerDocument")-1, dom_node_owner_document_read, NULL);
664666
dom_register_prop_handler(&dom_namespace_node_prop_handlers, "parentNode", sizeof("parentNode")-1, dom_node_parent_node_read, NULL);
665667
zend_hash_add_ptr(&classes, dom_namespace_node_class_entry->name, &dom_namespace_node_prop_handlers);

ext/dom/php_dom.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@ xmlNodePtr php_dom_create_fake_namespace_decl(xmlNodePtr nodep, xmlNsPtr origina
150150
void php_dom_get_content_into_zval(const xmlNode *nodep, zval *target, bool default_is_null);
151151
zend_string *dom_node_concatenated_name_helper(size_t name_len, const char *name, size_t prefix_len, const char *prefix);
152152
zend_string *dom_node_get_node_name_attribute_or_element(const xmlNode *nodep);
153+
bool php_dom_is_node_connected(const xmlNode *node);
153154

155+
/* parentnode */
154156
void dom_parent_node_prepend(dom_object *context, zval *nodes, uint32_t nodesc);
155157
void dom_parent_node_append(dom_object *context, zval *nodes, uint32_t nodesc);
156158
void dom_parent_node_after(dom_object *context, zval *nodes, uint32_t nodesc);

ext/dom/php_dom.stub.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ class DOMNode
326326
/** @readonly */
327327
public ?DOMNamedNodeMap $attributes;
328328

329+
/** @readonly */
330+
public bool $isConnected;
331+
329332
/** @readonly */
330333
public ?DOMDocument $ownerDocument;
331334

@@ -419,6 +422,9 @@ class DOMNameSpaceNode
419422
/** @readonly */
420423
public ?string $namespaceURI;
421424

425+
/** @readonly */
426+
public bool $isConnected;
427+
422428
/** @readonly */
423429
public ?DOMDocument $ownerDocument;
424430

ext/dom/php_dom_arginfo.h

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
DOMNode::isConnected and DOMNameSpaceNode::isConnected
3+
--EXTENSIONS--
4+
dom
5+
--FILE--
6+
<?php
7+
8+
$dom = new DOMDocument();
9+
$dom->loadXML('<!DOCTYPE html><html><head/><body/></html>');
10+
11+
$docElement = $dom->documentElement;
12+
$head = $docElement->firstChild;
13+
$body = $head->nextSibling;
14+
15+
echo "--- Created element not connected yet ---\n";
16+
17+
$p = $dom->createElement('p');
18+
var_dump($p->isConnected);
19+
20+
echo "--- Appending and checking connection isn't broken for parents ---\n";
21+
22+
$body->appendChild($p);
23+
var_dump($body->isConnected);
24+
var_dump($p->isConnected);
25+
$document = $docElement->parentNode;
26+
var_dump($document->isConnected);
27+
var_dump($dom->doctype->isConnected);
28+
29+
echo "--- Indirect removal should set isConnected to false for affected nodes ---\n";
30+
31+
$body->remove();
32+
var_dump($p->isConnected);
33+
var_dump($docElement->isConnected);
34+
var_dump($body->isConnected);
35+
var_dump($head->isConnected);
36+
var_dump($dom->doctype->isConnected);
37+
38+
echo "--- Empty document test ---\n";
39+
40+
$dom = new DOMDocument();
41+
var_dump($dom->isConnected);
42+
43+
?>
44+
--EXPECT--
45+
--- Created element not connected yet ---
46+
bool(false)
47+
--- Appending and checking connection isn't broken for parents ---
48+
bool(true)
49+
bool(true)
50+
bool(true)
51+
bool(true)
52+
--- Indirect removal should set isConnected to false for affected nodes ---
53+
bool(false)
54+
bool(true)
55+
bool(false)
56+
bool(true)
57+
bool(true)
58+
--- Empty document test ---
59+
bool(true)

ext/dom/tests/bug69846.phpt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ foreach ($dataNodes AS $node) {
3030
?>
3131
--EXPECTF--
3232
int(3)
33-
object(DOMText)#%d (21) {
33+
object(DOMText)#%d (22) {
3434
["wholeText"]=>
3535
string(3) "
3636
"
@@ -64,6 +64,8 @@ object(DOMText)#%d (21) {
6464
NULL
6565
["attributes"]=>
6666
NULL
67+
["isConnected"]=>
68+
bool(false)
6769
["ownerDocument"]=>
6870
string(22) "(object value omitted)"
6971
["namespaceURI"]=>
@@ -78,7 +80,7 @@ object(DOMText)#%d (21) {
7880
string(3) "
7981
"
8082
}
81-
object(DOMElement)#7 (25) {
83+
object(DOMElement)#7 (26) {
8284
["schemaTypeInfo"]=>
8385
NULL
8486
["tagName"]=>
@@ -121,6 +123,8 @@ object(DOMElement)#7 (25) {
121123
NULL
122124
["attributes"]=>
123125
string(22) "(object value omitted)"
126+
["isConnected"]=>
127+
bool(false)
124128
["ownerDocument"]=>
125129
string(22) "(object value omitted)"
126130
["namespaceURI"]=>
@@ -138,7 +142,7 @@ object(DOMElement)#7 (25) {
138142
Value C
139143
"
140144
}
141-
object(DOMText)#%d (21) {
145+
object(DOMText)#%d (22) {
142146
["wholeText"]=>
143147
string(1) "
144148
"
@@ -172,6 +176,8 @@ object(DOMText)#%d (21) {
172176
NULL
173177
["attributes"]=>
174178
NULL
179+
["isConnected"]=>
180+
bool(false)
175181
["ownerDocument"]=>
176182
string(22) "(object value omitted)"
177183
["namespaceURI"]=>

ext/dom/tests/bug70359.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ DOMNameSpaceNode Object
5757
[prefix] =>
5858
[localName] => xmlns
5959
[namespaceURI] => http://www.sitemaps.org/schemas/sitemap/0.9
60+
[isConnected] => 1
6061
[ownerDocument] => (object value omitted)
6162
[parentNode] => (object value omitted)
6263
)
@@ -73,6 +74,7 @@ DOMNameSpaceNode Object
7374
[prefix] => xsi
7475
[localName] => xsi
7576
[namespaceURI] => fooooooooooooooooooooo
77+
[isConnected] => 1
7678
[ownerDocument] => (object value omitted)
7779
[parentNode] => (object value omitted)
7880
)

ext/dom/tests/bug78577.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ var_dump($attr);
1313

1414
?>
1515
--EXPECT--
16-
object(DOMNameSpaceNode)#3 (8) {
16+
object(DOMNameSpaceNode)#3 (9) {
1717
["nodeName"]=>
1818
string(5) "xmlns"
1919
["nodeValue"]=>
@@ -26,6 +26,8 @@ object(DOMNameSpaceNode)#3 (8) {
2626
string(5) "xmlns"
2727
["namespaceURI"]=>
2828
string(19) "http://php.net/test"
29+
["isConnected"]=>
30+
bool(true)
2931
["ownerDocument"]=>
3032
string(22) "(object value omitted)"
3133
["parentNode"]=>

ext/dom/tests/bug80602_3.phpt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ var_dump($target);
2121
?>
2222
--EXPECTF--
2323
<a>barfoobaz<last/></a>
24-
object(DOMElement)#3 (25) {
24+
object(DOMElement)#3 (26) {
2525
["schemaTypeInfo"]=>
2626
NULL
2727
["tagName"]=>
@@ -60,6 +60,8 @@ object(DOMElement)#3 (25) {
6060
NULL
6161
["attributes"]=>
6262
string(22) "(object value omitted)"
63+
["isConnected"]=>
64+
bool(true)
6365
["ownerDocument"]=>
6466
string(22) "(object value omitted)"
6567
["namespaceURI"]=>
@@ -74,7 +76,7 @@ object(DOMElement)#3 (25) {
7476
string(0) ""
7577
}
7678
<a><last/>barfoobaz</a>
77-
object(DOMElement)#2 (25) {
79+
object(DOMElement)#2 (26) {
7880
["schemaTypeInfo"]=>
7981
NULL
8082
["tagName"]=>
@@ -113,6 +115,8 @@ object(DOMElement)#2 (25) {
113115
string(22) "(object value omitted)"
114116
["attributes"]=>
115117
string(22) "(object value omitted)"
118+
["isConnected"]=>
119+
bool(true)
116120
["ownerDocument"]=>
117121
string(22) "(object value omitted)"
118122
["namespaceURI"]=>

ext/dom/tests/clone_nodes.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ var_dump($barClone->parentNode);
4444
?>
4545
--EXPECT--
4646
-- Clone DOMNameSpaceNode --
47-
object(DOMNameSpaceNode)#3 (8) {
47+
object(DOMNameSpaceNode)#3 (9) {
4848
["nodeName"]=>
4949
string(5) "xmlns"
5050
["nodeValue"]=>
@@ -57,6 +57,8 @@ object(DOMNameSpaceNode)#3 (8) {
5757
string(5) "xmlns"
5858
["namespaceURI"]=>
5959
string(19) "http://php.net/test"
60+
["isConnected"]=>
61+
bool(true)
6062
["ownerDocument"]=>
6163
string(22) "(object value omitted)"
6264
["parentNode"]=>

ext/dom/tests/domobject_debug_handler.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ DOMDocument Object
5454
[previousSibling] =>
5555
[nextSibling] =>
5656
[attributes] =>
57+
[isConnected] => 1
5758
[ownerDocument] =>
5859
[namespaceURI] =>
5960
[prefix] =>

0 commit comments

Comments
 (0)