Upgrade Validation to v3#11
Open
henriquemoody wants to merge 13 commits into
Open
Conversation
The v3 line replaces the long-standing Validatable interface with a leaner Validator/Result contract, collapses the per-rule exception hierarchy into a single ValidationException, reorganises the built-in rules under a Validators\ namespace, and adjusts default message templates. This commit only bumps the constraint; the rest of the branch adapts the package to those breaking changes.
The v2 Validatable interface exposed validate(), check(), and reportError(); v3 replaces it with a single Validator::evaluate(mixed): Result method and routes exception construction through a separate ValidatorBuilder pipeline. Rule now implements Validator directly and codifies the precondition-then-inner-rule pattern in evaluate() rather than via check() side effects. Assertion routes string descriptions through ValidatorBuilder::init()->assert() to pick up v3 templating, while Throwable descriptions fall back to a manual evaluate-and-throw because the builder only understands string templates. The Envelope wrapper and its test are removed entirely. Its only job was to customise v2 exception templates; v3 lets each Validator carry its own template, so the abstraction has no remaining role here.
The v3 Validation package ships first-class All, Length, Min, and Max validators that already cover the type and applicability preconditions plus the length/extreme calculations this package used to assemble by hand. The local wrappers therefore collapse to a thin delegation: each one forwards evaluate() to the matching Validators\* class and drops the hand-rolled StringType/Countable preconditions, the mb_strlen/count fallback, and the min()/max() over iterables. The creators also have to track v3's class reorganisation. Built-in rules moved from Rules\ to Validators\, several were renamed (Nullable became NullOr, Attribute split into Property and PropertyExists, Key gained a companion KeyExists), and Validatable became Validator. StandardCreator, KeyCreator, PropertyCreator, and NullOrCreator each update the class names they resolve at runtime.
FakeRule was a hand-rolled Validatable double that captured calls to check(); v3 only exposes evaluate(mixed): Result, so the double has to be rewritten against the new contract. AssertionTest similarly stops mocking Validatable in favour of mocking Validator or using the ready-made AlwaysInvalid validator, which lets the tests drop the fragile manual construction of v2 ValidationException instances (which required pulling in Formatter and KeepOriginalStringName). Follow-on to the Rule/Assertion rewrite: the production code now demands Validator everywhere, so any double pretending to be a rule has to follow.
PrefixedCreator's class-string<Validatable> bound has to become class-string<Validator>, and NotCreator imports the built-in Not rule which moved from Rules\Not to Validators\Not. No behavioural change — purely tracking the v3 namespace reorganisation so the creators still resolve.
The unit suite references Validation classes by name in setup, mocks, and expected-output assertions. v3 moved the built-in rules under Validators\, renamed Validatable to Validator, split Key into Key plus KeyExists (and Attribute likewise into Property plus PropertyExists), and renamed Optional to UndefOr. The production code switched in earlier commits; this catches the unit suite up to those changes so it compiles and asserts against the right symbols.
v2 gave each rule its own exception subclass (NegativeException, IntTypeException, and so on). v3 collapsed that hierarchy into a single ValidationException with the rule identity carried as a parameter. The integration tests previously asserted on those subclasses and on the v2 wording of the default negative() template; both have to be updated to match what v3 actually throws.
v3 made several template-level changes that ripple through every
assertion error message: the standard substitution variable went from
{{input}} to {{subject}}, equality assertions emit "must be equal to"
instead of "must equal", type assertions say "must be a/an X" instead
of "must be of type X", and PropertyExists no longer prefixes its
output with "Attribute". The PHPT documentation tests and a few unit
test fixtures pin those exact strings, so every line that quoted the
v2 wording has to follow.
This is the tail end of the v3 message format migration: the
prefixes-not / prefixes-nullOr / prefixes-property documentation tests
and the All/NullOr/Not/Key/Property prefix assertions in the
integration suite still quoted v2 wording (object syntax, "must
equal", "Attribute" prefix, no "or must be null" suffix). With those
fixed, every test in the suite reflects what v3 actually emits.
The same pass picks up a handful of housekeeping items the rewrites
left behind: an unused Result import in Assertion.php, brace-style
and trailing-whitespace nits in Rule.php, an obsolete {@inheritdoc}
on FakeRule, and a FQCN reference to DomainException in AssertionTest
that should be a regular import.
PHPT tests run as a separate test suite and require a custom helper function to capture exception messages for comparison. This makes them harder to integrate with standard PHPUnit tooling, coverage reporting, and CI pipelines that already use PHPUnit. The ten PHPT files under tests/documentation/ are replaced with proper PHPUnit TestCase classes in tests/integration/Documentation/. Each class maps one-to-one to the original PHPT file, with one test method per documented behaviour. Tests that verify error messages use expectExceptionMessage(), and tests that verify a passing path are marked with @doesNotPerformAssertions so PHPUnit does not flag them as risky. The documentation testsuite entry in phpunit.xml.dist is removed; the integration testsuite already recurses into subdirectories and picks up the new classes automatically. The helpers.php autoload entry and the phpt extension from phpcs.xml.dist are cleaned up accordingly.
PHPStan 1.x cannot parse the PHP 8.5 clone-with syntax used in respect/validation 3.x, causing the Respect\Validation\Result and Respect\Validation\ValidatorBuilder classes to appear unknown to the static analyser. PHPStan 2.x adds support for PHP 8.5 syntax.
self always resolves to the declaring class (SimpleChain), while static correctly uses late static binding to reflect the actual calling class. Since __call returns $this, static is the semantically accurate type.
PHPStan 2.x changed how @mixin handles static return types: instead of resolving static to the calling class, it resolves to the mixin type itself (Mixin interface). This causes chain navigation methods (all(), key(), property()) called after assertion methods to appear undefined on the Mixin type, cascading into method.nonObject errors. Remove the phpt file extension (no .phpt files remain after the switch to PHPUnit integration tests). Retain the existing regex for undefined Mixin method calls and add a new identifier-scoped rule to suppress the cascading method.nonObject false positives in integration tests.
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.
No description provided.