Skip to content

Commit bab1118

Browse files
committed
Implement matches() (TODO: cleanup)
1 parent 7325e80 commit bab1118

File tree

6 files changed

+81
-1
lines changed

6 files changed

+81
-1
lines changed

ext/dom/element.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,4 +1744,20 @@ PHP_METHOD(DOMElement, querySelectorAll)
17441744
php_dom_dispatch_query_selector(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
17451745
}
17461746

1747+
PHP_METHOD(DOMElement, matches)
1748+
{
1749+
zend_string *selectors_str;
1750+
1751+
ZEND_PARSE_PARAMETERS_START(1, 1)
1752+
Z_PARAM_STR(selectors_str)
1753+
ZEND_PARSE_PARAMETERS_END();
1754+
1755+
xmlNodePtr thisp;
1756+
dom_object *intern;
1757+
zval *id;
1758+
DOM_GET_THIS_OBJ(thisp, id, xmlNodePtr, intern);
1759+
1760+
dom_element_matches(thisp, intern, return_value, selectors_str);
1761+
}
1762+
17471763
#endif

ext/dom/parentnode.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,11 +815,18 @@ void dom_parent_node_replace_children(dom_object *context, zval *nodes, uint32_t
815815
* CSS selector implementation below
816816
*/
817817

818+
// TODO: probably best if this is all moved to another file???
819+
818820
typedef struct {
819821
HashTable *list;
820822
dom_object *intern;
821823
} dom_query_selector_all_ctx;
822824

825+
typedef struct {
826+
const xmlNode *reference;
827+
bool result;
828+
} dom_query_selector_matches_ctx;
829+
823830
lxb_status_t php_dom_query_selector_find_single_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
824831
{
825832
xmlNodePtr *result = (xmlNodePtr *) ctx;
@@ -838,6 +845,16 @@ lxb_status_t php_dom_query_selector_find_array_callback(const xmlNode *node, lxb
838845
return LXB_STATUS_OK;
839846
}
840847

848+
lxb_status_t php_dom_query_selector_find_matches_callback(const xmlNode *node, lxb_css_selector_specificity_t spec, void *ctx)
849+
{
850+
dom_query_selector_matches_ctx *matches_ctx = (dom_query_selector_matches_ctx *) ctx;
851+
if (node == matches_ctx->reference) {
852+
matches_ctx->result = true;
853+
return LXB_STATUS_STOP;
854+
}
855+
return LXB_STATUS_OK;
856+
}
857+
841858
static lxb_status_t php_dom_query_selector_common(zval *return_value, dom_object *intern, const xmlNode *root, zend_string *selectors_str, lxb_selectors_cb_f cb, void *ctx)
842859
{
843860
lxb_status_t status;
@@ -923,4 +940,28 @@ void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zv
923940
}
924941
}
925942

943+
/* https://dom.spec.whatwg.org/#dom-element-matches */
944+
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, zend_string *selectors_str)
945+
{
946+
dom_query_selector_matches_ctx ctx = { thisp, false };
947+
948+
const xmlNode *root = thisp;
949+
while (root->parent != NULL) {
950+
root = root->parent;
951+
}
952+
953+
if (php_dom_query_selector_common(
954+
return_value,
955+
intern,
956+
root,
957+
selectors_str,
958+
php_dom_query_selector_find_matches_callback,
959+
&ctx
960+
) != LXB_STATUS_OK) {
961+
RETURN_THROWS();
962+
} else {
963+
RETURN_BOOL(ctx.result);
964+
}
965+
}
966+
926967
#endif

ext/dom/php_dom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ bool php_dom_pre_insert(php_libxml_ref_obj *document, xmlNodePtr node, xmlNodePt
200200
bool php_dom_pre_insert_is_parent_invalid(xmlNodePtr parent);
201201
void dom_parent_node_query_selector(xmlNodePtr thisp, dom_object *intern, zval *return_value, zend_string *selectors_str);
202202
void dom_parent_node_query_selector_all(xmlNodePtr thisp, dom_object *intern, zval *return_value, zend_string *selectors_str);
203+
void dom_element_matches(xmlNodePtr thisp, dom_object *intern, zval *return_value, zend_string *selectors_str);
203204

204205
/* nodemap and nodelist APIs */
205206
xmlNodePtr php_dom_named_node_map_get_named_item(dom_nnodemap_object *objmap, const zend_string *named, bool may_transform);

ext/dom/php_dom.stub.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,8 @@ public function querySelector(string $selectors): ?DOMElement {}
718718
/** @implementation-alias DOMElement::querySelectorAll */
719719
public function querySelectorAll(string $selectors): DOMNodeList {}
720720

721+
public function matches(string $selectors): bool {}
722+
721723
public function insertAdjacentElement(string $where, DOMElement $element): ?DOMElement {}
722724

723725
public function insertAdjacentText(string $where, string $data): void {}

ext/dom/php_dom_arginfo.h

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/dom/tests/querySelector/test_utils.inc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,27 @@ function test_helper(DOM\ParentNode $dom, string $selector, bool $only_name = fa
1111
throw new Exception('Mismatch in querySelector and querySelectorAll');
1212
}
1313

14+
$list = [];
1415
foreach ($all as $node) {
16+
$list[] = $node;
17+
1518
if ($only_name) {
1619
echo $node->nodeName, "\n";
1720
continue;
1821
}
22+
1923
echo $dom->saveXML($node), "\n";
2024
}
25+
26+
// If the element is in the list, then it must match, otherwise it must not
27+
// This loops over all the elements in the document and checks them
28+
foreach ($dom->querySelectorAll('*') as $node) {
29+
if (in_array($node, $list, true) !== $node->matches($selector)) {
30+
var_dump($node, $selector, in_array($node, $list, true), $node->matches($selector));
31+
echo $dom->saveXML($node), "\n";
32+
throw new Exception('Bug in Element::matches()');
33+
}
34+
}
2135
}
2236

2337
function test_failure(DOM\ParentNode $dom, string $selector)

0 commit comments

Comments
 (0)