This traverses a DOMDocument and attempts to find
matches to the provided selector.
\b How this works
This performs a bottom-up search. On the first pass,
it attempts to find all of the matching elements for the
last simple selector in a selector.
Subsequent passes attempt to eliminate matches from the
initial matching set.
Example:
Say we begin with the selector foo.bar baz. This is processed
as follows:
- First, find all baz elements.
- Next, for any baz element that does not have foo as an ancestor,
eliminate it from the matches.
- Finally, for those that have foo as an ancestor, does that foo
also have a class baz? If not, it is removed from the matches.
\b Extrapolation
Partial simple selectors are almost always expanded to include an
element.
Examples:
- :first is expanded to *:first
- .bar is expanded to *.bar.
- .outer .inner is expanded to *.outer *.inner
The exception is that IDs are sometimes not expanded, e.g.:
- #myElement does not get expanded
- #myElement .class \i may be expanded to *#myElement *.class
(which will obviously not perform well).
/** * @param DOMNode $node * @param string $selector * @return bool * @throws \QueryPath\CSS\ParseException */ public function matches(DOMNode $node, $selector) { $traverser = new DOMTraverser(new \SPLObjectStorage()); $handler = new Selector(); $parser = new Parser($selector, $handler); $parser->parse(); foreach ($handler as $selectorGroup) { if ($traverser->matchesSelector($node, $selectorGroup)) { return true; } } return false; }
public function findInPlace($selector) { $query = new \QueryPath\CSS\DOMTraverser($this->matches); $query->find($selector); $this->setMatches($query->matches()); return $this; }
/** * Get the children of the elements in the DOMQuery object. * * If a selector is provided, the list of children will be filtered through * the selector. * * @param string $selector * A valid selector. * @return \QueryPath\DOMQuery * A DOMQuery wrapping all of the children. * @see removeChildren() * @see parent() * @see parents() * @see next() * @see prev() */ public function children($selector = NULL) { $found = new \SplObjectStorage(); $filter = strlen($selector) > 0; if ($filter) { $tmp = new \SplObjectStorage(); } foreach ($this->matches as $m) { foreach ($m->childNodes as $c) { if ($c->nodeType == XML_ELEMENT_NODE) { // This is basically an optimized filter() just for children(). if ($filter) { $tmp->attach($c); $query = new \QueryPath\CSS\DOMTraverser($tmp, TRUE, $c); $query->find($selector); if (count($query->matches()) > 0) { $found->attach($c); } $tmp->detach($c); } else { $found->attach($c); } } } } $new = $this->inst($found, NULL, $this->options); return $new; }