Skip to content

Symfony: Add support for #[MapInput] console command DTOs#340

Merged
janedbal merged 8 commits intomasterfrom
feature/symfony-console-map-input
Apr 17, 2026
Merged

Symfony: Add support for #[MapInput] console command DTOs#340
janedbal merged 8 commits intomasterfrom
feature/symfony-console-map-input

Conversation

@janedbal
Copy link
Copy Markdown
Member

@janedbal janedbal commented Apr 10, 2026

Problem

When a Symfony console command uses #[MapInput] on a method parameter, the framework creates a DTO via newInstanceWithoutConstructor() and writes directly to public properties decorated with #[Argument] or #[Option]. It also reads those properties back in setValue(). Methods with #[Interact] on the DTO are called for interactive input. #[MapInput] can also be placed on a DTO property to nest another input DTO. All of this is invisible in user source code, causing false positives.

Approach

Detection is anchored at the #[MapInput] usage site — we only mark DTO properties as used when the DTO class is actually referenced by a #[MapInput] parameter (directly or via nesting). A class with #[Argument]/#[Option] properties that is never referenced by #[MapInput] will still be reported as dead.

Changes

  • Detect #[MapInput] attribute on method parameters
  • Resolve the parameter type to the DTO class
  • Mark properties with #[Argument] or #[Option] as both read and written
  • Mark methods with #[Interact] as used
  • Recurse into nested #[MapInput] properties on the DTO (the host property is marked read+written, cycles prevented via visited set)
  • Add positive test (referenced DTO), negative test (orphaned DTO not referenced by #[MapInput]), and nested-DTO test

Co-Authored-By: Claude Code

@janedbal janedbal changed the title Add support for #[MapInput] console command DTOs Symfony: Add support for #[MapInput] console command DTOs Apr 11, 2026
@janedbal janedbal force-pushed the feature/symfony-console-map-input branch from 87a951c to b217503 Compare April 14, 2026 13:40
@janedbal janedbal changed the base branch from feature/symfony-map-request-payload to master April 14, 2026 13:40
Recurse into DTO properties annotated with #[MapInput] so their
reported as dead. The host property is marked read+written.
@janedbal janedbal force-pushed the feature/symfony-console-map-input branch from b217503 to d76d366 Compare April 15, 2026 11:22
Symfony Console 7.x (which introduced MapInput/Argument/Option
attributes) requires PHP 8.2+, so on PHP 8.1 those classes are
unknown and PHPStan rejects the literal class-string. Uses
reportUnmatched: false so the ignore doesn't fail on PHP 8.2+.
@janedbal janedbal marked this pull request as ready for review April 15, 2026 13:23
Fits under the same "allow referencing any attribute classes" block
as the Class/Method variants.
Only process #[MapInput] parameters when the method is __invoke
and the owning class has #[AsCommand] or extends Command.
An enum cannot be a command, so the isCommand check already handles
that case — no need for an instanceof ReflectionEnum guard.
- Return list instead of mutating &$usages parameter
- Use hasAttribute() for all attribute checks (IdentifierNotFound guard)
- Use createPropertyUsage() instead of inline construction
- Extract getNativeReflection() to local variable
- Drop redundant hasClass check from caller
@janedbal janedbal merged commit bb6d5c9 into master Apr 17, 2026
32 checks passed
@janedbal janedbal deleted the feature/symfony-console-map-input branch April 17, 2026 10:05
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.

1 participant