Skip to content

Upgrade Validation to v3#11

Open
henriquemoody wants to merge 13 commits into
Respect:mainfrom
henriquemoody:upgrade_v3
Open

Upgrade Validation to v3#11
henriquemoody wants to merge 13 commits into
Respect:mainfrom
henriquemoody:upgrade_v3

Conversation

@henriquemoody

Copy link
Copy Markdown
Member

No description provided.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant