Repository navigation
Map each class with a mapper built once per class - #226
Merged
Merged
Conversation
The hydrator now builds one chain per class, once: the mapper of the class wrapped in the middlewares which run for it and, for lazy classes, the lazy proxy. A middleware gets the rest of that chain as a Mapper instead of Next, so nothing is allocated per call anymore. Transformer is renamed to Mapper throughout. MapperFactory replaces ClassTransformerFactory and gets the StackHydrator, whose mapper() returns the chain of a class. The ReflectionMapper uses it to map nested objects of an ObjectNormalizer right away instead of going through the normalizer and the hydrator again. ChainTransformerFactory and ReflectionTransformerFactory are gone, the hydrator asks its factories in order and falls back to the ReflectionMapper. PRIORITY_BEFORE_TRANSFORM and the beforeTransform argument of the UpcastExtension are renamed to PRIORITY_BEFORE_MAPPING and beforeMapping.
|
Hello 👋 here is the most recent benchmark result:
This comment gets update everytime a new commit comes in! |
When the chain of a nested class is a ReflectionMapper of the same hydrator, its properties are mapped right away. The object to populate and the hydrator in the context were already checked by the parent mapper.
DanielBadura
approved these changes
Oct 16, 2026
DanielBadura
left a comment
Member
There was a problem hiding this comment.
Besides the 2 comments, LGTM
| * | ||
| * @return array<string, array{class-string, bool}> | ||
| * | ||
| * @infection-ignore-all |
Member
Author
There was a problem hiding this comment.
The nested shortcut is only an optimization: if a mutation breaks it, the object goes through the ObjectNormalizer and StackHydrator::hydrate() and ends up in the same chain with the same result. So those mutants are equivalent and can't be killed by a test, the StackHydrator is final and there's nothing to observe. Without the annotation the diff MSI drops below 90%.
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.
The transformer naming grew out of the old middleware stack and left us with several names for the same thing. This replaces it with a single concept: the hydrator maps every class with a
Mapper, middlewares wrap that mapper and normalizers convert single values.The hydrator now builds one chain per class, once. It consists of the mapper of the class, the middlewares which don't skip it and the lazy proxy for lazy classes. A middleware gets the rest of that chain as a
Mapperinstead ofNext, so nothing is allocated per call anymore. Skipping per direction still works, a link simply passes the skipped direction on.MapperFactoryreplacesClassTransformerFactoryand receives theStackHydrator. Its newmapper()method returns the chain of a class, which is the hook for generated code. TheReflectionMapperuses it to map nested objects of anObjectNormalizerright away instead of going through the normalizer and the hydrator again, as long as the call comes from its own hydrator.ChainTransformerFactoryandReflectionTransformerFactoryare gone.For consistency
PRIORITY_BEFORE_TRANSFORMand thebeforeTransformargument of theUpcastExtensionare renamed toPRIORITY_BEFORE_MAPPINGandbeforeMapping. The upgrade guide covers all of it.Compared to 3.0.x, hydrating and extracting the benchmark event with three nested objects takes roughly 40% less time. With 20 nested objects it is about twice as fast.