fix(symfony): reject duplicate operation names instead of silently dropping operations#8232
Merged
soyuka merged 6 commits intoJun 3, 2026
Merged
Conversation
…opping operations
Two operations declared on the same resource with an identical explicit `name`
silently overwrote each other in the operations collection, leaving only the
last one alive. The reporter hit this with two custom operations sharing the
URI template `/forms/{id}/submit{._format}` and the same `name`, differing only
by HTTP method (POST and PATCH): the first declaration would 405.
The operation name is the unique key in the operations collection and is also
used verbatim as the Symfony route name, so duplicates can never both work.
The attribute and XML/YAML factories now detect a collision at metadata-build
time and throw `RuntimeException` with a message pointing to the duplicate
name and how to fix it (either drop the duplicate `name` so the framework
disambiguates by method, or set distinct names).
Fixes api-platform#8175
…ad of throwing The previous commit threw a RuntimeException whenever two operations resolved to the same key in the operations collection. That broke legitimate "override an earlier declaration" patterns used across the test suite (e.g. OverriddenOperationDummy re-declares Get with custom serialization groups, producing the same default key _api_OverriddenOperationDummy_get twice). The throw fired during cache warmup and took out every PHPUnit job. Restore the pre-regression behavior selectively: - Same key + same HTTP method: leave untouched. This is the override pattern; the later declaration wins, matching how Operations::add() already behaves. - Same key + different HTTP method (the issue api-platform#8175 case): the colliding entry is auto-suffixed with its method (e.g. _api_/forms/{id}/submit{._format}_patch) so both operations survive in the collection and Symfony route registration. The disambiguation lives on OperationDefaultsTrait so both the attributes/concerns factories (via MetadataCollectionFactoryTrait) and the XML/YAML extractor factory share one implementation. Tests previously asserting the throw now assert that both operations are present with the expected methods.
The disambiguateOperationName calls added two lines to MetadataCollectionFactoryTrait, shifting the existing shortName-duplicate trigger_deprecation from line 252 to 254. PHPUnit baselines key on the line number; out-of-sync baselines stop ignoring the deprecation, so --fail-on-deprecation jobs (no-deprecations, Symfony dev, metadata) fail on a pre-existing, intentionally-baselined message.
…er line" This reverts commit d2a51e3.
…od instead of throwing" This reverts commit b7d98ec.
…ures The fixture declared two parameterless `new Get()` operations on the same resource, which resolved to identical default operation names. In 4.2.0+ the second declaration silently overwrote the first; with the duplicate-name guard added in 271664c this now throws, breaking cache warmup. The first `new Get()` was dead — the second one (with overridden_operation_dummy_get groups) is what every test exercises. Removing it is the same fix users hitting api-platform#8175 need to apply to their own resources. Also bump the shortName-deduplication baseline from line 252 to 254 to match the new trigger_deprecation position once the throw guard's assertOperationNameIsUnique calls have shifted the surrounding code.
Contributor
|
This could also have been the reason why in the Symfony Profiler the tabs on an entity's resources (when two Resource attributes were defined - e.g. one for users, one for admins) would not allow clicking the entity's second resource. Will try to verify that later this week. |
Member
Author
|
Sure let me know ! |
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
namesilently dropped one of them because the operation name is the unique key in the operations collection (and the Symfony route name).RuntimeExceptionwith an actionable message instead of letting the second declaration overwrite the first.Reproduction
Two custom operations sharing
uriTemplate: '/forms/{id}/submit{._format}'andname: '_api_/forms/{id}/submit{._format}'but differing by method (POST and PATCH) — only the second-declared operation survived, the other 405'd.Test plan
Fixes #8175