Skip to content
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

[PropertyInfo] ConstructorExtractor which has higher priority than PhpDocExtractor and ReflectionExtractor #30128

Closed
wants to merge 3 commits into from

Conversation

karser
Copy link
Contributor

@karser karser commented Feb 9, 2019

Q A
Branch? 4.2
Bug fix? yes
New feature? no
BC breaks? hopefully no
Deprecations? no
Tests pass? yes
Fixed tickets #30053
License MIT

The discussion is in previous PR #30056
In short, when using PhpDocExtractor, it ignores the constructor argument type, although argument types from the constructor are the only types that are valid for the class instantiation.

This PR adds a separate extractor - ConstructorExtractor which is called first (-999) and it attempts to extract the type from constructor only, either from PhpDoc or using reflection.
I added getTypesFromConstructor to PhpDocExtractor and ReflectionExtractor - they implement ConstructorArgumentTypeExtractorInterface interface. ConstructorExtractor aggregates those extractors using compiler pass.

So the flow of control looks like this:

PropertyInfoExtractor::getTypes:
    - ConstructorExtractor::getTypes
        - PhpDocExtractor::getTypesFromConstructor
        - ReflectionExtractor::getTypesFromConstructor
    - PhpDocExtractor::getTypes
    - ReflectionExtractor::getTypes

@nicolas-grekas nicolas-grekas added this to the 4.2 milestone Feb 10, 2019
*
* @return DocBlock
*/
private function filterDocBlockParams(DocBlock $docBlock, $allowedParam)
Copy link
Contributor

Choose a reason for hiding this comment

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

You should use type hints and return types wherever possible as this is for 4.2 and new code if I am right

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@OskarStark I applied the type hints in #30335

@jvasseur
Copy link
Contributor

The problem with this solution is that by replacing the property types with the ones from the constructor we will break updating the entity with the setter (when using object_to_populate for example) because the type will be wrong there instead.

I think the only way to solve this problem without breaking anything would be to hold constructor argument metadata separately from property metadata. Maybe we could go with the ConstructorArgumentTypeExtractorInterface interface introduced in this PR but instead of using a ConstructorExtractor we could call the getTypesFromConstructor directly from the denormalizer.

@karser
Copy link
Contributor Author

karser commented Feb 11, 2019

@jvasseur
How should we approach these controversial cases?
We could restrict inferring the property type from constructor to the original case only (see #30053) where the property is private and doesn't have getter/setter.
If we did that, would it still break updating the entity with the setter?

@jvasseur
Copy link
Contributor

@karser I think we souldn't infer the property type from constructor arguments at all, this would leave the property type guessing only to for properties (and setter/getter) and provide a completely separate API for getting constructor types.

I was originaly not sure about guessing property types from constructor arguments (#25605) but didn't have an example ready to be sure if there would be a problem or not, I guess now we have one.

@fabpot
Copy link
Member

fabpot commented Feb 21, 2019

As discussed in the PR for 3.4, this is a new feature and as such it should target master.

*
* @return Type[]|null
*/
public function getTypesFromConstructor($class, $property);
Copy link
Contributor

Choose a reason for hiding this comment

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

you should add typehints and return type

*
* @return \ReflectionParameter|null
*/
private function getReflectionParameterFromConstructor($property, \ReflectionMethod $reflectionConstructor)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private function getReflectionParameterFromConstructor($property, \ReflectionMethod $reflectionConstructor)
private function getReflectionParameterFromConstructor(string $property, \ReflectionMethod $reflectionConstructor): ?\ReflectionParameter


public function testGetTypes()
{
$this->assertEquals([new Type(Type::BUILTIN_TYPE_STRING)], $this->extractor->getTypes('Foo', 'bar', []));
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use assertSame() here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't, assertSame asserts that two variables reference the same object.
image

* @param int $date Timestamp
* @param \DateTimeInterface $dateObject
*/
public function __construct(\DateTimeZone $timezone, $date, $dateObject, \DateTime $dateTime)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
public function __construct(\DateTimeZone $timezone, $date, $dateObject, \DateTime $dateTime)
public function __construct(\DateTimeZone $timezone, int $date, \DateTimeInterface $dateObject, \DateTime $dateTime)

Copy link
Contributor

Choose a reason for hiding this comment

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

but $dateObject isn't used´ .... 🤔

Copy link
Contributor Author

@karser karser Feb 21, 2019

Choose a reason for hiding this comment

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

@OskarStark That's on purpose, they are used for testing phpdoc types extraction from constructor args even if no such property exists.

@karser
Copy link
Contributor Author

karser commented Feb 21, 2019

closing this in favor of #30335

@karser karser closed this Feb 21, 2019
@karser karser deleted the 4-2-constructor-extractor branch February 21, 2019 16:49
fabpot added a commit that referenced this pull request Aug 26, 2020
…riority than PhpDocExtractor and ReflectionExtractor (karser)

This PR was merged into the 5.2-dev branch.

Discussion
----------

[PropertyInfo] ConstructorExtractor which has higher priority than PhpDocExtractor and ReflectionExtractor

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | yes
| BC breaks?    | hopefully no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #30053
| License       | MIT

Supersedes #30056 #30128

In short, when using PhpDocExtractor, it ignores the constructor argument type, although `argument types from the constructor are the only types that are valid for the class instantiation`.

This PR adds a separate extractor - `ConstructorExtractor` which is called first (-999) and it attempts to extract the type from constructor only, either from PhpDoc or using reflection.
I added `getTypesFromConstructor` to `PhpDocExtractor` and `ReflectionExtractor` - they implement `ConstructorArgumentTypeExtractorInterface` interface. `ConstructorExtractor` aggregates those extractors using compiler pass.

So the flow of control looks like this:
```
PropertyInfoExtractor::getTypes:
    - ConstructorExtractor::getTypes
        - PhpDocExtractor::getTypesFromConstructor
        - ReflectionExtractor::getTypesFromConstructor
    - PhpDocExtractor::getTypes
    - ReflectionExtractor::getTypes
```

Commits
-------

5049e25 Added ConstructorExtractor which has higher priority than PhpDocExtractor and ReflectionExtractor
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.

None yet

6 participants