mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Add DOMNode::compareDocumentPosition() (#12146)
Reference: https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
This commit is contained in:
3
NEWS
3
NEWS
@@ -8,4 +8,7 @@ Core:
|
||||
. Fixed bug GH-12102 (Incorrect compile error when using array access on TMP
|
||||
value in function call). (ilutov)
|
||||
|
||||
DOM:
|
||||
. Added DOMNode::compareDocumentPosition(). (nielsdos)
|
||||
|
||||
<<< NOTE: Insert NEWS from last stable release here prior to actual release! >>>
|
||||
|
||||
18
UPGRADING
18
UPGRADING
@@ -19,10 +19,25 @@ PHP 8.4 UPGRADE NOTES
|
||||
1. Backward Incompatible Changes
|
||||
========================================
|
||||
|
||||
- DOM:
|
||||
. New methods and constants were added to some DOM classes. If you inherit
|
||||
from these and you happen to have a method or property with the same name,
|
||||
you might encounter errors if the declaration is incompatible.
|
||||
Consult sections 2. New Features and 6. New Functions for a list of
|
||||
newly implemented methods and constants.
|
||||
|
||||
========================================
|
||||
2. New Features
|
||||
========================================
|
||||
|
||||
- DOM:
|
||||
. Added constant DOMNode::DOCUMENT_POSITION_DISCONNECTED.
|
||||
. Added constant DOMNode::DOCUMENT_POSITION_PRECEDING.
|
||||
. Added constant DOMNode::DOCUMENT_POSITION_FOLLOWING.
|
||||
. Added constant DOMNode::DOCUMENT_POSITION_CONTAINS.
|
||||
. Added constant DOMNode::DOCUMENT_POSITION_CONTAINED_BY.
|
||||
. Added constant DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC.
|
||||
|
||||
========================================
|
||||
3. Changes in SAPI modules
|
||||
========================================
|
||||
@@ -39,6 +54,9 @@ PHP 8.4 UPGRADE NOTES
|
||||
6. New Functions
|
||||
========================================
|
||||
|
||||
- DOM:
|
||||
. Added DOMNode::compareDocumentPosition().
|
||||
|
||||
========================================
|
||||
7. New Classes and Interfaces
|
||||
========================================
|
||||
|
||||
179
ext/dom/node.c
179
ext/dom/node.c
@@ -1518,6 +1518,15 @@ static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other
|
||||
PHP_DOM_DEFINE_LIST_EQUALITY_HELPER(xmlNode)
|
||||
PHP_DOM_DEFINE_LIST_EQUALITY_HELPER(xmlNs)
|
||||
|
||||
static bool php_dom_is_equal_attr(const xmlAttr *this_attr, const xmlAttr *other_attr)
|
||||
{
|
||||
ZEND_ASSERT(this_attr != NULL);
|
||||
ZEND_ASSERT(other_attr != NULL);
|
||||
return xmlStrEqual(this_attr->name, other_attr->name)
|
||||
&& php_dom_node_is_ns_uri_equal((const xmlNode *) this_attr, (const xmlNode *) other_attr)
|
||||
&& php_dom_node_is_content_equal((const xmlNode *) this_attr, (const xmlNode *) other_attr);
|
||||
}
|
||||
|
||||
static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other)
|
||||
{
|
||||
ZEND_ASSERT(this != NULL);
|
||||
@@ -1552,9 +1561,7 @@ static bool php_dom_node_is_equal_node(const xmlNode *this, const xmlNode *other
|
||||
} else if (this->type == XML_ATTRIBUTE_NODE) {
|
||||
const xmlAttr *this_attr = (const xmlAttr *) this;
|
||||
const xmlAttr *other_attr = (const xmlAttr *) other;
|
||||
return xmlStrEqual(this_attr->name, other_attr->name)
|
||||
&& php_dom_node_is_ns_uri_equal(this, other)
|
||||
&& php_dom_node_is_content_equal(this, other);
|
||||
return php_dom_is_equal_attr(this_attr, other_attr);
|
||||
} else if (this->type == XML_ENTITY_REF_NODE) {
|
||||
return xmlStrEqual(this->name, other->name);
|
||||
} else if (this->type == XML_ENTITY_DECL || this->type == XML_NOTATION_NODE || this->type == XML_ENTITY_NODE) {
|
||||
@@ -2030,4 +2037,170 @@ PHP_METHOD(DOMNode, getRootNode)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ URL: https://dom.spec.whatwg.org/#dom-node-comparedocumentposition (last check date 2023-07-24)
|
||||
Since:
|
||||
*/
|
||||
|
||||
#define DOCUMENT_POSITION_DISCONNECTED 0x01
|
||||
#define DOCUMENT_POSITION_PRECEDING 0x02
|
||||
#define DOCUMENT_POSITION_FOLLOWING 0x04
|
||||
#define DOCUMENT_POSITION_CONTAINS 0x08
|
||||
#define DOCUMENT_POSITION_CONTAINED_BY 0x10
|
||||
#define DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 0x20
|
||||
|
||||
PHP_METHOD(DOMNode, compareDocumentPosition)
|
||||
{
|
||||
zval *id, *node_zval;
|
||||
xmlNodePtr other, this;
|
||||
dom_object *this_intern, *other_intern;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &node_zval, dom_node_class_entry) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
DOM_GET_THIS_OBJ(this, id, xmlNodePtr, this_intern);
|
||||
DOM_GET_OBJ(other, node_zval, xmlNodePtr, other_intern);
|
||||
|
||||
/* Step 1 */
|
||||
if (this == other) {
|
||||
RETURN_LONG(0);
|
||||
}
|
||||
|
||||
/* Step 2 */
|
||||
xmlNodePtr node1 = other;
|
||||
xmlNodePtr node2 = this;
|
||||
|
||||
/* Step 3 */
|
||||
xmlNodePtr attr1 = NULL;
|
||||
xmlNodePtr attr2 = NULL;
|
||||
|
||||
/* Step 4 */
|
||||
if (node1->type == XML_ATTRIBUTE_NODE) {
|
||||
attr1 = node1;
|
||||
node1 = attr1->parent;
|
||||
}
|
||||
|
||||
/* Step 5 */
|
||||
if (node2->type == XML_ATTRIBUTE_NODE) {
|
||||
/* 5.1 */
|
||||
attr2 = node2;
|
||||
node2 = attr2->parent;
|
||||
|
||||
/* 5.2 */
|
||||
if (attr1 != NULL && node1 != NULL && node2 == node1) {
|
||||
for (const xmlAttr *attr = node2->properties; attr != NULL; attr = attr->next) {
|
||||
if (php_dom_is_equal_attr(attr, (const xmlAttr *) attr1)) {
|
||||
RETURN_LONG(DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING);
|
||||
} else if (php_dom_is_equal_attr(attr, (const xmlAttr *) attr2)) {
|
||||
RETURN_LONG(DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_FOLLOWING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 6 */
|
||||
/* We first check the first condition,
|
||||
* and as we need the root later anyway we'll cache the root and perform the root check after this if. */
|
||||
if (node1 == NULL || node2 == NULL) {
|
||||
goto disconnected;
|
||||
}
|
||||
bool node2_is_ancestor_of_node1 = false;
|
||||
size_t node1_depth = 0;
|
||||
xmlNodePtr node1_root = node1;
|
||||
while (node1_root->parent) {
|
||||
node1_root = node1_root->parent;
|
||||
if (node1_root == node2) {
|
||||
node2_is_ancestor_of_node1 = true;
|
||||
}
|
||||
node1_depth++;
|
||||
}
|
||||
bool node1_is_ancestor_of_node2 = false;
|
||||
size_t node2_depth = 0;
|
||||
xmlNodePtr node2_root = node2;
|
||||
while (node2_root->parent) {
|
||||
node2_root = node2_root->parent;
|
||||
if (node2_root == node1) {
|
||||
node1_is_ancestor_of_node2 = true;
|
||||
}
|
||||
node2_depth++;
|
||||
}
|
||||
/* Second condition from step 6 */
|
||||
if (node1_root != node2_root) {
|
||||
goto disconnected;
|
||||
}
|
||||
|
||||
/* Step 7 */
|
||||
if ((node1_is_ancestor_of_node2 && attr1 == NULL) || (node1 == node2 && attr2 != NULL)) {
|
||||
RETURN_LONG(DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_PRECEDING);
|
||||
}
|
||||
|
||||
/* Step 8 */
|
||||
if ((node2_is_ancestor_of_node1 && attr2 == NULL) || (node1 == node2 && attr1 != NULL)) {
|
||||
RETURN_LONG(DOCUMENT_POSITION_CONTAINED_BY | DOCUMENT_POSITION_FOLLOWING);
|
||||
}
|
||||
|
||||
/* Special case: comparing children and attributes.
|
||||
* They belong to a different tree and are therefore hard to compare, but spec demands attributes to precede children
|
||||
* according to the pre-order depth-first search ordering.
|
||||
* Because their tree is different, the node parents only meet at the common element instead of earlier.
|
||||
* Therefore, it seems that one is the ancestor of the other. */
|
||||
if (node1_is_ancestor_of_node2) {
|
||||
ZEND_ASSERT(attr1 != NULL); /* Would've been handled in step 7 otherwise */
|
||||
RETURN_LONG(DOCUMENT_POSITION_PRECEDING);
|
||||
} else if (node2_is_ancestor_of_node1) {
|
||||
ZEND_ASSERT(attr2 != NULL); /* Would've been handled in step 8 otherwise */
|
||||
RETURN_LONG(DOCUMENT_POSITION_FOLLOWING);
|
||||
}
|
||||
|
||||
/* Step 9 */
|
||||
|
||||
/* We'll use the following strategy (which was already prepared during step 6) to implement this efficiently:
|
||||
* 1. Move nodes upwards such that they are at the same depth.
|
||||
* 2. Then we move both nodes upwards simultaneously until their parents are equal.
|
||||
* 3. If we then move node1 to the next entry repeatedly and we encounter node2,
|
||||
* then we know node1 precedes node2. Otherwise, node2 must precede node1. */
|
||||
/* 1. */
|
||||
if (node1_depth > node2_depth) {
|
||||
do {
|
||||
node1 = node1->parent;
|
||||
node1_depth--;
|
||||
} while (node1_depth > node2_depth);
|
||||
} else if (node2_depth > node1_depth) {
|
||||
do {
|
||||
node2 = node2->parent;
|
||||
node2_depth--;
|
||||
} while (node2_depth > node1_depth);
|
||||
}
|
||||
/* 2. */
|
||||
while (node1->parent != node2->parent) {
|
||||
node1 = node1->parent;
|
||||
node2 = node2->parent;
|
||||
}
|
||||
/* 3. */
|
||||
ZEND_ASSERT(node1 != node2);
|
||||
ZEND_ASSERT(node1 != NULL);
|
||||
ZEND_ASSERT(node2 != NULL);
|
||||
do {
|
||||
node1 = node1->next;
|
||||
if (node1 == node2) {
|
||||
RETURN_LONG(DOCUMENT_POSITION_PRECEDING);
|
||||
}
|
||||
} while (node1 != NULL);
|
||||
|
||||
/* Step 10 */
|
||||
RETURN_LONG(DOCUMENT_POSITION_FOLLOWING);
|
||||
|
||||
disconnected:;
|
||||
zend_long ordering;
|
||||
if (UNEXPECTED(node1 == node2)) {
|
||||
/* Degenerate case, they're both NULL, but the ordering must be consistent... */
|
||||
ZEND_ASSERT(node1 == NULL);
|
||||
ordering = other_intern < this_intern ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING;
|
||||
} else {
|
||||
ordering = node1 < node2 ? DOCUMENT_POSITION_PRECEDING : DOCUMENT_POSITION_FOLLOWING;
|
||||
}
|
||||
RETURN_LONG(DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | ordering);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#endif
|
||||
|
||||
@@ -297,6 +297,13 @@ interface DOMChildNode
|
||||
/** @not-serializable */
|
||||
class DOMNode
|
||||
{
|
||||
public const int DOCUMENT_POSITION_DISCONNECTED = 0x01;
|
||||
public const int DOCUMENT_POSITION_PRECEDING = 0x02;
|
||||
public const int DOCUMENT_POSITION_FOLLOWING = 0x04;
|
||||
public const int DOCUMENT_POSITION_CONTAINS = 0x08;
|
||||
public const int DOCUMENT_POSITION_CONTAINED_BY = 0x10;
|
||||
public const int DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
|
||||
|
||||
/** @readonly */
|
||||
public string $nodeName;
|
||||
|
||||
@@ -404,6 +411,8 @@ class DOMNode
|
||||
public function contains(DOMNode|DOMNameSpaceNode|null $other): bool {}
|
||||
|
||||
public function getRootNode(?array $options = null): DOMNode {}
|
||||
|
||||
public function compareDocumentPosition(DOMNode $other): int {}
|
||||
}
|
||||
|
||||
/** @not-serializable */
|
||||
|
||||
44
ext/dom/php_dom_arginfo.h
generated
44
ext/dom/php_dom_arginfo.h
generated
@@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: ebe9bcbd185e1973b5447beb306bd9d93051f415 */
|
||||
* Stub hash: 4705229124ee243538e712f4af1d94ddc4c3be0b */
|
||||
|
||||
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)
|
||||
@@ -114,6 +114,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_DOMNode_getRootNode, 0, 0,
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_DOMNode_compareDocumentPosition, 0, 1, IS_LONG, 0)
|
||||
ZEND_ARG_OBJ_INFO(0, other, DOMNode, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_DOMImplementation_getFeature, 0, 2, IS_NEVER, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, feature, IS_STRING, 0)
|
||||
ZEND_ARG_TYPE_INFO(0, version, IS_STRING, 0)
|
||||
@@ -553,6 +557,7 @@ ZEND_METHOD(DOMNode, removeChild);
|
||||
ZEND_METHOD(DOMNode, replaceChild);
|
||||
ZEND_METHOD(DOMNode, contains);
|
||||
ZEND_METHOD(DOMNode, getRootNode);
|
||||
ZEND_METHOD(DOMNode, compareDocumentPosition);
|
||||
ZEND_METHOD(DOMImplementation, getFeature);
|
||||
ZEND_METHOD(DOMImplementation, hasFeature);
|
||||
ZEND_METHOD(DOMImplementation, createDocumentType);
|
||||
@@ -745,6 +750,7 @@ static const zend_function_entry class_DOMNode_methods[] = {
|
||||
ZEND_ME(DOMNode, replaceChild, arginfo_class_DOMNode_replaceChild, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, contains, arginfo_class_DOMNode_contains, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, getRootNode, arginfo_class_DOMNode_getRootNode, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(DOMNode, compareDocumentPosition, arginfo_class_DOMNode_compareDocumentPosition, ZEND_ACC_PUBLIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
@@ -1098,6 +1104,42 @@ static zend_class_entry *register_class_DOMNode(void)
|
||||
class_entry = zend_register_internal_class_ex(&ce, NULL);
|
||||
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;
|
||||
|
||||
zval const_DOCUMENT_POSITION_DISCONNECTED_value;
|
||||
ZVAL_LONG(&const_DOCUMENT_POSITION_DISCONNECTED_value, 0x1);
|
||||
zend_string *const_DOCUMENT_POSITION_DISCONNECTED_name = zend_string_init_interned("DOCUMENT_POSITION_DISCONNECTED", sizeof("DOCUMENT_POSITION_DISCONNECTED") - 1, 1);
|
||||
zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_DISCONNECTED_name, &const_DOCUMENT_POSITION_DISCONNECTED_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||
zend_string_release(const_DOCUMENT_POSITION_DISCONNECTED_name);
|
||||
|
||||
zval const_DOCUMENT_POSITION_PRECEDING_value;
|
||||
ZVAL_LONG(&const_DOCUMENT_POSITION_PRECEDING_value, 0x2);
|
||||
zend_string *const_DOCUMENT_POSITION_PRECEDING_name = zend_string_init_interned("DOCUMENT_POSITION_PRECEDING", sizeof("DOCUMENT_POSITION_PRECEDING") - 1, 1);
|
||||
zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_PRECEDING_name, &const_DOCUMENT_POSITION_PRECEDING_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||
zend_string_release(const_DOCUMENT_POSITION_PRECEDING_name);
|
||||
|
||||
zval const_DOCUMENT_POSITION_FOLLOWING_value;
|
||||
ZVAL_LONG(&const_DOCUMENT_POSITION_FOLLOWING_value, 0x4);
|
||||
zend_string *const_DOCUMENT_POSITION_FOLLOWING_name = zend_string_init_interned("DOCUMENT_POSITION_FOLLOWING", sizeof("DOCUMENT_POSITION_FOLLOWING") - 1, 1);
|
||||
zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_FOLLOWING_name, &const_DOCUMENT_POSITION_FOLLOWING_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||
zend_string_release(const_DOCUMENT_POSITION_FOLLOWING_name);
|
||||
|
||||
zval const_DOCUMENT_POSITION_CONTAINS_value;
|
||||
ZVAL_LONG(&const_DOCUMENT_POSITION_CONTAINS_value, 0x8);
|
||||
zend_string *const_DOCUMENT_POSITION_CONTAINS_name = zend_string_init_interned("DOCUMENT_POSITION_CONTAINS", sizeof("DOCUMENT_POSITION_CONTAINS") - 1, 1);
|
||||
zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_CONTAINS_name, &const_DOCUMENT_POSITION_CONTAINS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||
zend_string_release(const_DOCUMENT_POSITION_CONTAINS_name);
|
||||
|
||||
zval const_DOCUMENT_POSITION_CONTAINED_BY_value;
|
||||
ZVAL_LONG(&const_DOCUMENT_POSITION_CONTAINED_BY_value, 0x10);
|
||||
zend_string *const_DOCUMENT_POSITION_CONTAINED_BY_name = zend_string_init_interned("DOCUMENT_POSITION_CONTAINED_BY", sizeof("DOCUMENT_POSITION_CONTAINED_BY") - 1, 1);
|
||||
zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_CONTAINED_BY_name, &const_DOCUMENT_POSITION_CONTAINED_BY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||
zend_string_release(const_DOCUMENT_POSITION_CONTAINED_BY_name);
|
||||
|
||||
zval const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_value;
|
||||
ZVAL_LONG(&const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_value, 0x20);
|
||||
zend_string *const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_name = zend_string_init_interned("DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC", sizeof("DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC") - 1, 1);
|
||||
zend_declare_typed_class_constant(class_entry, const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_name, &const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG));
|
||||
zend_string_release(const_DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC_name);
|
||||
|
||||
zval property_nodeName_default_value;
|
||||
ZVAL_UNDEF(&property_nodeName_default_value);
|
||||
zend_string *property_nodeName_name = zend_string_init("nodeName", sizeof("nodeName") - 1, 1);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: attribute vs child order
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<before/>
|
||||
<outer align="center">
|
||||
<p>foo</p>
|
||||
<div>
|
||||
<div>
|
||||
<p>bar</p>
|
||||
</div>
|
||||
</div>
|
||||
</outer>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$before = $dom->documentElement->firstElementChild;
|
||||
$outer = $before->nextElementSibling;
|
||||
$foo = $outer->firstElementChild;
|
||||
$bar = $foo->nextElementSibling->firstElementChild->firstElementChild;
|
||||
|
||||
// See note about attributes vs children positions: attributes precede children
|
||||
|
||||
echo "--- outer attribute vs before ---\n";
|
||||
var_dump($outer->attributes[0]->compareDocumentPosition($before) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
var_dump($before->compareDocumentPosition($outer->attributes[0]) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
|
||||
echo "--- outer attribute vs foo ---\n";
|
||||
var_dump($outer->attributes[0]->compareDocumentPosition($foo) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($foo->compareDocumentPosition($outer->attributes[0]) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
echo "--- outer attribute vs bar ---\n";
|
||||
var_dump($outer->attributes[0]->compareDocumentPosition($bar) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($bar->compareDocumentPosition($outer->attributes[0]) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- outer attribute vs before ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- outer attribute vs foo ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- outer attribute vs bar ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,54 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: attribute order for different element
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<outer>
|
||||
<inner a="b" c="d" e="f" />
|
||||
<inner a="b" x="y" />
|
||||
</outer>
|
||||
XML);
|
||||
|
||||
$attrs1 = $dom->documentElement->firstElementChild->attributes;
|
||||
$attrs2 = $dom->documentElement->firstElementChild->nextElementSibling->attributes;
|
||||
|
||||
foreach ($attrs1 as $attr1) {
|
||||
foreach ($attrs2 as $attr2) {
|
||||
echo "--- Compare 1->2 {$attr1->name} and {$attr2->name} ---\n";
|
||||
var_dump($attr1->compareDocumentPosition($attr2) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
echo "--- Compare 2->1 {$attr1->name} and {$attr2->name} ---\n";
|
||||
var_dump($attr2->compareDocumentPosition($attr1) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Compare 1->2 a and a ---
|
||||
bool(true)
|
||||
--- Compare 2->1 a and a ---
|
||||
bool(true)
|
||||
--- Compare 1->2 a and x ---
|
||||
bool(true)
|
||||
--- Compare 2->1 a and x ---
|
||||
bool(true)
|
||||
--- Compare 1->2 c and a ---
|
||||
bool(true)
|
||||
--- Compare 2->1 c and a ---
|
||||
bool(true)
|
||||
--- Compare 1->2 c and x ---
|
||||
bool(true)
|
||||
--- Compare 2->1 c and x ---
|
||||
bool(true)
|
||||
--- Compare 1->2 e and a ---
|
||||
bool(true)
|
||||
--- Compare 2->1 e and a ---
|
||||
bool(true)
|
||||
--- Compare 1->2 e and x ---
|
||||
bool(true)
|
||||
--- Compare 2->1 e and x ---
|
||||
bool(true)
|
||||
@@ -0,0 +1,40 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: attribute order for same element
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container a="b" c="d" e="f" />
|
||||
XML);
|
||||
|
||||
$attrs = $dom->documentElement->attributes;
|
||||
for ($i = 0; $i <= 2; $i++) {
|
||||
for ($j = $i + 1; $j <= $i + 2; $j++) {
|
||||
echo "--- Compare $i and ", ($j % 3), " ---\n";
|
||||
if ($i < ($j % 3)) {
|
||||
$expected = DOMNode::DOCUMENT_POSITION_FOLLOWING | DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
|
||||
} else {
|
||||
$expected = DOMNode::DOCUMENT_POSITION_PRECEDING | DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
|
||||
}
|
||||
var_dump($attrs[$i]->compareDocumentPosition($attrs[$j % 3]) === $expected);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Compare 0 and 1 ---
|
||||
bool(true)
|
||||
--- Compare 0 and 2 ---
|
||||
bool(true)
|
||||
--- Compare 1 and 2 ---
|
||||
bool(true)
|
||||
--- Compare 1 and 0 ---
|
||||
bool(true)
|
||||
--- Compare 2 and 0 ---
|
||||
bool(true)
|
||||
--- Compare 2 and 1 ---
|
||||
bool(true)
|
||||
@@ -0,0 +1,23 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: contains attribute as a direct descendent
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container align="center"/>
|
||||
XML);
|
||||
|
||||
$container = $dom->documentElement;
|
||||
$attribute = $container->attributes[0];
|
||||
|
||||
var_dump($container->compareDocumentPosition($attribute) === (DOMNode::DOCUMENT_POSITION_FOLLOWING | DOMNode::DOCUMENT_POSITION_CONTAINED_BY));
|
||||
var_dump($attribute->compareDocumentPosition($container) === (DOMNode::DOCUMENT_POSITION_PRECEDING | DOMNode::DOCUMENT_POSITION_CONTAINS));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,22 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: contains attribute for a freestanding element
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$foo = $dom->createElement("foo");
|
||||
$foo->setAttribute("test", "value");
|
||||
$attribute = $foo->attributes[0];
|
||||
|
||||
echo $dom->saveXML($foo), "\n";
|
||||
|
||||
var_dump($foo->compareDocumentPosition($attribute) === (DOMNode::DOCUMENT_POSITION_FOLLOWING | DOMNode::DOCUMENT_POSITION_CONTAINED_BY));
|
||||
var_dump($attribute->compareDocumentPosition($foo) === (DOMNode::DOCUMENT_POSITION_PRECEDING | DOMNode::DOCUMENT_POSITION_CONTAINS));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
<foo test="value"/>
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,28 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: contains attribute as a descendent in a longer path
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<div>
|
||||
<p align="center"/>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$container = $dom->documentElement;
|
||||
$p = $container->firstElementChild->firstElementChild;
|
||||
$attribute = $p->attributes[0];
|
||||
|
||||
var_dump($container->compareDocumentPosition($attribute) === (DOMNode::DOCUMENT_POSITION_FOLLOWING | DOMNode::DOCUMENT_POSITION_CONTAINED_BY));
|
||||
var_dump($attribute->compareDocumentPosition($container) === (DOMNode::DOCUMENT_POSITION_PRECEDING | DOMNode::DOCUMENT_POSITION_CONTAINS));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,25 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: contains nodes as a direct descendent
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<div/>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$container = $dom->documentElement;
|
||||
$div = $container->firstChild;
|
||||
|
||||
var_dump($container->compareDocumentPosition($div) === (DOMNode::DOCUMENT_POSITION_FOLLOWING | DOMNode::DOCUMENT_POSITION_CONTAINED_BY));
|
||||
var_dump($div->compareDocumentPosition($container) === (DOMNode::DOCUMENT_POSITION_PRECEDING | DOMNode::DOCUMENT_POSITION_CONTAINS));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,44 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: contains nodes as a descendent in a longer path
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<a>
|
||||
<b/>
|
||||
<c>
|
||||
<d/>
|
||||
</c>
|
||||
</a>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
$container = $xpath->query("//container")->item(0);
|
||||
|
||||
foreach (["a", "b", "c", "d"] as $tag) {
|
||||
echo "--- Test $tag ---\n";
|
||||
$element = $xpath->query("//$tag")->item(0);
|
||||
var_dump($container->compareDocumentPosition($element) === (DOMNode::DOCUMENT_POSITION_FOLLOWING | DOMNode::DOCUMENT_POSITION_CONTAINED_BY));
|
||||
var_dump($element->compareDocumentPosition($container) === (DOMNode::DOCUMENT_POSITION_PRECEDING | DOMNode::DOCUMENT_POSITION_CONTAINS));
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Test a ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Test b ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Test c ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Test d ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
68
ext/dom/tests/compareDocumentPosition/disconnected.phpt
Normal file
68
ext/dom/tests/compareDocumentPosition/disconnected.phpt
Normal file
@@ -0,0 +1,68 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: disconnected
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function check($element1, $element2) {
|
||||
echo "Disconnect and implementation flag (1->2): ";
|
||||
var_dump(($element1->compareDocumentPosition($element2) & (DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOMNode::DOCUMENT_POSITION_DISCONNECTED)) === (DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOMNode::DOCUMENT_POSITION_DISCONNECTED));
|
||||
echo "Disconnect and implementation flag (2->1): ";
|
||||
var_dump(($element2->compareDocumentPosition($element1) & (DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOMNode::DOCUMENT_POSITION_DISCONNECTED)) === (DOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOMNode::DOCUMENT_POSITION_DISCONNECTED));
|
||||
|
||||
// Must be the opposite result
|
||||
echo "Opposite result: ";
|
||||
if ($element1->compareDocumentPosition($element2) & DOMNode::DOCUMENT_POSITION_FOLLOWING) {
|
||||
var_dump(($element2->compareDocumentPosition($element1) & DOMNode::DOCUMENT_POSITION_PRECEDING) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
} else {
|
||||
var_dump(($element2->compareDocumentPosition($element1) & DOMNode::DOCUMENT_POSITION_FOLLOWING) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
}
|
||||
}
|
||||
|
||||
$dom1 = new DOMDocument();
|
||||
$dom1->loadXML('<?xml version="1.0"?><container/>');
|
||||
$dom2 = new DOMDocument();
|
||||
$dom2->loadXML('<?xml version="1.0"?><container/>');
|
||||
|
||||
echo "--- Two documents ---\n";
|
||||
check($dom1, $dom2);
|
||||
echo "--- Two document roots ---\n";
|
||||
check($dom1->documentElement, $dom2->documentElement);
|
||||
echo "--- Fragment ---\n";
|
||||
$fragment = $dom1->createDocumentFragment();
|
||||
$foo = $fragment->appendChild(new DOMText("foo"));
|
||||
check($foo, $dom1);
|
||||
echo "--- Unattached element ---\n";
|
||||
check(new DOMElement("foo"), new DOMElement("bar"));
|
||||
echo "--- Unattached attribute ---\n";
|
||||
check(new DOMAttr("foo"), new DOMAttr("bar"));
|
||||
echo "--- Unattached attribute & element ---\n";
|
||||
check(new DOMAttr("foo"), new DOMElement("bar"));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Two documents ---
|
||||
Disconnect and implementation flag (1->2): bool(true)
|
||||
Disconnect and implementation flag (2->1): bool(true)
|
||||
Opposite result: bool(true)
|
||||
--- Two document roots ---
|
||||
Disconnect and implementation flag (1->2): bool(true)
|
||||
Disconnect and implementation flag (2->1): bool(true)
|
||||
Opposite result: bool(true)
|
||||
--- Fragment ---
|
||||
Disconnect and implementation flag (1->2): bool(true)
|
||||
Disconnect and implementation flag (2->1): bool(true)
|
||||
Opposite result: bool(true)
|
||||
--- Unattached element ---
|
||||
Disconnect and implementation flag (1->2): bool(true)
|
||||
Disconnect and implementation flag (2->1): bool(true)
|
||||
Opposite result: bool(true)
|
||||
--- Unattached attribute ---
|
||||
Disconnect and implementation flag (1->2): bool(true)
|
||||
Disconnect and implementation flag (2->1): bool(true)
|
||||
Opposite result: bool(true)
|
||||
--- Unattached attribute & element ---
|
||||
Disconnect and implementation flag (1->2): bool(true)
|
||||
Disconnect and implementation flag (2->1): bool(true)
|
||||
Opposite result: bool(true)
|
||||
@@ -0,0 +1,50 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: element order at a different tree depth
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<div>
|
||||
<p>foo</p>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>bar</p>
|
||||
</div>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
$query = $xpath->query("//p");
|
||||
$foo = $query->item(0);
|
||||
$bar = $query->item(1);
|
||||
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
echo "--- Check on depth $i ---\n";
|
||||
var_dump($foo->compareDocumentPosition($bar) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($bar->compareDocumentPosition($foo) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
$foo = $foo->parentElement;
|
||||
$bar = $bar->parentElement;
|
||||
}
|
||||
|
||||
echo "--- One contains the other at depth 2 ---\n";
|
||||
var_dump(boolval($foo->compareDocumentPosition($bar) & DOMNode::DOCUMENT_POSITION_CONTAINED_BY));
|
||||
var_dump(boolval($bar->compareDocumentPosition($foo) & DOMNode::DOCUMENT_POSITION_CONTAINS));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Check on depth 0 ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Check on depth 1 ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- One contains the other at depth 2 ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,18 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: direct root children
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$foo = $dom->appendChild($dom->createElement("foo"));
|
||||
$bar = $dom->appendChild($dom->createElement("bar"));
|
||||
|
||||
var_dump($foo->compareDocumentPosition($bar) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($bar->compareDocumentPosition($foo) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
@@ -0,0 +1,71 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: element order at the same tree depth
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<div>
|
||||
<div>
|
||||
<strong/>
|
||||
<p>foo</p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<p>bar</p>
|
||||
<strong/>
|
||||
</div>
|
||||
</div>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
$query = $xpath->query("//p");
|
||||
$foo = $query->item(0);
|
||||
$bar = $query->item(1);
|
||||
|
||||
echo "--- strong & foo ---\n";
|
||||
var_dump($foo->previousElementSibling->compareDocumentPosition($foo) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($foo->compareDocumentPosition($foo->previousElementSibling) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
echo "--- strong & bar ---\n";
|
||||
var_dump($bar->nextElementSibling->compareDocumentPosition($bar) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
var_dump($bar->compareDocumentPosition($bar->nextElementSibling) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
|
||||
echo "--- strong & strong ---\n";
|
||||
var_dump($foo->previousElementSibling->compareDocumentPosition($bar->nextElementSibling) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($bar->nextElementSibling->compareDocumentPosition($foo->previousElementSibling) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
echo "--- Check on depth $i ---\n";
|
||||
var_dump($foo->compareDocumentPosition($bar) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($bar->compareDocumentPosition($foo) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
$foo = $foo->parentElement;
|
||||
$bar = $bar->parentElement;
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- strong & foo ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- strong & bar ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- strong & strong ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Check on depth 0 ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Check on depth 1 ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Check on depth 2 ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
51
ext/dom/tests/compareDocumentPosition/entity.phpt
Normal file
51
ext/dom/tests/compareDocumentPosition/entity.phpt
Normal file
@@ -0,0 +1,51 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: entity ordering
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE root [
|
||||
<!ENTITY e1 "e1">
|
||||
<!ENTITY e2 "e2">
|
||||
]>
|
||||
<container>
|
||||
<child>&e1;</child>
|
||||
</container>
|
||||
XML, LIBXML_NOENT);
|
||||
|
||||
$entities = iterator_to_array($dom->doctype->entities);
|
||||
usort($entities, fn ($a, $b) => strcmp($a->nodeName, $b->nodeName));
|
||||
|
||||
echo "--- Compare entities ---\n";
|
||||
|
||||
var_dump($entities[0]->compareDocumentPosition($entities[1]) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($entities[1]->compareDocumentPosition($entities[0]) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
$child = $xpath->query('//child')->item(0);
|
||||
|
||||
echo "--- Compare entities against first child ---\n";
|
||||
|
||||
var_dump($entities[0]->compareDocumentPosition($child->firstChild) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
var_dump($entities[1]->compareDocumentPosition($child->firstChild) === DOMNode::DOCUMENT_POSITION_FOLLOWING);
|
||||
|
||||
echo "--- Compare first child against entities ---\n";
|
||||
|
||||
var_dump($child->firstChild->compareDocumentPosition($entities[0]) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
var_dump($child->firstChild->compareDocumentPosition($entities[1]) === DOMNode::DOCUMENT_POSITION_PRECEDING);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
--- Compare entities ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Compare entities against first child ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
--- Compare first child against entities ---
|
||||
bool(true)
|
||||
bool(true)
|
||||
29
ext/dom/tests/compareDocumentPosition/equal.phpt
Normal file
29
ext/dom/tests/compareDocumentPosition/equal.phpt
Normal file
@@ -0,0 +1,29 @@
|
||||
--TEST--
|
||||
compareDocumentPosition: equal nodes
|
||||
--EXTENSIONS--
|
||||
dom
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$dom = new DOMDocument();
|
||||
$dom->loadXML(<<<XML
|
||||
<?xml version="1.0"?>
|
||||
<container>
|
||||
<p>foo</p>
|
||||
<p>foo</p>
|
||||
</container>
|
||||
XML);
|
||||
|
||||
$xpath = new DOMXPath($dom);
|
||||
$nodes = $xpath->query('//p');
|
||||
var_dump($nodes->item(0)->compareDocumentPosition($nodes->item(0)) === 0);
|
||||
var_dump($nodes->item(1)->compareDocumentPosition($nodes->item(1)) === 0);
|
||||
var_dump($nodes->item(0)->compareDocumentPosition($nodes->item(1)) === 0);
|
||||
var_dump($nodes->item(1)->compareDocumentPosition($nodes->item(0)) === 0);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bool(true)
|
||||
bool(true)
|
||||
bool(false)
|
||||
bool(false)
|
||||
Reference in New Issue
Block a user