Integrate Fluent, FluentGen and FluentAnalysis#1730
Integrate Fluent, FluentGen and FluentAnalysis#1730alganet wants to merge 1 commit intoRespect:mainfrom
Conversation
|
This is a draft, which includes BC breaks and FluentAnalysis integration, mentioned in #1729 |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1730 +/- ##
============================================
- Coverage 99.41% 99.40% -0.02%
+ Complexity 1020 998 -22
============================================
Files 194 191 -3
Lines 2387 2334 -53
============================================
- Hits 2373 2320 -53
Misses 14 14 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
8827c6b to
10d894a
Compare
da4a13c to
a9f8dcc
Compare
Replace the internal code generation and builder infrastructure with Respect/Fluent for runtime method resolution, Respect/FluentGen for mixin interface generation, and Respect/FluentAnalysis for PHPStan type narrowing. ValidatorBuilder now extends Fluent's Append builder with ComposableMap for prefix composition at runtime. Each validator is annotated with: - #[Assurance] declaring the type it narrows to (100+ validators) - #[AssuranceParameter] on constructor params used for dynamic type resolution (Instance) - #[Composable] with class-string references for prefix composition constraints (Not, All, Key, Property, NullOr, UndefOr, Min, Max, Length, and 29 validators with with/without constraints) - #[ComposableParameter] on promoted prefix parameters (Key, Property) Removes the internal CodeGen infrastructure (MethodBuilder, MixinGenerator, PrefixMapGenerator, etc.) in favor of FluentGen. Adds type inference tests validating PHPStan narrowing for type validators, val variants, composites (allOf, anyOf, oneOf, noneOf, when), modifiers (not, nullOr, undefOr), element narrowing (each, all), value/member narrowing (identical, in), parameter narrowing (instance), and chain intersection.
a9f8dcc to
fbc2247
Compare
| '{{subject}} must be a boolean', | ||
| '{{subject}} must not be a boolean', | ||
| )] | ||
| #[Assurance(type: 'bool')] |
There was a problem hiding this comment.
I don't think this is true, though. I think the assurance should be strict
There was a problem hiding this comment.
Good catch. I need to review all narrowings I declared. There might be more than this one innacurate.
| '{{subject}} must not be sorted in descending order', | ||
| self::TEMPLATE_DESCENDING, | ||
| )] | ||
| #[Assurance(type: 'array|string')] |
There was a problem hiding this comment.
What's the difference between type: 'array|string' and type: ['array', 'string']? I've seen both
There was a problem hiding this comment.
For plain types, it makes no difference at all during runtime.
In runtime code, zero difference. This is imploded by | when it is an array. It's an array to allow users to use ::class when the target is a class, which enhances refactoring tools and IDE assistance.
This is a part of the design I'm not entirely sure about.
There was a problem hiding this comment.
What if type would accept an AssuranceType object (or a string that creates a SingleType). That way we could allow union types as well:
interface AssuranceType {}
class SimpleType implements AssuranceType {}
class IntersectionType implements AssuranceType {}
You could have a from() in all those classes.
new SimpleType('string');
new UnionType('string', 'int');
new IntersectionType(Countable::class, Iterable::class);The attribute declaration could remain the similar (with a factory method under the hood):
#[Assurance(type: 'string')]But it would also allow more explicit declarations:
#[Assurance(type: new UnionType('string', 'int'))]But, to be honest, I wouldn't mind always passing an object to type anyways
There was a problem hiding this comment.
As it is today, one could pass generics too: array<FileInfo> and that should work. The type API gets hairy very quickly with the edge cases, and in the end we're inventing a new type declaration API. If going that way, probably sebastian/type is a better choice.
I personally like pure string (friendly to search&replace: you change a phpdoc and the attribute in one go), but I recognize IDEs fare better when classes use ::class class constants ("Refactor class..." option in some IDEs). Also future-proof (later we can add more support for other ways).
Another avenue could be trying to break phpstan to allow using mixed $input on ->evaluate(), but a different phpdoc for it (which today is not allowed), and the attribute would instead be on the evaluate method. That would require some changes to Simple validators (basically, all validators would need explicit evaluate), and it's an untested approach to extensions that I never tried.
| public static function __callStatic(string $ruleName, array $arguments): self | ||
| public static function __callStatic(string $ruleName, array $arguments): static | ||
| { | ||
| return self::init()->__call($ruleName, $arguments); |
There was a problem hiding this comment.
I think we'd still try/catch to throw an exception from Validation
There was a problem hiding this comment.
The way it is, it would throw FluentException instead, which might be more descriptive than ComponentException. There are tests for this behavior!
There was a problem hiding this comment.
We could pass the message through, though. I'm more concerned about the type. I think it's useful for the user to expect a Respect\Validation\Exceptions\Exception from this library, and this would break that.
There was a problem hiding this comment.
You're right! I'll do it.
Replace the internal code generation and builder infrastructure with Respect/Fluent for runtime method resolution, Respect/FluentGen for mixin interface generation, and Respect/FluentAnalysis for PHPStan type narrowing.
ValidatorBuilder now extends Fluent's Append builder with ComposableMap for prefix composition at runtime.
Each validator is annotated with:
Removes the internal CodeGen infrastructure (MethodBuilder, MixinGenerator, PrefixMapGenerator, etc.) in favor of FluentGen.
Adds type inference tests validating PHPStan narrowing for type validators, val variants, composites (allOf, anyOf, oneOf, noneOf, when), modifiers (not, nullOr, undefOr), element narrowing (each, all), value/member narrowing (identical, in), parameter narrowing (instance), and chain intersection.