Skip to content

Fix #19943 Make sure to process each interface metadata only once #20078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

lemoinem
Copy link
Contributor

@lemoinem lemoinem commented Sep 28, 2016

Q A
Branch? 2.7+
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #19943
License MIT
Doc PR N/A

Here is the fix for #19943, If you have InterfaceA <- InterfaceB <- Class with a constraint on a method defined on InterfaceA, the constraint and its eventual violations are currently validated and reported twice.

Copy from #19943 (comment):
As far as I can see, the problem seems to arise in LazyLoadingMetadataFactory:

ReflectionClass::getInterfaces() returns both interfaces implemented directly and through inheritance (either through another interface or through a parent class). In the end, the following process occurs:

  1. PriceInterface is parsed and its NotBlank constraint on value is loaded
  2. VariablePriceInterface is parsed and inherits PriceInterface's constraints (which is OK).
  3. ProductPrice is parsed and inherits both PriceInterface and VariablePriceInterface's constraints, which leads to a duplicated NotBlank constraint, one from each Interface.

The Best Way ™️ would be to be able to extract the list of interfaces implemented by a class directly only. However, the process seems a bit intricate... I will start working on it and prepare a PR to that effect. However, if any of you has a better idea, I'm all hears...

TODO:

  • Regression tests to make sure the bug doesn't reappear

@HeahDude
Copy link
Contributor

The label "BC break" should be removed. With some tests it would be 👍 , thanks!

@lemoinem lemoinem force-pushed the fix/validator/19943-interface-metadata-double-processing branch 2 times, most recently from c23bc3b to a696f42 Compare September 28, 2016 21:32
@lemoinem
Copy link
Contributor Author

lemoinem commented Sep 28, 2016

@HeahDude Yes, the label has been added by mistake, I made an error with the PR template... And I can't seem to be able to remove it ;).

Here are the tests, I had to add an Interface so the use case would be adequately represented. I double checked, the tests fail without the patch. I think I'm good for the full review.

The failed tests on appveyor and travis concern the Twig Bridge and Bundle or the SecurityBundle. It's unrelated to this PR.

Status: Needs Review

@lemoinem lemoinem changed the title [WIP] Fix #19943 Make sure to process each interface metadata only once Fix #19943 Make sure to process each interface metadata only once Sep 28, 2016
@@ -19,7 +19,7 @@
* @Assert\GroupSequence({"Foo", "Entity"})
* @Assert\Callback({"Symfony\Component\Validator\Tests\Fixtures\CallbackClass", "callback"})
*/
class Entity extends EntityParent
class Entity extends EntityParent implements EntityInterface, EntityParentInterface
Copy link
Contributor

@HeahDude HeahDude Sep 28, 2016

Choose a reason for hiding this comment

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

I think you should add another interface like OtherEntityInterface (I don't really like the name but you got the point), because implementing both interfaces when one extends another does not make sense, this PR is not trying to fix that case.

Copy link
Contributor

Choose a reason for hiding this comment

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

Or just add the EntityParentInterface to the EntityParent instead :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes... I guess I see your point. Although the fix is going to take care of this case too.

I also added the interface on the EntityParent this case (interface implemented through class inheritance) was already taken care of in the code before my patch, but not in the test. I'll add (yet) another interface to take care of this.

Copy link
Contributor Author

@lemoinem lemoinem Sep 28, 2016

Choose a reason for hiding this comment

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

If I added EntityParentInterface to EntityParent would not have solved the issue because I had added the EntityInterface to it too. I think the new version is better.

@lemoinem lemoinem force-pushed the fix/validator/19943-interface-metadata-double-processing branch from a696f42 to 422ca4e Compare September 28, 2016 22:18
@lemoinem
Copy link
Contributor Author

@HeahDude I updated the tests to take into account your comments. Thanks for the feedback. The interfaces are now named EntityInterfaceA and EntityInterfaceB. I think that's the cleanest way to go for Test Fixtures...

@@ -20,15 +20,17 @@ class LazyLoadingMetadataFactoryTest extends \PHPUnit_Framework_TestCase
{
const CLASSNAME = 'Symfony\Component\Validator\Tests\Fixtures\Entity';
const PARENTCLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityParent';
const INTERFACECLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterface';
const INTERFACEACLASS = 'Symfony\Component\Validator\Tests\Fixtures\EntityInterfaceA';
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please add some underscores to constants names to ease readability, I know you did it for consistency because there weren't any but since you add some more, and longer ones with single-lettered meanings... I think maybe we could start using them :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup, good point. Here you go.

@lemoinem lemoinem force-pushed the fix/validator/19943-interface-metadata-double-processing branch from 422ca4e to a59f2a5 Compare September 28, 2016 22:36
@lemoinem lemoinem force-pushed the fix/validator/19943-interface-metadata-double-processing branch from a59f2a5 to 2f9b65a Compare September 28, 2016 22:39
@HeahDude
Copy link
Contributor

HeahDude commented Sep 29, 2016

LGTM 👍, thanks!

Status: Reviewed

Failures unrelated and should now be fixed by #20079.

@fabpot
Copy link
Member

fabpot commented Sep 30, 2016

Thank you @lemoinem.

@fabpot fabpot merged commit 2f9b65a into symfony:2.7 Sep 30, 2016
fabpot added a commit that referenced this pull request Sep 30, 2016
…ly once (lemoinem)

This PR was merged into the 2.7 branch.

Discussion
----------

Fix #19943 Make sure to process each interface metadata only once

| Q             | A
| ------------- | ---
| Branch?       | 2.7+
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #19943
| License       | MIT
| Doc PR        | N/A

Here is the fix for #19943, If you have `InterfaceA <- InterfaceB <- Class` with a constraint on a method defined on `InterfaceA`, the constraint and its eventual violations are currently validated and reported twice.

Copy from #19943 (comment):
As far as I can see, the problem seems to arise in [`LazyLoadingMetadataFactory`](https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Validator/Mapping/Factory/LazyLoadingMetadataFactory.php#L117-L123):

[`ReflectionClass::getInterfaces()`](http://php.net/manual/en/reflectionclass.getinterfaces.php) returns both interfaces implemented directly and through inheritance (either through another interface or through a parent class). In the end, the following process occurs:

1. `PriceInterface` is parsed and its `NotBlank` constraint on `value` is loaded
2. `VariablePriceInterface` is parsed and inherits `PriceInterface`'s constraints (which is OK).
3. `ProductPrice` is parsed and inherits both `PriceInterface` and `VariablePriceInterface`'s  constraints, which leads to a duplicated `NotBlank` constraint, one from each Interface.

The Best Way ™️ would be to be able to extract the list of interfaces implemented by a class directly only. However, the process seems a bit intricate... I will start working on it and prepare a PR to that effect. However, if any of you has a better idea, I'm all hears...

TODO:
- [x] Regression tests to make sure the bug doesn't reappear

Commits
-------

2f9b65a Fix #19943 Make sure to process each interface metadata only once
This was referenced Oct 3, 2016
@lemoinem lemoinem deleted the fix/validator/19943-interface-metadata-double-processing branch May 4, 2017 00:27
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