Skip to content

Select with DOMTraverser in remove() and replaceAll() - #127

Merged
jakejackson1 merged 2 commits into
mainfrom
fix/issue-126
Oct 26, 2026
Merged

jakejackson1 merged 2 commits into
mainfrom
fix/issue-126

Conversation

@gravitypdf-bot

Copy link
Copy Markdown
Contributor

remove($selector) and replaceAll($selector, $document) were the last callers of the legacy QueryPath\CSS\QueryPathEventHandler engine, which disagrees with find() in three ways:

$xml = '<?xml version="1.0"?><root><p><i>a</i><b>x</b></p><p><i>b</i></p><i>c</i></root>';
qp($xml, 'p')->remove('p')->count();   // was 2 (the <p>s themselves), now 0, as detach('p') and find('p')

$xml = '<?xml version="1.0"?><ul><li id="a"/><li id="b" class="x"/><li id="c"/></ul>';
qp($xml)->remove('li:matches(.x)');    // threw "Unknown Pseudo-Class: matches", now removes b
qp($xml)->remove('li:not(:first)');    // removed nothing, now removes b, c
qp($xml)->remove('li:not(:odd)');      // removed a, b, c, now removes a, c

Changes

  • remove($selector) runs DOMTraverser over the match set. That is what find() does, but find() itself isn't called, because inst() deep-clones the match set. remove() and detach($selector) now agree on every selector.
  • replaceAll($selector, $document) runs DOMTraverser seeded with $document->documentElement, which is how qp($document) seeds it. The selector still searches the whole target document, document element included (the Make find() search descendants only, as jQuery does #73 exception), and the current match set still only supplies the replacement.
  • QueryPathEventHandler is left in place and unchanged apart from a @deprecated docblock pointing at DOMTraverser. No runtime notice. Docblocks that named it as the engine behind these methods are updated.
  • CHANGELOG: new Behaviour change entry. I also corrected two Unreleased entries that said remove()/replaceAll() still use the legacy engine; one of them also claimed the legacy engine agreed on all positional pseudo-classes, which isn't true inside :not() (remove('li:not(:first)') and other positional :not() arguments are evaluated per element in the legacy engine #124).

Behaviour change

remove($selector) now searches the descendants of the match set only, so a selector that matched the nodes in hand no longer removes them. Use filter($selector)->remove() to get the old result. Selectors that used to throw (:matches()) now work.

Tests

:is() is not added here. To work with positional arguments it also needs adding to Util::SELECTOR_ARGUMENT_PSEUDO_CLASSES, so it's left for a follow-up.

Fixes #123
Fixes #124
Fixes #126

🤖 Generated with Claude Code

gravitypdf-bot and others added 2 commits October 26, 2026 13:23
remove($selector) and replaceAll($selector) were the last callers of the
legacy QueryPathEventHandler engine, which matched the nodes in hand, did
not know :matches(), and evaluated a positional :not() argument one
element at a time. They now select what find() does. Deprecate
QueryPathEventHandler, which QueryPath no longer uses.

Fixes #126, #123, #124.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
find() deep-clones the match set through inst(), and QueryPath::with()
builds a whole DOMQuery, only for the matches to be read and discarded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.16%. Comparing base (6ae6acf) to head (ba44e04).
⚠️ Report is 14 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main     #127      +/-   ##
============================================
+ Coverage     90.69%   91.16%   +0.47%     
- Complexity     1424     1460      +36     
============================================
  Files            25       26       +1     
  Lines          3170     3227      +57     
============================================
+ Hits           2875     2942      +67     
+ Misses          295      285      -10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants