Add lightweight support data objects - #566
Merged
Merged
Conversation
Use focused formatting, analysis, and affected tests for isolated changes instead of requiring the full repository suite at every checkpoint. Reserve composer fix for work that can affect code beyond focused tests, clarify when PHPUnit or ParaTest is appropriate, and point full static analysis at the dedicated composer analyse command.
Introduce Hypervel\Support\DataObject for trusted internal envelopes and high-throughput value mapping without pulling in the full Hypervel Data feature engine. Compile a compact immutable recipe once per used class, then construct through exact named arguments with strict scalar conversion, backed-enum support, configured date handling, nested data objects, and recursive array or JSON output. Keep the class transient and retain no request data, reflection objects, or transformed instance cache. Cover construction precedence, invalid declarations, scalar conversion, enums, relative and inherited object types, date targets, transformation, mutation, serialization, cache ownership, and native failure boundaries.
Add a Laravel-style guide for choosing Support DataObject when trusted internal values need fast typed construction and recursive array or JSON output. Explain the exact-key contract, strict common conversions, nested object behavior, explicit list conversion, and the boundary with Data, Dto, Resource, and DataCollection so developers can select the smaller API without confusing it with the full data package.
Keep a reproducible comparison between the supported Support DataObject and full Hypervel Data across construction, transformation, retained memory, and first-use behavior. Add reverse measurement order for checking ordering bias and preserve equal-output collection scenarios. Remove the frozen historical mapper now that acceptance measurements are complete, avoiding permanent retention of code with known recursion, coercion, and cache defects.
Record a framework-wide audit of integer, float, and boolean input contracts across InteractsWithData, Support DataObject, and Hypervel Data. Only extract a neutral conversion primitive when public semantics genuinely converge, and keep any future Data behavior decision independent so this lightweight implementation does not create an accidental cross-package contract change.
Document the final Support DataObject contract, compiled-recipe design, strict conversion rules, date and nesting behavior, transformation semantics, documentation boundary, benchmark acceptance criteria, and verification coverage. Capture the rejected overlapping or configurable designs so future work preserves the small complementary API without rebuilding a second Hypervel Data engine.
Implement Foundation's existing RequestCastable contract directly on Support DataObject subclasses. A small Support-owned caster now converts already-validated arrays through the concrete object's from() method, preserves null values, and reports the affected request key for invalid input shapes.\n\nKeep the mapper's construction and transformation hot paths unchanged, reject unsupported cast arguments consistently with Hypervel Data, and cover concrete caster selection, null handling, and the inherited public contract.
Exercise Support DataObject classes through FormRequest's generic cast pipeline. The coverage verifies exact and wildcard declarations, sparse list-key preservation, strict scalar conversion, nullable values, safe extraction, and unchanged raw request input.\n\nAlso pin the clear failure for a validated non-array value so request and cast rule drift identifies the affected input and target class.
Show how a FormRequest may return lightweight DataObject instances after it validates the submitted arrays. Document direct casts for one object and wildcard casts for lists, while keeping object-owned validation, mapping, resources, and collection abstractions with the full Data package.\n\nCross-link the data-object and validation guides so developers can choose the appropriate object type without restoring the removed casted() API or Foundation-specific adapters.
Record the final request-casting design, ownership boundary, public contract, documentation, tests, file changes, and verification steps. Replace the earlier blanket rejection of integration adapters with the narrow use of Foundation's existing generic RequestCastable extension point.\n\nKeep validation and richer object behavior in Hypervel Data while documenting that already-validated arrays may become lightweight DataObject instances without a Foundation special case.
Require float-like inputs for integer-backed enums to be finite, integral, and within the platform integer range before conversion. This prevents fractional request or object values from silently selecting a truncated enum case while retaining integral decimal and exponent forms. Cover the shared helper together with the public DataObject and validation boundaries so downstream consumers inherit the corrected behavior without duplicate test matrices.
Clarify the integer-backed enum input contract for lightweight and full Data objects, FormRequest casting, and validation. Integral numeric values remain supported, while fractional values are rejected instead of being truncated to another case.
Feed Support DataObject and full Data the same ISO timestamp in the date and mixed-payload scenarios. This keeps the benchmark focused on framework cost instead of comparing different DateTimeImmutable parse inputs.
Record the shared integer-backed enum conversion rule, its tests and documentation, and the identical timestamp requirement for fair date benchmarks. Keep the plan aligned with the final reviewed implementation and verification boundary.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Summary
This adds a lightweight
Hypervel\Support\DataObjectfor typed mapping in hot paths that do not need the full Hypervel Data feature set.The new class is complementary to
Hypervel\Data\Data. UseDataObjectfor trusted arrays, internal message envelopes, SDK payloads, and per-item value objects. Use Data when the object owns validation, mapping, lazy properties, partials, resources, persistence, or collection behavior.Design
DataObjecthas a deliberately small public API:from(array)constructs an object from constructor parameter names.toArray(),jsonSerialize(), andtoJson()recursively normalize supported values.flushState()clears worker-cached recipes through the existing global cleanup path.Construction compiles one immutable reflection recipe per concrete class and retains it for the worker lifetime. Warm calls execute that compact recipe without container resolution, package metadata, PHPDoc parsing, or per-instance mapper state.
The mapper handles common scalar conversions, backed enums, dates, nested
DataObjectvalues, nullable parameters, and constructor defaults. Invalid scalar input fails instead of silently inventing values. Ambiguous unions and unsupported declarations remain subject to PHP's native type checks rather than introducing a second extensible conversion engine.Transformation reads the current public property values, so ordinary mutation is reflected immediately. It does not retain an output cache on each instance.
The implementation is marked transient, contains no request-scoped state, and publishes compiled recipes only after they are complete.
Form Requests
DataObjectimplements the existing genericRequestCastablecontract. Form requests can therefore cast an already-validated object or each member of a validated list without adding a Foundation special case:The converted values remain available through the existing
validated()andsafe()APIs. Validation continues to run against the submitted arrays before casting.Performance
Measurements below are the median p50 and p95 from three complete alternating-order runs on PHP 8.4.23 with CLI OPcache and JIT disabled. The committed harness warms each scenario and uses repeated samples. Times are nanoseconds per reported operation; the 1,000-item rows are normalized per item.
The acceptance baseline used a frozen copy of the removed 0.3 implementation. The rebuilt implementation is faster across the representative common paths, while fixing its known correctness problems. The historical fixture was deleted after the comparison so it does not become maintained production-adjacent code.
The supported harness compares the rebuilt
DataObjectwith full Data:Construction
DataObjectp50DataObjectp95Transformation
DataObjectp50DataObjectp95Retained instance memory is comparable for untransformed objects and stays fixed after transformation because
DataObjecthas no instance output cache. A small compiled recipe retained 1.7 KB in this run. Fresh-process first use measured 22.8 us p50 and 34.9 us p95.These measurements compare two different contracts. They show the cost boundary for choosing the lightweight mapper; they are not a claim that
DataObjectreplaces Data.Documentation
The data object guide now explains the lightweight and full Data use cases side by side, including construction, conversion, nesting, serialization, and FormRequest casting. The benchmark harness remains in
tests/Benchmarks/Dataso future changes can be measured against both APIs.The implementation plan records the design and verification boundary. The repository TODO also tracks a future audit of strict typed-input conversion across framework entry points.
Verification
Summary by CodeRabbit
New Features
Documentation
Tests