Skip to content

Map the data with class transformers and remove the CoreExtension - #217

Merged
DavidBadura merged 2 commits into
3.0.xfrom
class-transformer
Oct 6, 2026
Merged

DavidBadura merged 2 commits into
3.0.xfrom
class-transformer

Conversation

@DavidBadura

Copy link
Copy Markdown
Member

The property mapping is no longer a middleware. After the last middleware, Next calls the ClassTransformer of the class, and if no middleware runs for a class at all, the hydrator calls the transformer directly without creating a Next.

Transformers come from a ClassTransformerFactory. Factories are registered with StackHydratorBuilder::addTransformerFactory(), the builder chains them in a ChainTransformerFactory and always appends the ReflectionTransformerFactory, so reflection is just the last factory instead of a hidden fallback. This is the extension point the generated code from #214 will plug into later. The StackHydrator itself takes a single factory and defaults to reflection.

The builder now also always appends the BuiltInGuesser after the registered guessers, like the AttributeMetadataFactory already did without the builder. With that the CoreExtension had nothing left to register and is removed. new StackHydrator() and (new StackHydratorBuilder())->build() behave the same without registering anything.

Removed, since they can not happen or have no effect anymore:

  • TransformMiddleware (its code lives on in the ReflectionTransformer)
  • CoreExtension
  • MissingMiddlewares, AllMiddlewaresSkipped, NoMoreMiddleware
  • Extension::PRIORITY_TRANSFORM

The ReflectionTransformer only unsets OBJECT_TO_POPULATE from the context when it is set. unset() copies the array even if the key is missing, and since #215 the context always contains the hydrator, so this saved a copy per object.

ClassTransformerFactory::create() only gets the metadata for now. The generated transformers will need more (metadata of nested classes, the middlewares), I would extend that together with the codegen PR instead of guessing now.

The upgrade guide covers the removal of the CoreExtension, the TransformMiddleware and the exceptions.

The TransformMiddleware is replaced by a ClassTransformer which the hydrator
calls after the last middleware, or directly if no middleware runs for a
class. Transformers come from a ClassTransformerFactory, the builder chains
the registered factories and always ends with the ReflectionTransformerFactory.
The builder also always adds the BuiltInGuesser as last guesser, so the
CoreExtension had nothing left to do and is removed.

MissingMiddlewares, AllMiddlewaresSkipped, NoMoreMiddleware and
PRIORITY_TRANSFORM can not happen or have no effect anymore and are removed.
@github-actions

github-actions Bot commented Oct 6, 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 ()        | 3.600μs (±0.00%) | 4.200μs (±0.00%) | 2.772mb         | 3.296mb    |
| benchExtract1Object ()        | 4.000μs (±0.00%) | 4.600μs (±0.00%) | 2.772mb         | 2.772mb    |
| benchHydrate1000Objects ()    | 2.801ms (±0.00%) | 3.054ms (±0.00%) | 3.035mb         | 3.052mb    |
| benchExtract1000Objects ()    | 2.623ms (±0.00%) | 2.813ms (±0.00%) | 3.032mb         | 3.020mb    |
| benchHydrate1000000Objects () | 1.412s (±0.00%)  | 1.809s (±0.00%)  | 3.035mb         | 3.052mb    |
| benchExtract1000000Objects () | 1.516s (±0.00%)  | 1.757s (±0.00%)  | 3.032mb         | 3.020mb    |
+-------------------------------+------------------+------------------+-----------------+------------+

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

+------------------------------------------+-----------------------------------------+------------------------------+
|                                          | time (kde mode)                         | memory                       |
+------------------------------------------+--------------------+--------------------+-----------------+------------+
| subject                                  | Tag: <current>     | Tag: base          | Tag: <current>  | Tag: base  |
+------------------------------------------+--------------------+--------------------+-----------------+------------+
| benchHydrate1Object ()                   | 1.400μs (±0.00%)   | 1.000μs (±0.00%)   | 2.772mb         | 2.772mb    |
| benchHydrate1ObjectTriggerInit ()        | 4.600μs (±0.00%)   | 5.200μs (±0.00%)   | 2.772mb         | 2.772mb    |
| benchHydrate1000Objects ()               | 607.000μs (±0.00%) | 522.333μs (±0.00%) | 2.772mb         | 2.830mb    |
| benchHydrate1000ObjectsTriggerInit ()    | 3.931ms (±0.00%)   | 3.852ms (±0.00%)   | 3.020mb         | 2.865mb    |
| benchHydrate1000000Objects ()            | 368.592ms (±0.00%) | 357.678ms (±0.00%) | 2.772mb         | 2.830mb    |
| benchHydrate1000000ObjectsTriggerInit () | 2.306s (±0.00%)    | 2.452s (±0.00%)    | 3.020mb         | 2.865mb    |
+------------------------------------------+--------------------+--------------------+-----------------+------------+

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

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| subject                       | Tag: <current>    | Tag: base         | Tag: <current>  | Tag: base  |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| benchHydrate1Object ()        | 8.000μs (±0.00%)  | 8.200μs (±0.00%)  | 2.822mb         | 2.820mb    |
| benchExtract1Object ()        | 17.400μs (±0.00%) | 22.600μs (±0.00%) | 2.822mb         | 2.820mb    |
| benchHydrate1000Objects ()    | 4.376ms (±0.00%)  | 4.687ms (±0.00%)  | 2.991mb         | 3.132mb    |
| benchExtract1000Objects ()    | 9.958ms (±0.00%)  | 9.965ms (±0.00%)  | 3.202mb         | 3.201mb    |
| benchHydrate1000000Objects () | 2.367s (±0.00%)   | 2.720s (±0.00%)   | 2.991mb         | 3.132mb    |
| benchExtract1000000Objects () | 7.281s (±0.00%)   | 7.171s (±0.00%)   | 3.202mb         | 3.201mb    |
+-------------------------------+-------------------+-------------------+-----------------+------------+

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

+-------------------------------+-------------------------------------+------------------------------+
|                               | time (kde mode)                     | memory                       |
+-------------------------------+------------------+------------------+-----------------+------------+
| subject                       | Tag: <current>   | Tag: base        | Tag: <current>  | Tag: base  |
+-------------------------------+------------------+------------------+-----------------+------------+
| benchHydrate1Object ()        | 4.000μs (±0.00%) | 4.000μs (±0.00%) | 2.772mb         | 2.772mb    |
| benchExtract1Object ()        | 4.000μs (±0.00%) | 4.000μs (±0.00%) | 2.772mb         | 2.772mb    |
| benchHydrate1000Objects ()    | 2.807ms (±0.00%) | 3.117ms (±0.00%) | 3.035mb         | 3.052mb    |
| benchExtract1000Objects ()    | 2.671ms (±0.00%) | 2.809ms (±0.00%) | 3.032mb         | 3.020mb    |
| benchHydrate1000000Objects () | 1.409s (±0.00%)  | 1.788s (±0.00%)  | 3.035mb         | 3.052mb    |
| benchExtract1000000Objects () | 1.484s (±0.00%)  | 1.755s (±0.00%)  | 3.032mb         | 3.020mb    |
+-------------------------------+------------------+------------------+-----------------+------------+

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

+-------------------------------+---------------------------------------+------------------------------+
|                               | time (kde mode)                       | memory                       |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| subject                       | Tag: <current>    | Tag: base         | Tag: <current>  | Tag: base  |
+-------------------------------+-------------------+-------------------+-----------------+------------+
| benchHydrate1Object ()        | 7.400μs (±0.00%)  | 7.800μs (±0.00%)  | 2.822mb         | 2.820mb    |
| benchExtract1Object ()        | 18.000μs (±0.00%) | 17.800μs (±0.00%) | 2.822mb         | 2.820mb    |
| benchHydrate1000Objects ()    | 4.421ms (±0.00%)  | 4.704ms (±0.00%)  | 2.991mb         | 3.132mb    |
| benchExtract1000Objects ()    | 9.769ms (±0.00%)  | 9.982ms (±0.00%)  | 3.202mb         | 3.201mb    |
| benchHydrate1000000Objects () | 2.364s (±0.00%)   | 2.793s (±0.00%)   | 2.991mb         | 3.132mb    |
| benchExtract1000000Objects () | 7.102s (±0.00%)   | 7.207s (±0.00%)   | 3.202mb         | 3.201mb    |
+-------------------------------+-------------------+-------------------+-----------------+------------+

This comment gets update everytime a new commit comes in!

@DavidBadura DavidBadura added this to the 3.0.0 milestone Oct 6, 2026
@DavidBadura
DavidBadura merged commit 1c413c3 into 3.0.x Oct 6, 2026
16 of 17 checks passed
@DavidBadura
DavidBadura deleted the class-transformer branch October 6, 2026 14:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants