AutoMapper version: 10.1.0
PHP version: 8.4
Description
When map_private_properties: true is set in the bundle config, the AutoMapperExtension only updates the ReflectionExtractor, but the two PhpStan extractors (phpstan_source_extractor and phpstan_target_extractor) remain hardcoded with allowPrivateAccess: false.
This means that for a target class with a private promoted constructor parameter typed as a generic collection via PHPDoc, AutoMapper cannot determine the item type. The failure is silent: instead of throwing, it falls back to mapping each item with the Entity → array mapper, resulting in a runtime error deep in business code like Call to a member function getFoo() on array.
Here is the full reproduction section:
Reproduction
automapper.yaml:
automapper:
map_private_properties: true
Source entity:
#[ORM\Entity]
#[Mapper(null, Order::class)]
class OrderEntity
{
/** @var Collection<int, ProductEntity> */
#[ORM\OneToMany(targetEntity: ProductEntity::class, mappedBy: 'order', cascade: ['persist', 'remove'])]
private Collection $products;
/** @return list<ProductEntity> */
public function getProducts(): array
{
return $this->products->getValues();
}
}
Related entity:
#[Mapper(null, Product::class)]
#[ORM\Entity]
class ProductEntity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
public function __construct(
#[ORM\ManyToOne(inversedBy: 'products')]
#[ORM\JoinColumn(nullable: false)]
private readonly OrderEntity $order,
#[ORM\Column(length: 255)]
private readonly string $name,
) {}
public function getId(): ?int { return $this->id; }
public function getName(): string { return $this->name; }
}
Target model:
final readonly class Order
{
/**
* @param list<Product> $products
*/
public function __construct(
private int $id,
private array $products,
) {}
/** @return list<Product> */
public function getProducts(): array { return $this->products; }
}
Related model:
final readonly class Product
{
public function __construct(private int $id, private string $name) {}
public function getId(): int { return $this->id; }
public function getName(): string { return $this->name; }
}
Mapping:
$model = $autoMapper->map($orderEntity, Order::class);
$model->getProducts()[0]->getName(); // Error: Call to a member function getName() on array
Expected behavior
Each item in $products is mapped to a Product model object.
Actual behavior
Each item in $products is a raw array, causing a fatal error when calling any method on it.
Question
Is this an intended limitation, or a bug where map_private_properties: true should also set allowPrivateAccess: true on the PhpStan extractors?
Workaround
Override the service in services.yaml to enable private access on the target PhpStan extractor:
automapper.property_info.phpstan_target_extractor:
class: Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor
arguments:
$accessorPrefixes: []
$allowPrivateAccess: true
This allows the extractor to read the @param list<Product> PHPDoc on private promoted constructor parameters, restoring correct type resolution for collection items.
AutoMapper version: 10.1.0
PHP version: 8.4
Description
When
map_private_properties: trueis set in the bundle config, theAutoMapperExtensiononly updates theReflectionExtractor, but the two PhpStan extractors (phpstan_source_extractorandphpstan_target_extractor) remain hardcoded withallowPrivateAccess: false.This means that for a target class with a private promoted constructor parameter typed as a generic collection via PHPDoc, AutoMapper cannot determine the item type. The failure is silent: instead of throwing, it falls back to mapping each item with the
Entity → arraymapper, resulting in a runtime error deep in business code likeCall to a member function getFoo() on array.Here is the full reproduction section:
Reproduction
automapper.yaml:Source entity:
Related entity:
Target model:
Related model:
Mapping:
Expected behavior
Each item in
$productsis mapped to aProductmodel object.Actual behavior
Each item in
$productsis a rawarray, causing a fatal error when calling any method on it.Question
Is this an intended limitation, or a bug where
map_private_properties: trueshould also setallowPrivateAccess: trueon the PhpStan extractors?Workaround
Override the service in services.yaml to enable private access on the target PhpStan extractor:
This allows the extractor to read the
@param list<Product>PHPDoc on private promoted constructor parameters, restoring correct type resolution for collection items.