Extract PropertyResolver strategy#1799
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1799 +/- ##
============================================
- Coverage 99.27% 99.23% -0.04%
- Complexity 1048 1058 +10
============================================
Files 197 200 +3
Lines 2466 2602 +136
============================================
+ Hits 2448 2582 +134
- Misses 18 20 +2 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
db23275 to
18a3c72
Compare
c856a35 to
37123a7
Compare
eb82319 to
b3734c1
Compare
There was a problem hiding this comment.
Pull request overview
This PR refactors Respect\Validation\Validators\Attributes by extracting “which validators apply to a property” into a dedicated PropertyResolver strategy interface with composable implementations, aiming to make resolution extensible and to avoid duplicate nested validation (notably around #[Attributes] + declared class types).
Changes:
- Introduces
PropertyResolverplusDeclaredTypePropertyResolver,ExplicitAttributePropertyResolver, andCompositePropertyResolver. - Updates
Attributesto accept an injectable resolver chain and to delegate property-validator discovery to it. - Adds unit tests and new test stubs to cover the new resolution strategies and composite de-duplication behavior; updates docs and the dev lint command exclusions.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/Validators/Attributes/ExplicitAttributePropertyResolverTest.php | Adds unit coverage for resolving explicit validator attributes from properties. |
| tests/unit/Validators/Attributes/DeclaredTypePropertyResolverTest.php | Adds unit coverage for resolving validators from declared property types (named/union/intersection). |
| tests/unit/Validators/Attributes/CompositePropertyResolverTest.php | Adds unit coverage for composing resolver strategies and collapsing duplicate Attributes instances. |
| tests/src/Stubs/WithUntypedProperty.php | Adds stub for “no declared type” property resolver behavior. |
| tests/src/Stubs/WithNoValidatorAttributes.php | Adds stub for properties with no validator attributes. |
| tests/src/Stubs/WithMixedProperty.php | Adds stub used by composite resolver tests. |
| tests/src/Stubs/WithExplicitStringTypeProperty.php | Adds stub to test explicit validator attribute resolution (#[StringType]). |
| tests/src/Stubs/WithExplicitAttributesAttributeProperty.php | Adds stub to test explicit #[Attributes] resolution returning the passed Attributes instance. |
| tests/src/Stubs/WithClassTypedProperty.php | Adds stub for declared class-typed property resolution. |
| tests/src/Stubs/StubPropertyResolver.php | Adds a test double implementing PropertyResolver for composite resolver tests. |
| src/Validators/Attributes/PropertyResolver.php | Introduces the new resolver interface used by Attributes. |
| src/Validators/Attributes/ExplicitAttributePropertyResolver.php | Implements resolution of explicit attribute-based validators on a property. |
| src/Validators/Attributes/DeclaredTypePropertyResolver.php | Implements resolution from declared property type (including union/intersection cases). |
| src/Validators/Attributes/CompositePropertyResolver.php | Composes multiple resolvers and collapses duplicate $attributes entries. |
| src/Validators/Attributes.php | Injects the resolver chain into Attributes and removes inline property-resolution logic. |
| src-dev/Commands/LintMixinCommand.php | Excludes the new PropertyResolver type from mixin lint generation. |
| docs/validators/Attributes.md | Documents the new constructor signature accepting a PropertyResolver. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b3734c1 to
1ecb9b5
Compare
Previously, the resolution of validators from property types and explicit new resolution strategies required editing the class, and a class-typed property annotated with #[Attributes] was validated twice — once by the explicit-attribute branch and again by the declared-type branch — which tripped the circular-reference guard on the second pass. This commit moves that responsibility to a dedicated PropertyResolver interface with composable implementations, so each strategy can be developed and composed independently. The composite collapses duplicate Attributes entries, ensuring each property is validated exactly once even when multiple resolution paths produce the same instance. The Attributes class is reduced to its single responsibility, and resolver chains become pluggable through the constructor.
1ecb9b5 to
f2ab556
Compare
191f36d to
88dfdfb
Compare
The circular-reference guard keyed visited objects by spl_object_id(), which is recycled once an object is garbage-collected. On a reused validator instance this caused two problems: unbounded growth of the visited map across calls, and false circular-reference failures when a fresh object inherited a freed ID. WeakMap is keyed by object identity, so distinct objects can never collide, and entries are reclaimed automatically when the key object is garbage-collected, no manual reset is required. Since WeakMap is not serializable, __sleep excludes the field and __wakeup reinitializes it, which also avoids carrying stale IDs across unserialize.
88dfdfb to
b2f7e6f
Compare
Previously, the resolution of validators from property types and explicit new resolution strategies required editing the class, and a class-typed property annotated with #[Attributes] was validated twice — once by the explicit-attribute branch and again by the declared-type branch — which tripped the circular-reference guard on the second pass.
This commit moves that responsibility to a dedicated PropertyResolver interface with composable implementations, so each strategy can be developed and composed independently. The composite collapses duplicate Attributes entries, ensuring each property is validated exactly once even when multiple resolution paths produce the same instance. The Attributes class is reduced to its single responsibility, and resolver chains become pluggable through the constructor.