Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[Serializer] Fix is/has/can accessor naming to strip prefix unless colliding#63136

Merged
nicolas-grekas merged 1 commit into
symfony:6.4from
nicolas-grekas:ser-accessor-collision
Jan 23, 2026
Merged

[Serializer] Fix is/has/can accessor naming to strip prefix unless colliding#63136
nicolas-grekas merged 1 commit into
symfony:6.4from
nicolas-grekas:ser-accessor-collision

Conversation

@nicolas-grekas
Copy link
Copy Markdown
Member

@nicolas-grekas nicolas-grekas commented Jan 20, 2026

Q A
Branch? 6.4
Bug fix? yes
New feature? no
Deprecations? no
Issues Fix #62353
License MIT

Before PR #61097 (Symfony 6.4.26/7.3.5), methods like isPublished() would serialize using the base name "published" by default.

PR #61097 fixed an issue where objects with both $isPublished property and isPublished() method couldn't round-trip properly. However, it caused a regression: ALL is/has/can accessors started using the prefixed form ("isPublished") when a matching property exists, even when there's no actual collision.

This PR fixes the regression by:

  1. Only using the full method name (e.g., "isPublished") when there's an actual collision - another property or accessor that would map to the same base name ("published")
  2. Keeping the base name ("published") when no collision exists, matching pre-6.4.26 behavior for the common case
  3. Extracting shared collision detection logic into AccessorCollisionResolverTrait to ensure consistent behavior between ObjectNormalizer and AttributeLoader

…ng collision detection

Before PR symfony#61097 (Symfony 6.4.26/7.3.5), methods like isPublished() would
serialize using the base name "published" by default.

PR symfony#61097 fixed an issue where objects with both $isPublished property and
isPublished() method couldn't round-trip properly. However, it caused a
regression: ALL is/has/can accessors started using the prefixed form
("isPublished") when a matching property exists, even when there's no
actual collision.

This PR fixes the regression by:

1. Only using the full method name (e.g., "isPublished") when there's an
   actual collision - another property or accessor that would map to the
   same base name ("published")

2. Keeping the base name ("published") when no collision exists, matching
   pre-6.4.26 behavior for the common case

3. Extracting shared collision detection logic into AccessorCollisionResolverTrait
   to ensure consistent behavior between ObjectNormalizer and AttributeLoader
@nicolas-grekas nicolas-grekas merged commit 1db906a into symfony:6.4 Jan 23, 2026
11 of 12 checks passed
@RafaelKr
Copy link
Copy Markdown
Contributor

@nicolas-grekas Thanks for iterating over this, besides from #63187 to me it looks like this is the way to go. Now de- and serialization should behave deterministically, also fixing the initial problem (#61097 (comment)).
Also it's nice, that this is now available via trait.

@nicolas-grekas nicolas-grekas deleted the ser-accessor-collision branch January 26, 2026 16:15
};

// ctype_lower check to find out if method looks like accessor but actually is not, e.g. hash, cancel
if (null === $i || ctype_lower($methodName[$i] ?? 'a') || $method->isStatic()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas is there a reason for ignoring static methods here? This introduces a regression when using serialization groups with static methods (a use case for this when there are different implementations for an interface and more details about the implementation are available via a static method)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please submit a PR to remove that limitation, this case wasn't covered by a test and I didn't think it was use.

} elseif ($this->hasProperty($reflMethod->getDeclaringClass(), $name)) {
$attributeName = $this->getAttributeNameFromAccessor($reflClass, $reflMethod, false);

if ($this->hasPropertyForAccessor($reflMethod->getDeclaringClass(), $name) && (null === $attributeName || $this->hasAttributeNameCollision($reflClass, $attributeName, $name))) {
Copy link
Copy Markdown

@solverat solverat Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nicolas-grekas with that, static methods like public static function select() will be passed to normalization which is probably something we don't want to?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be happy to review a failing test case

nicolas-grekas added a commit that referenced this pull request Feb 5, 2026
…s (digilist)

This PR was merged into the 6.4 branch.

Discussion
----------

[Serializer] Normalize static methods when they have groups

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | https://github.com/symfony/symfony/pull/63136/files#r2761176730
| License       | MIT

PR #63136 introduced a regression that stopped normalization of static methods when they had explicit groups defined. Before that changes, static methods with groups were normalized. Afterwards, these static methods are not normalized anymore.

The regression was caused because previously `ObjectNormalizer::extractAttributes()` ignored static methods while `AttributeLoader::loadClassMetadata()` did not, and the PR introduced a new `getAttributeNameFromAccessor()` method that is shared across those two methods. As a result the `AttributeLoader::loadClassMetadata()` method is now also ignoring static methods.

You can verify this regression by adding the test case from this PR to a commit before the PR was merged (e.g. `1db906a6802c54f8a28f79fbb928d5cb1d5a642c^`)

Commits
-------

78c509c [Serializer] Normalize static methods when they have groups
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants