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)

0 commit comments

Comments
 (0)