Guidelines
Description of the bug
Any selector using the child combinator (>) whose right-hand side can also match the document element raises a TypeError from the selector engine. In practice this means X > * always fails, whatever X is, because * matches the root as well as everything else.
Uncaught TypeError: QueryPath\CSS\DOMTraverser::matchesSimpleSelector():
Argument #1 ($node) must be of type DOMElement, DOMDocument given
This affects qp(), htmlqp(), html5qp() and find() alike, since they all go through CSS\DOMTraverser.
Root cause
DOMTraverser::combineDirectDescendant() hands the parent node straight to matchesSimpleSelector() without checking its type (src/CSS/DOMTraverser.php:358-367):
public function combineDirectDescendant($node, $selectors, $index)
{
$parent = $node->parentNode;
if (empty($parent)) {
return false;
}
return $this->matchesSimpleSelector($parent, $selectors, $index);
}
matchesSimpleSelector() is typed DOMElement $node (src/CSS/DOMTraverser.php:207). When the right-hand selector matches the document element, $parent is the DOMDocument, and the type declaration rejects it.
combineAnyDescendant() immediately above already guards for this case:
if ($node->nodeType != XML_ELEMENT_NODE) {
continue;
}
which is why the descendant combinator (X *) works and the child combinator does not.
Suggested fix
Return false when the parent is not an element:
$parent = $node->parentNode;
if (empty($parent) || $parent->nodeType !== XML_ELEMENT_NODE) {
return false;
}
This is correct by definition — the document node can never match an element selector.
Workaround
children() does the same job without going through the combinator.
QueryPath version
4.2.0 (also reproduces on main at bed5d2c)
PHP Version and environment (server type, cli provider etc., enclosing libraries and their respective versions)
PHP 8.3.16 CLI (macOS, Homebrew). Not PHP-8 specific — the cause is present on every supported version unless noted.
Minimal reproducible PHP+HTML snippet to replicate bug
<?php
require __DIR__ . '/vendor/autoload.php';
$xml = '<?xml version="1.0"?><root><wrap><a/><b/></wrap></root>';
var_dump(qp($xml, 'wrap > a')->count()); // int(1) - fine
var_dump(qp($xml, 'wrap > *')->count()); // TypeError
html5qp('<html><body><ul><li>a</li></ul></body></html>', 'ul > *') fails identically.
Guidelines
Description of the bug
Any selector using the child combinator (
>) whose right-hand side can also match the document element raises aTypeErrorfrom the selector engine. In practice this meansX > *always fails, whateverXis, because*matches the root as well as everything else.This affects
qp(),htmlqp(),html5qp()andfind()alike, since they all go throughCSS\DOMTraverser.Root cause
DOMTraverser::combineDirectDescendant()hands the parent node straight tomatchesSimpleSelector()without checking its type (src/CSS/DOMTraverser.php:358-367):matchesSimpleSelector()is typedDOMElement $node(src/CSS/DOMTraverser.php:207). When the right-hand selector matches the document element,$parentis theDOMDocument, and the type declaration rejects it.combineAnyDescendant()immediately above already guards for this case:which is why the descendant combinator (
X *) works and the child combinator does not.Suggested fix
Return
falsewhen the parent is not an element:This is correct by definition — the document node can never match an element selector.
Workaround
children()does the same job without going through the combinator.QueryPath version
4.2.0 (also reproduces on
mainat bed5d2c)PHP Version and environment (server type, cli provider etc., enclosing libraries and their respective versions)
PHP 8.3.16 CLI (macOS, Homebrew). Not PHP-8 specific — the cause is present on every supported version unless noted.
Minimal reproducible PHP+HTML snippet to replicate bug
html5qp('<html><body><ul><li>a</li></ul></body></html>', 'ul > *')fails identically.