Object hydration looks like a solved problem until you measure it. You fetch a row from the database, you map the array to an object, and you move on. Most of us treat it as plumbing—unseen, unsexy, and assumed to be fast enough. Then one day you profile a batch job or a queue worker and notice that a surprising chunk of CPU time vanishes into the mapper. That realization is exactly what led to HydraType, a hydrator built around one stubborn rule: a class should never pay for features its properties do not use.

If a field is a plain string that needs zero transformation, the generated code should look like a human wrote it by hand. Direct assignment. No loops, no pipelines, no reflection.

What Hydration Actually Costs

At first glance, turning ['name' => 'Alice', 'age' => 30] into a User object seems trivial. The trouble starts when you want flexibility. Most general-purpose hydrators lean on reflection to inspect classes at runtime. They build metadata maps, negotiate type coercion, and resolve nested object graphs. They also love pipelines. Every value crawls through a sequence of mutators, assertions, and transformers, even if that sequence is empty. The loop structure itself adds overhead.

Fetch a few dozen records and you will never notice. But modern PHP applications routinely process thousands of queue messages, import massive CSV sets, or hydrate deep result sets from Elasticsearch. In those scenarios, a mapper that burns even a few microseconds per field becomes a real bottleneck. A thousand objects with eight fields each multiplies into a tax you feel.

The Zero-Overhead Philosophy

The central idea behind HydraType is feature isolation. Type conversion, nested object hydration, and assertions are all supported, yet none of them are mandatory. If a property needs nothing but a straight copy from array to object, the generated writer contains exactly that. There is no shared pipeline forcing a plain scalar field to wait in line behind validators it does not need.

This is harder to achieve than it sounds. Many libraries share a single execution path for every field because it keeps the codebase small and uniform. HydraType takes the opposite approach. It generates a unique PHP class for each target DTO, emitting precisely the operations that specific class requires. Complexity becomes opt-in, not a blanket tax on every property.

How the Speed Happens

Four concrete choices separate HydraType from both generic reflection-based tools and even other generated approaches.

1. Generate Code Once, Run It Forever

HydraType inspects your target class a single time, then writes a dedicated PHP class tailored to its exact shape. If your DTO contains a public string $name, an int $status, and a private DateTime $createdAt, the generated writer knows all of this upfront. There is no repeated reflection, no string parsing, and no lazy metadata building during runtime. Once OPCache compiles the generated file, the Hydrator is indistinguishable from hand-written PHP.

2. Eliminate Empty Pipelines

Most hydrators structure their work around a runtime pipeline. They maintain an array of callables—mutators, validators, transformers—and iterate over every value. Even when those arrays are empty, the foreach or array_reduce still executes. HydraType removes this entirely. During code generation it checks what a property actually requires. If the operation list is empty, it emits a plain assignment like $object->name = $data['name'];. The runtime never iterates anything, because the generator already did the thinking.

3. Use Closure Scoping to Kill Reflection Writes

Private properties are a classic headache for external mappers. The usual escape hatch is ReflectionProperty::setValue(), but that method carries a heavy penalty compared to direct property access. HydraType sidesteps it by using Closure::bind. The generated writer contains closures scoped to the target class, which lets them touch private and protected properties directly without reflection. The binding happens once during construction; after that, the closure runs at near-native speed.

4. Reuse the Writer

Some libraries instantiate a new strategy or reflection graph for every single object they hydrate. HydraType creates its writer once, then reuses that instance across thousands of objects. The per-object cost drops to the bare minimum of setting property values and returning the result.

The Numbers

No PHP 8.2, a diferença é gritante:

  • HydraType: 267.9 ns
  • Ocramius GeneratedHydrator: 307.9 ns
  • Symfony PropertyNormalizer: 8,499.0 ns
  • Valinor: 10,755.2 ns

O Symfony PropertyNormalizer e o Valinor são ferramentas poderosas, mas são generalistas. O PropertyNormalizer faz parte de um componente de serialização que lida com detecção de formato, normalizadores e grafos de objetos profundos. O Valinor é rigoroso em relação a árvores de tipos e coerção. Esse poder tem um custo. Neste teste, eles foram aproximadamente trinta a quarenta vezes mais lentos que o HydraType. Mesmo o Ocramius GeneratedHydrator, que já utiliza geração de código, fica um pouco atrás devido a diferenças arquiteturais em torno de pipelines e acesso a propriedades.

O contexto importa. Uma única consulta ao banco de dados ou um roundtrip HTTP torna todos esses números insignificantes. Se você estiver hidratando três objetos após uma consulta, perseguir nanossegundos é um desperdício de energia. A diferença torna-se significativa quando você escala. Um processo em lote hidratando cinquenta mil registros, ou um consumidor de fila reconstruindo objetos a partir de arrays de cache, sentirá a diferença entre 267 nanossegundos e dez microssegundos. Multiplique isso por campos e linhas, e o mapper mais rápido pode reduzir segundos de uma tarefa sem alterar nenhuma lógica de negócio.

A Real Lição

A lição aqui não é que todo projeto precise de um hidratador customizado. É que as escolhas de arquitetura se acumulam. Ao gerar código que inclui apenas o que você solicita, o HydraType mantém propriedades simples rápidas, ao mesmo tempo em que oferece um mecanismo de escape para as complexas. Conversão de tipos e objetos aninhados existem quando você precisa deles, mas eles não impõem o mesmo custo de desempenho para cada string escalar no objeto.

Antes de trocar de ferramenta, faça o profiling. Se a hidratação não aparecer em seus traces do mundo real, não há nada para corrigir. Mas se você estiver processando grandes conjuntos de dados através de mappers genéricos e vendo o consumo de CPU disparar, lembre-se de que a atribuição simples em PHP ainda existe. Às vezes, o código mais rápido é simplesmente o código que você mesmo teria escrito, gerado uma vez e depois esquecido.