ocramius/generated-hydrator
Generates fast, specialized hydrator classes for PHP objects, letting you populate and extract properties quickly without reflection overhead. A good fit for ORMs, serializers, and data mappers.
What it actually does. ocramius/generated-hydrator is a compile-time code-generation tool for object hydration. At runtime it reflects once on a target class, generates a dedicated PHP hydrator class into a cache directory, and then includes that class so that hydration and extraction become direct property writes rather than repeated reflection or setter calls. The generation cost is paid once per class per deployment; the hydration cost then approaches a hand-written mapper. Core runtime dependency is laminas/laminas-code (legacy path: zend-code); there is no Laravel binding, config file, facade, or service provider.
Architecture fit. This is infrastructure, not domain code. It belongs behind a port at the persistence/serialization boundary — read-model DTOs, bulk imports, CSV/report exports, ETL jobs, and queue-payload rehydration. It is a poor fit anywhere near Eloquent: hydrate() writes to non-public properties directly, so $fillable mass-assignment protection, casts, mutators, observers, timestamps, and validation do not run. Teams that treat this as a faster Model::hydrate() will silently bypass the framework's guardrails. It should be adopted only for objects that are pure data carriers with no framework semantics attached.
Integration feasibility. Technically straightforward — one configuration object (target directory, autoloader) bound as a singleton, one factory reused for hydrator instances. Roughly a sprint-day of glue code. The real cost is not wiring; it is that with zero dependents in the scanned ecosystem there is no reference integration, no community glue, and no prior art to benchmark against. Every pattern is bespoke and must be reviewed and tested internally.
Technical risk (ranked).
Closure::bind-based hydration ((fn() => $this->x = $y)->call($obj)) delivers most of the speedup with no codegen, no cache directory, and no dependency. The package's core advantage has eroded since it was designed.Key questions before commitment.
composer.json, and does that cover our target runtime for the next 18 months?readonly properties, promoted constructor properties, enums, and uninitialized typed properties — does it fatal or silently skip?Model::hydrate(), symfony/serializer, or bounded-closure hydration at our object counts?Stack fit. Workable on Laravel 10/11 with PHP 8.1+, contingent on verifying the composer constraint actually admits the runtime we ship on — for an archived package this is the first gate, not a formality. Wire it as a singleton factory in a dedicated service provider with the target directory at storage/app/generated-hydrators (gitignored). No config publishing exists; keep the config inside our own config/ file so it is cacheable with config:cache.
Migration path. Do not retrofit. Introduce a single internal interface — ObjectHydrator with hydrate(object $o, array $data): object and extract(object $o): array — with a GeneratedHydratorAdapter behind it. That port is the entire insurance policy: if the package is retired, we swap one class. If replacing an existing serializer, run both implementations behind a feature flag on one read path, diff the outputs, and only then cut over. Never let application code type-hint against GeneratedHydrator\* directly.
Compatibility. Build an explicit compatibility test matrix before adoption rather than discovering semantics in production: readonly properties, uninitialized typed properties with missing array keys, enums, nested objects (does it recurse?), __get/__set, and private-in-parent-class visibility. Behavior here diverges from both Eloquent and symfony/serializer, and divergence discovered late is expensive.
Sequencing.
Model::hydrate(), and our existing serializer on realistic object counts. If the delta is under ~20% on our workloads, stop — the archived-risk premium is not worth it.config:cache in CI, baked into the container image.Maintenance. We become the maintainer on day one. The codebase is small, so periodic patches for PHP-version churn are feasible — but budget a fork pinned to an internal VCS repository or private Packagist entry, plus a named owner. An unowned archived dependency in a hot path is a latent outage.
Support. No upstream support, no issue triage, no CVE feed, no security advisories. We must add it to the internal dependency-risk register and treat any future vulnerability as unfixed-by-default.
Scaling. Hydration throughput scales well once hydrators are warm. The scaling hazard is horizontal: on cold start with N containers, each generates the same files independently — a thundering herd on the cache directory, and potential write contention on shared storage. Eliminate this with deploy-time pre-generation baked into the image, not lazy runtime generation. Hydrator instances are cached per class, so steady-state memory is negligible. OPcache should be configured with validate_timestamps=0 in production for the generated directory, which in turn mandates a cache clear on every deploy.
Failure modes to design against.
Ramp-up. Low technical ramp-up for a mid-level PHP developer — an afternoon with the README. The real ramp-up cost is organizational: because this is a performance optimization with no user-visible feature, a future team will look at it, see an archived package, and remove it without understanding why it exists. Ship it with an ADR containing the benchmark numbers, the port boundary, and an explicit retirement trigger (next PHP major upgrade). Without that artifact, the adoption will not survive contact with the next maintainer.
How can I help you explore Laravel packages today?