fix: length rules reject a valid length of 0 - #17
Open
shakurShirajul wants to merge 3 commits into
Open
Conversation
…erage for Between, Max, and Min rules
There was a problem hiding this comment.
Pull request overview
Fixes an edge case in WPValidator’s length-based rules where a valid computed length of 0 was treated as falsy and incorrectly rejected. The change ensures only “unmeasurable” values (where getValueLength() returns false) short-circuit validation, while a real length/value of 0 proceeds through comparisons.
Changes:
- Update
BetweenRule,MinRule, andMaxRuleto guard on$length === falserather than truthiness. - Expand rule test coverage to include zero-length/value cases (
0,0.0,'',[]) and explicitly unmeasurable cases (true,null,stdClass). - Add boundary-negative assertions in existing min/max/between tests to validate rejection beyond configured limits.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/Rules/MinRule.php | Treat 0 as a valid measurable length/value; only false (unmeasurable) fails fast. |
| src/Rules/MaxRule.php | Same fix as MinRule for max comparisons (accepts 0). |
| src/Rules/BetweenRule.php | Same fix as Min/Max for range comparisons (accepts 0). |
| tests/Rules/MinTest.php | Adds coverage for zero-length acceptance, unmeasurable rejection, and boundary negatives. |
| tests/Rules/MaxTest.php | Adds coverage for zero-length acceptance, unmeasurable rejection, and boundary negatives. |
| tests/Rules/BetweenTest.php | Adds coverage for zero-length acceptance, unmeasurable rejection, and boundary negatives. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…tor Between, Max, and Min rules to utilize new method; add test-watch script and update README
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.
Summary
The
min,max, andbetweenrules rejected any value whose length is0. An empty string, an empty array, and the numbers0/0.0all failed validation even when the configured bounds explicitly allowed a length of zero —min:0,max:5, andbetween:0,5would each reject''.The cause is a truthiness check on a length value:
getValueLength()returns an integer length, orfalsewhen the value has no measurable length at all (bool, null, object). Because0andfalseare both falsy, the guard could not tell "this value is legitimately empty" from "this value cannot be measured", and collapsed both into a failure.This PR distinguishes the two, and moves the distinction into the shared helper so it is made in exactly one place.
Behaviour change
Only values with a length of
0are affected. Verified before/after:min:0max:5between:0,5''false→truefalse→truefalse→true[]false→truefalse→truefalse→true0false→truefalse→truefalse→true0.0false→truefalse→truefalse→trueEverything else is untouched. Non-zero bounds behave identically to before (
min:3still accepts'abc'and rejects'ab'), and values with no measurable length —true,null,new stdClass()— still fail, as they should.Note this only changes whether an empty value passes a length rule. It does not make empty values pass validation overall:
requiredis still the rule that rejects them, which is the correct separation of concerns. A field declaredmin:0was always meant to accept an empty value.Changes
Fix — added
Helpers::lengthWithin()next togetValueLength()in the trait all three rules already use. It decodes thefalsesentinel once and applies the bounds, withnullmeaning "no bound on this side":MinRule,MaxRule, andBetweenRulenow each delegate to it in a single line rather than repeating the same guard three times. The original fix applied the corrected check separately in all three classes; consolidating it means a future length-based rule cannot reintroduce the bug by writingif ($length)again, and there is one definition of what a measurable length is.Tests — added coverage to
MinTest,MaxTest, andBetweenTestfor the three distinct cases the old code conflated:0is accepted, for strings, arrays, and numberstrue,null,stdClass) are still rejectedThe previously commented-out array assertion in
MaxTestis now enabled.Developer tooling — added
composer test:watch(bin/test-watch), which re-runs the suite when a PHP file undersrc/ortests/changes. It delegates to the existingtest:unitscript so there is a single definition of the test command, and uses plain polling so it needs no extra dependencies. Added/bin export-ignoreto.gitattributesso this dev-only script is not shipped in the distributed package, and documented the two commands briefly under Contributing.Testing
composer test:unit— 36 tests, 123 assertions, all passing.Before/after behaviour in the table above was verified by running both revisions of the rules directly against each value.
Compatibility
No API changes.
lengthWithin()is a newprotectedtrait method;getValueLength()keeps its existing signature and return contract, so any code extendingRuleor usingHelperscontinues to work unchanged. PHP 7.2+ compatible, consistent with the package's existing constraint.One caveat worth calling out for reviewers: if a project currently relies on
min:0orbetween:0,Nto reject empty values, that behaviour was a bug and this PR ends it. Such a field should userequiredinstead.