Integrate Respect/Fluent and Respect/FluentGen#1729
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1729 +/- ##
============================================
- Coverage 99.42% 99.26% -0.16%
- Complexity 1034 1040 +6
============================================
Files 195 196 +1
Lines 2416 2438 +22
============================================
+ Hits 2402 2420 +18
- Misses 14 18 +4 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
5b12244 to
36947eb
Compare
There was a problem hiding this comment.
Pull request overview
This PR migrates Respect/Validation’s fluent rule resolution and mixin/codegen infrastructure from the in-house implementation to the external respect/fluent runtime and respect/fluentgen generator, while keeping the validation rule surface largely the same.
Changes:
- Introduces
FluentValidatorFactoryand wires it intoContainerRegistryusingComposingLookup + NamespaceLookup + ComposableMapfor prefix-aware rule resolution. - Replaces the custom
#[Mixin]attribute usages on validators with#[Composable](fromrespect/fluent) and switches generated prefix metadata fromPrefixMaptoPrefixConstants. - Removes the legacy
src-dev/CodeGenimplementation and updates the mixin lint command to userespect/fluentgen; updates composer dependencies accordingly.
Reviewed changes
Copilot reviewed 63 out of 64 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/FluentValidatorFactoryTest.php | Adds unit coverage for the new Fluent-based validator factory behavior. |
| src/FluentValidatorFactory.php | New Fluent-backed ValidatorFactory adapter mapping Fluent exceptions to existing Validation exceptions. |
| src/ContainerRegistry.php | Switches default DI wiring to the Fluent-based factory and ComposableMap prefix resolution. |
| src/Transformers/Prefix.php | Updates prefix metadata reference from PrefixMap to PrefixConstants. |
| src/Mixins/PrefixMap.php | Removes the previous generated prefix metadata class. |
| src/Mixins/PrefixConstants.php | Adds new generated prefix metadata constants (replacing PrefixMap). |
| src/Mixins/Builder.php | Updates generated fluent builder signature union ordering (`int |
| src/Mixins/Chain.php | Updates generated fluent chain signature union ordering (`int |
| src/Mixins/NotBuilder.php | Updates generated fluent builder signature union ordering (`int |
| src/Mixins/NotChain.php | Updates generated fluent chain signature union ordering (`int |
| src/Mixins/NullOrBuilder.php | Updates generated fluent builder signature union ordering (`int |
| src/Mixins/NullOrChain.php | Updates generated fluent chain signature union ordering (`int |
| src/Mixins/UndefOrBuilder.php | Updates generated fluent builder signature union ordering (`int |
| src/Mixins/UndefOrChain.php | Updates generated fluent chain signature union ordering (`int |
| src/Validators/All.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Attributes.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Between.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/BetweenExclusive.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Blank.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Equals.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Equivalent.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Even.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Exists.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Factor.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Finite.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Formatted.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/GreaterThan.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/GreaterThanOrEqual.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Identical.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/In.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Infinite.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Key.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/KeyExists.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/KeyOptional.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/KeySet.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Length.php | Replaces custom #[Mixin] with Fluent #[Composable] and updates opt-in semantics. |
| src/Validators/LessThan.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/LessThanOrEqual.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Max.php | Replaces custom #[Mixin] with Fluent #[Composable] and updates opt-in semantics. |
| src/Validators/Min.php | Replaces custom #[Mixin] with Fluent #[Composable] and updates opt-in semantics. |
| src/Validators/Multiple.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Named.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Not.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/NullOr.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Odd.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Positive.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Property.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/PropertyExists.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/PropertyOptional.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Templated.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/Undef.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src/Validators/UndefOr.php | Replaces custom #[Mixin] with Fluent #[Composable]. |
| src-dev/Commands/LintMixinCommand.php | Switches mixin/prefix constant generation linting from in-house codegen to respect/fluentgen. |
| src-dev/CodeGen/CodeGenerator.php | Removes legacy in-house codegen API. |
| src-dev/CodeGen/Config.php | Removes legacy in-house codegen config. |
| src-dev/CodeGen/InterfaceConfig.php | Removes legacy in-house interface config. |
| src-dev/CodeGen/NamespaceScanner.php | Removes legacy in-house namespace scanner. |
| src-dev/CodeGen/OutputFormatter.php | Removes legacy in-house output formatter. |
| src-dev/CodeGen/FluentBuilder/MethodBuilder.php | Removes legacy in-house fluent method builder. |
| src-dev/CodeGen/FluentBuilder/Mixin.php | Removes legacy in-house #[Mixin] attribute. |
| src-dev/CodeGen/FluentBuilder/MixinGenerator.php | Removes legacy in-house mixin generator. |
| src-dev/CodeGen/FluentBuilder/PrefixMapGenerator.php | Removes legacy in-house prefix map generator. |
| composer.json | Adds respect/fluent and respect/fluentgen dependencies. |
| composer.lock | Locks new Fluent/FluentGen dependencies and updates other dependency versions accordingly. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
b2cc661 to
d8cbcf8
Compare
|
@TheRespectPanda benchmark |
|
Triggered phpbench benchmarks for PR #1729 — workflow run: https://github.com/Respect/Validation/actions/workflows/ci-perf.yml |
d8cbcf8 to
001b6b4
Compare
|
Some updates and issues I see with the current design:
Basically, removing all references we have for parameters and methods that are stored in strings like // Before:
#[Composable(prefixParameter: 'foo')]
class Instance {
public function __construct(private string $foo) {}
}
// After:
#[Composable]
class Instance {
public function __construct(
#[ComposableParameter] private string $foo,
) {}
}For future type narrowing (as described in #1681), I just can't quite get a good design for the type algebra parameters. It's likely they will be strings with phpdoc union types that are parsed by the extension. |
db0f2bb to
64c1262
Compare
|
I addressed the concerns in my previous comment. Much cleaner I also went full static analysis on a separate PR #1730 that takes this approach even further and it in draft. |
|
I was able to integrate more of my local changes into #1730. I'm still not satisfied with some things, like: #[Assurance(type: 'array|Countable')]
final class Countable extends SimpleI can't find a good way of expressing those union types for phpstan narrowing. The obvious: #[Assurance(type: ['array', Countable::class])]
final class Countable extends Simple... is not so good future-proofing for more complex expressions. We can't assume it will always be unions. |
henriquemoody
left a comment
There was a problem hiding this comment.
This is absolutely awesome!
I haven't read the code from Fluent and FluentGen yet, but it seems really great!
64c1262 to
5c982d2
Compare
|
I decided I'll work more on Fluent, FluentGen and FluentAnalysis and first integrate them into Data+Relational ($mapper->table auto-complete, syntax analysis), Rest (routine composition, static analysis that potentially understand basic HTTP semantics) and Config (container auto-complete). Those projects are in incubation mode and I can iterate much faster on them, which presents more dogfooding opportunities. Once I'm happy with the abstractions there, I'll come back to Validation. |
|
Hey, @alganet ! Do you have any plans of getting back to this? I'm considering upgrading Assertion and this would be incredibly useful! |
|
@henriquemoody I do have plans for it! Is this the PR you actually need? (this replaces stub generation, mostly). The one with narrowing is #1730, related to #1681. That one needs more work (and minor releases of Fluent and FluentAnalysis). We can chose either one or the other, #1730 obsoletes this one, but it's also larger and less incremental. I'm fine with either sequence (#1729 first, then a revised #1730; or #1730 directly). Let me know, and I'll update a new amend for review in the one you actually need right now. |
|
In fact, #1730 is really what I need, even though I suspect there would be a some work to make it work with assertion. My goal is allowing this: Assertion::intType($var);
echo substr('String', $var);not to fail because $var is not an integer, but also allow IDEs to identify those as much possible, which probably means reading the attributes and translating them into docblocks with No rush, though, it might take some time for me to get deep into that. |
|
The way The division is:
If we want stubs with Respect\Assertion would probably need nothing for case 1 (the substr not failing phpstan) if the extension works as intended. The information is ingested by phpstan in runtime and it knows Assertion is an adapter for Validation (which carries semantics of the types). I haven't tested case 2 (the IDE itself knowing this deeply). |
5c982d2 to
0eaddb8
Compare
0eaddb8 to
d4f238a
Compare
d4f238a to
6f864a8
Compare
Replace the in-house factory and codegen infrastructure with Fluent and FluentGen packages. The runtime now uses ComposingLookup with ComposableMap for prefix resolution via a FluentValidatorFactory adapter that preserves exception types. Validators use #[Composable] from Fluent instead of the custom #[Mixin] attribute. Mixin generation uses FluentGen, producing PrefixConstants in place of PrefixMap. The old src-dev/CodeGen directory is removed. All public APIs and BC are preserved.
6f864a8 to
22a357c
Compare
henriquemoody
left a comment
There was a problem hiding this comment.
This is really awesome!
I have just a few questions, but it looks good. Another question of mine is whether you've benchmarked those changes. Have you seen any significant differences?
|
Another thought that occurred to me is how we'll integrate Respect\Parameters with the |
|
@henriquemoody I'm working on the container cleanup, that will come soon. |
|
@TheRespectPanda benchmark |
|
Triggered phpbench benchmarks for PR #1729 — workflow run: https://github.com/Respect/Validation/actions/workflows/ci-perf.yml |
|
@henriquemoody benchmarks will be here https://github.com/Respect/Validation/actions/runs/28156799528/job/83387267406
|
Sounds good to me! I think we should hold version 3.2 until we finish that stuff, so we don't generate BC breaks, do you agree? |
|
@henriquemoody what BC break are we talking about? So much stuff lately that I'm lost 😅 Either way, probably yes (lots of new features, this is 3.2 or 4.0) |
|
I was talking about the No API breaks in |
|
@henriquemoody for the Parameter thing, you're talking about? We can implement a local FluentFactory that receives the parameter resolver. Then, we pass it to the FluentValidatorFactory instead of the It's a composite pattern, these lookup classes can be nested and arranged and a user of the |
|
I ran a rebaseline through manual workflow invokation, then, I ran a benchmark again. There are regressions in the composite validator timings, likely due to the indirection of going through one more class. They're more than I would like, but manageable. I will be investigating them, but likely merge as it is. It's probable that the parameter work will add even more delay if my hypothesis is correct (if it is, indirection in the builder is bad and I'll work towards perf optimizing it). |
Replace the in-house factory and codegen infrastructure with Fluent and FluentGen packages. The runtime now uses ComposingLookup with ComposableMap for prefix resolution via a FluentValidatorFactory adapter that preserves exception types. Validators use #[Composable] from Fluent instead of the custom #[Mixin] attribute. Mixin generation uses FluentGen, producing PrefixConstants in place of PrefixMap. The old src-dev/CodeGen directory is removed. All public APIs and BC are preserved.
This PR is currently molded as a 3.x release, but we could push the integration further and remove more code by integrating deeper with Respect\Fluent. However, that breaks BC, and it would be a 4.x release instead.
That BC break would be kind of "soft", in the sense that validators keep working. It would only affect really advanced users that have their own ValidatorFactory implementations. This is related to #1710 somehow: if we had automated backports, a 4.x release would make more sense (easier to keep both 3.x and this 4.x iteration, since the bulk of validators don't change, just some wiring on fluent stuff).
Integration with Respect/FluentAnalysis is not yet in this PR, but its use should be possible with it with some boilerplate configuration.
Those three together unlock reusable, declarative advanced fluent composition, type inference and IDE support with extensibility. For example, Validation extensions with custom namespaces could ship
#[Composable]annotations and end users would get IDE support for custom and extended rules too (requires some wiring on composer scripts).This PR introduces two new projects. I also welcome reviews on them as issues on their specific repositories, linked to this PR number so we can track them as a group.