Make filter() narrow the match set instead of keeping elements that contain a match - #71
Closed
jakejackson1 wants to merge 2 commits into
Closed
Make filter() narrow the match set instead of keeping elements that contain a match#71jakejackson1 wants to merge 2 commits into
jakejackson1 wants to merge 2 commits into
Conversation
filter() kept any element that *contained* a match, because it ran a descendant
search per node. jQuery's filter() narrows the set to the members that match, so
qp($file, 'inner')->filter('li') returned both <inner> elements where jQuery
returns none. has() already provides the old behaviour.
The original author left a comment on the line saying the correct traverser mode
"fails unit tests". It fails exactly one, DOMQueryTest::testFilter, which asserts
the containment result; that test is rebaselined here.
Both methods now filter their candidates as a single set rather than one node at
a time. A per-node pass cannot evaluate a selector that describes a position
within the set, because each node is the only member of its own one-element set.
This also drops the per-node scope node, so children(':scope') no longer matches
every child.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## issue-62 #71 +/- ##
==============================================
- Coverage 89.74% 89.71% -0.03%
Complexity 1363 1363
==============================================
Files 27 27
Lines 3071 3064 -7
==============================================
- Hits 2756 2749 -7
Misses 315 315 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Member
Author
|
Collapsed into #72, which carries all of this work unchanged — same tree, one branch. Closing to keep the review in one place; reopen if the split turns out to be preferable. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the containment bug in
filter()— the one all three of the open selector PRs ran intoand left alone because fixing it means deliberately rebaselining an existing test.
Based on
issue-62(#69), which is based onissue-51(#67). Merge order: #67 → #69 → this.It needs
QueryPath\Helpers\NodeMatcher, introduced in #67.The bug
filter()ran a descendant search per node, so it kept any element that merely contained amatch rather than narrowing the set to the elements that match:
That is what
has()does, andhas()keeps doing it — it is the migration path for anyonerelying on the old behaviour.
The author's comment
filter()carried this since 2009:It fails exactly one test —
DOMQueryTest::testFilter, which asserts the containment result:Under jQuery semantics both are
0. The test is rebaselined, and two tests are added: onepinning the
filter()/has()difference so the migration path stays covered, one coveringset-level evaluation.
Filtering the set, not the node
Both
filter()andchildren($selector)now filter their candidates in one pass instead ofone node at a time. A per-node pass cannot evaluate a selector describing a position within the
set — each node would be the only member of its own one-element set.
This also drops the per-node scope node, fixing the same
:scopebug that #69 fixed in thetraversal methods:
children()did not have the containment bug — it already used the correct traverser mode.Only its scope handling was wrong.
Interaction with #66
#66 documents a known limitation: because
children()andfilter()built a traverser pernode, a positional pseudo-class saw a one-element set, so
children('li:first')returned everylichild. Filtering set-at-a-time resolves it. Verified by merging #66 into this branchlocally — 382 tests pass, and:
No change is needed in #66; this branch just removes the reason for its caveat.
Verification
vendor/bin/phpunit— 346 tests, 1164 assertions, 0 failures (2 pre-existingcreate_functionskips)composer run lintandcomposer run lint:min-php— clean🤖 Generated with Claude Code