Skip to content

fix(transformer): cast inner values of array properties when mapping from untyped sources#330

Open
nlemoine wants to merge 1 commit intojolicode:mainfrom
nlemoine:main
Open

fix(transformer): cast inner values of array properties when mapping from untyped sources#330
nlemoine wants to merge 1 commit intojolicode:mainfrom
nlemoine:main

Conversation

@nlemoine
Copy link

@nlemoine nlemoine commented Mar 6, 2026

Hi @joelwurtz,

I finally got the chance to test the feature addressing my original issue. Works great using attributes, thanks again!. However, I just wondered if that could be done using phpdoc only. Because having to leverage attributes feels a bit redundant for those use cases:

 // $source: ['10', '12.5']

 /** @var array<float> */
 public array $age;

FromTargetMappingExtractor::transformTargetType() mirrors target types to source for untyped sources (array, stdClass, LazyMap). For array<int>, the source value type becomes int - identical to target. BuiltinTransformer(int, int) sees matching types and returns input unchanged, skipping the cast.

Solution

In ArrayTransformerFactory, detect when source is untyped and the collection value types are identical mirrored scalars, then override the source value type to mixed. This makes the chain resolve to BuiltinTransformer(mixed, target_type) which generates explicit casts.

Nullable types (array<int|null>) are handled via manual NullableTransformer wrapping since NullableType(mixed) is impossible in Symfony TypeInfo.

Also adds the missing bool entry in BuiltinTransformer::CAST_MAPPING[mixed].

Let me know if this needs change.

…from untyped sources

When mapping from array/stdClass sources to DTOs with PHPDoc-typed array properties (e.g. `@var array<int>`), inner values were not cast because FromTargetMappingExtractor mirrors the target type to source, making both identical. BuiltinTransformer(int, int) then passes values through.

Override source collection value type to mixed in ArrayTransformerFactory when the source is untyped and value types are identical mirrored scalars.

This triggers MixedTransformerFactory → BuiltinTransformer(mixed, target) which generates proper casts.

Also add missing bool entry in BuiltinTransformer CAST_MAPPING for mixed.
@Korbeil
Copy link
Member

Korbeil commented Mar 7, 2026

Hey @nlemoine,
I do like your contribution but I think we should put that behind a feature flag. So we can have the current behavior (with attributes) by default and if you enable the feature you have the behavior where it takes types from phpdoc.

What do you think about this way of doing it ?

@nlemoine
Copy link
Author

nlemoine commented Mar 9, 2026

Hello @Korbeil,

Sure, preserving current behavior is totally legit. I'll update this soon. Note that I also have an upcoming PR to handle array shapes. I was just waiting for this one feedback.

One thing, I would push ee1272a#diff-c48da6ba30bec3efb1de38fc5cf4f40129a4b3d5d1c329f0b6bde2e148855ae9R39 into the current main since it's a "bug" from actual implementation. I can submit a separate PR if needed.

@joelwurtz
Copy link
Member

joelwurtz commented Mar 10, 2026

I do like your contribution but I think we should put that behind a feature flag.

Don't think it need a feature flag for that, we have enough of them, use case / tests seems correct for me 👍 for merging this

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.

3 participants