Skip to content

Decide what the hydrator does once per class with handlers - #224

Closed
DanielBadura wants to merge 2 commits into
3.0.xfrom
handlers
Closed

DanielBadura wants to merge 2 commits into
3.0.xfrom
handlers

Conversation

@DanielBadura

Copy link
Copy Markdown
Member

The StackHydrator injected itself into every HydratorAwareNormalizer of the
loaded metadata. That mutated shared, cached metadata and made it hard for a
hydrator which wraps the StackHydrator to see nested objects. The hydrator is
now passed with every call in the context under Hydrator::HYDRATOR. Each
hydrator only sets it if it is missing, so the outermost one wins.

HydratorAwareNormalizer and the setHydrator() methods of the built-in
normalizers are removed.

The StackHydrator keeps a handler per class and direction: the transformer
itself if nothing else has to run, otherwise a handler which calls the class
normalizer, runs the middlewares or creates a lazy proxy. The middleware chain
is built once per class and reused.

Transformer factories get a TransformerResolver, the ReflectionTransformer uses
it to map nested objects with the handlers of their classes directly instead of
going through the ObjectNormalizer and the hydrator again.
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Hello 👋

here is the most recent benchmark result:

StackHydratorBench
==================

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+--------------------+------------------+-----------------+------------+
| subject                       | Tag: <current>     | Tag: base        | Tag: <current>  | Tag: base  |
+-------------------------------+--------------------+------------------+-----------------+------------+
| benchHydrate1Object ()        | 72.000μs (±0.00%)  | 3.600μs (±0.00%) | 2.837mb         | 3.272mb    |
| benchExtract1Object ()        | 3.400μs (±0.00%)   | 4.000μs (±0.00%) | 2.788mb         | 3.272mb    |
| benchHydrate1000Objects ()    | 1.976ms (±0.00%)   | 2.714ms (±0.00%) | 2.945mb         | 3.035mb    |
| benchExtract1000Objects ()    | 1.990ms (±0.00%)   | 2.621ms (±0.00%) | 2.917mb         | 3.032mb    |
| benchHydrate1000000Objects () | 919.458ms (±0.00%) | 1.420s (±0.00%)  | 2.893mb         | 3.035mb    |
| benchExtract1000000Objects () | 978.744ms (±0.00%) | 1.578s (±0.00%)  | 2.917mb         | 3.032mb    |
+-------------------------------+--------------------+------------------+-----------------+------------+

HydratorWithLazyBench
=====================

+------------------------------------------+-----------------------------------------+------------------------------+
|                                          | time (kde mode)                         | memory                       |
+------------------------------------------+--------------------+--------------------+-----------------+------------+
| subject                                  | Tag: <current>     | Tag: base          | Tag: <current>  | Tag: base  |
+------------------------------------------+--------------------+--------------------+-----------------+------------+
| benchHydrate1Object ()                   | 0.800μs (±0.00%)   | 1.000μs (±0.00%)   | 2.790mb         | 2.772mb    |
| benchHydrate1ObjectTriggerInit ()        | 3.800μs (±0.00%)   | 5.000μs (±0.00%)   | 2.790mb         | 2.772mb    |
| benchHydrate1000Objects ()               | 462.333μs (±0.00%) | 616.333μs (±0.00%) | 2.892mb         | 2.772mb    |
| benchHydrate1000ObjectsTriggerInit ()    | 2.740ms (±0.00%)   | 3.618ms (±0.00%)   | 2.909mb         | 3.020mb    |
| benchHydrate1000000Objects ()            | 303.694ms (±0.00%) | 383.484ms (±0.00%) | 2.892mb         | 2.772mb    |
| benchHydrate1000000ObjectsTriggerInit () | 1.663s (±0.00%)    | 2.335s (±0.00%)    | 2.900mb         | 3.020mb    |
+------------------------------------------+--------------------+--------------------+-----------------+------------+

StackHydratorWithCryptographyBench
==================================

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| subject                       | Tag: <current>    | Tag: base         | Tag: <current>  | Tag: base  |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| benchHydrate1Object ()        | 70.600μs (±0.00%) | 7.400μs (±0.00%)  | 2.911mb         | 2.822mb    |
| benchExtract1Object ()        | 15.400μs (±0.00%) | 18.800μs (±0.00%) | 2.877mb         | 2.822mb    |
| benchHydrate1000Objects ()    | 3.337ms (±0.00%)  | 4.421ms (±0.00%)  | 2.984mb         | 2.991mb    |
| benchExtract1000Objects ()    | 8.253ms (±0.00%)  | 9.809ms (±0.00%)  | 3.296mb         | 3.202mb    |
| benchHydrate1000000Objects () | 1.609s (±0.00%)   | 2.337s (±0.00%)   | 2.984mb         | 2.991mb    |
| benchExtract1000000Objects () | 5.936s (±0.00%)   | 7.308s (±0.00%)   | 3.296mb         | 3.202mb    |
+-------------------------------+-------------------+-------------------+-----------------+------------+

HydratorBench
=============

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+--------------------+------------------+-----------------+------------+
| subject                       | Tag: <current>     | Tag: base        | Tag: <current>  | Tag: base  |
+-------------------------------+--------------------+------------------+-----------------+------------+
| benchHydrate1Object ()        | 76.000μs (±0.00%)  | 3.400μs (±0.00%) | 2.837mb         | 2.772mb    |
| benchExtract1Object ()        | 3.600μs (±0.00%)   | 3.600μs (±0.00%) | 2.788mb         | 2.772mb    |
| benchHydrate1000Objects ()    | 2.056ms (±0.00%)   | 2.753ms (±0.00%) | 2.945mb         | 3.035mb    |
| benchExtract1000Objects ()    | 1.961ms (±0.00%)   | 2.597ms (±0.00%) | 2.917mb         | 3.032mb    |
| benchHydrate1000000Objects () | 927.646ms (±0.00%) | 1.400s (±0.00%)  | 2.893mb         | 3.035mb    |
| benchExtract1000000Objects () | 962.826ms (±0.00%) | 1.543s (±0.00%)  | 2.917mb         | 3.032mb    |
+-------------------------------+--------------------+------------------+-----------------+------------+

HydratorWithCryptographyBench
=============================

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| subject                       | Tag: <current>    | Tag: base         | Tag: <current>  | Tag: base  |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| benchHydrate1Object ()        | 70.600μs (±0.00%) | 7.800μs (±0.00%)  | 2.911mb         | 2.822mb    |
| benchExtract1Object ()        | 21.600μs (±0.00%) | 17.000μs (±0.00%) | 2.877mb         | 2.822mb    |
| benchHydrate1000Objects ()    | 3.320ms (±0.00%)  | 4.412ms (±0.00%)  | 2.984mb         | 2.991mb    |
| benchExtract1000Objects ()    | 8.315ms (±0.00%)  | 9.895ms (±0.00%)  | 3.296mb         | 3.202mb    |
| benchHydrate1000000Objects () | 1.660s (±0.00%)   | 2.347s (±0.00%)   | 2.984mb         | 2.991mb    |
| benchExtract1000000Objects () | 5.924s (±0.00%)   | 7.106s (±0.00%)   | 3.296mb         | 3.202mb    |
+-------------------------------+-------------------+-------------------+-----------------+------------+

This comment gets update everytime a new commit comes in!

The handlers and the transformer described the same thing: what runs for a
class in one direction. The resolver returned either of them, so the
hydrator and the transformers had to tell them apart, and the handler and
transformer namespaces depended on each other.

Now there is one concept per direction. A ClassHydrator hydrates the
objects of one class from an array, a ClassExtractor extracts them. The
ClassTransformer is both and the end of the chain the hydrator builds per
class, every middleware and the lazy proxy wrap the rest of the chain. If
nothing else runs, the transformer is the whole chain.

A middleware gets the rest of the chain as $next and calls it with the data
and the context, Next is removed. A class with a class normalizer has no
chain, the hydrator calls the normalizer and the resolver returns null.
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