-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[PropertyInfo] Extract property type from property declaration #31798
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
public int $int; | ||
public ?string $string; | ||
public Dummy $dummy; | ||
public ?Dummy $optionalDummy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you forgot about "callable" property, which is mention in test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this test case should be removed because declaring callable
for a property is not allowed. https://wiki.php.net/rfc/typed_properties_v2
if ($fromDeclaredType = $this->extractFromDeclaredType($class, $property)) { | ||
return $fromDeclaredType; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change causing test fails:
PHPUnit 7.4.5 by Sebastian Bergmann and contributors.
Testing Symfony Property Info Component Test Suite
............................................................... 63 / 188 ( 33%)
.....SSS.................................EEEEEEEEEEEEEEEEEEEEEE 126 / 188 ( 67%)
.............................................................. 188 / 188 (100%)
Time: 7.72 seconds, Memory: 8.00 MB
There were 22 errors:
1) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #0 ('a', null)
ReflectionException: Property a does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
2) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #1 ('b', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property b does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
3) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #2 ('c', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property c does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
4) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #3 ('d', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property d does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
5) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #4 ('e', null)
ReflectionException: Property e does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
6) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #5 ('f', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property f does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
7) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #6 ('donotexist', null)
ReflectionException: Property donotexist does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
8) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #7 ('staticGetter', null)
ReflectionException: Property staticGetter does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
9) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #8 ('staticSetter', null)
ReflectionException: Property staticSetter does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
10) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #9 ('self', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property self does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
11) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractors with data set #10 ('realParent', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property realParent does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:155
12) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp7Type with data set #0 ('foo', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property foo does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:180
13) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp7Type with data set #1 ('bar', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property bar does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:180
14) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp7Type with data set #2 ('baz', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property baz does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:180
15) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp7Type with data set #3 ('buz', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property buz does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:180
16) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp7Type with data set #4 ('biz', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property biz does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:180
17) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp7Type with data set #5 ('donotexist', null)
ReflectionException: Property donotexist does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:180
18) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp71Type with data set #0 ('foo', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property foo does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:200
19) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp71Type with data set #1 ('buz', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property buz does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:200
20) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp71Type with data set #2 ('bar', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property bar does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:200
21) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp71Type with data set #3 ('baz', array(Symfony\Component\PropertyInfo\Type Object (...)))
ReflectionException: Property baz does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:200
22) Symfony\Component\PropertyInfo\Tests\Extractor\ReflectionExtractorTest::testExtractPhp71Type with data set #4 ('donotexist', null)
ReflectionException: Property donotexist does not exist
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:205
/usr/src/app/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:112
/usr/src/app/src/Symfony/Component/PropertyInfo/Tests/Extractor/ReflectionExtractorTest.php:200
ERRORS!
Tests: 188, Assertions: 251, Errors: 22, Skipped: 3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can easily reproduce this tests by running this in src/Symfony/Component/PropertyInfo
:
docker run --rm -it -v $(pwd):/var/www/html devilbox/php-fpm-7.4 bash
composer install # (if you don't have yet)
composer require --dev symfony/test-pack
vendor/bin/simple-phpunit
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed
} catch (\ReflectionException $e) { | ||
return null; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To prevent test fails you can add following code here:
if (!$reflectionClass->hasProperty($property)) {
return null;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or you can expand try-catch block for the whole function content (to prevent errors, which can happen while calling hasType
, getProperty
, getType
etc. which can throw ReflectionException).
I think the best option - add either hasProperty
or expand try-catch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've used the same try/catch
block where I create the reflection class to get the property, and if it doesn't exist, it'll return null
if ($fromDeclaredType = $this->extractFromDeclaredType($class, $property)) { | ||
return $fromDeclaredType; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can easily reproduce this tests by running this in src/Symfony/Component/PropertyInfo
:
docker run --rm -it -v $(pwd):/var/www/html devilbox/php-fpm-7.4 bash
composer install # (if you don't have yet)
composer require --dev symfony/test-pack
vendor/bin/simple-phpunit
I'm wondering if this feature should allow failure for PHP 7.4-dev on travisci since the PHP7.4 is not officially released. What do you think? |
private function extractFromDeclaredType(string $class, string $property) | ||
{ | ||
// to be removed as soon as Symfony bumps the minimum PHP Version to 7.4 | ||
if (version_compare(PHP_VERSION, '7.4.0-dev', '<')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should use \PHP_VERSION_ID < 70400
instead. This matches our conventions, which are set this way because it allows to have the condition resolved at compile-time (and so OPCache can optimize the code)
@@ -185,6 +189,30 @@ public function isInitializable(string $class, string $property, array $context | |||
return false; | |||
} | |||
|
|||
private function extractFromDeclaredType(string $class, string $property) | |||
{ | |||
// to be removed as soon as Symfony bumps the minimum PHP Version to 7.4 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment should be removed. Looking for any check on \PHP_VERSION_ID
is part of our process to bump the min version, so it will be done. The comment would not be checked.
} | ||
|
||
return $type; | ||
} | ||
|
||
private function resolveTypeName(string $name, \ReflectionMethod $reflectionMethod): string | ||
private function resolveTypeName(string $name, ?\ReflectionMethod $reflectionMethod, ?\ReflectionProperty $reflectionProperty): string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as we only need ->getDeclaringClass()
, which is common on both class, I suggest using a single argument accepting \ReflectionMethod|\ReflectionProperty
(and same in extractFromReflectionType
). This will keep the code simpler
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better than contribute with something is learn something contributing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this doesn't seems to be a valid syntax. Is your suggestion something like this?
private function extractFromReflectionType(\ReflectionType $reflectionType, \ReflectionMethod|\ReflectionProperty $reflector): Type {...}
Or you are suggesting to not declare this argument with type:
/**
* @param \ReflectionMethod|\ReflectionProperty $reflector
*/
private function extractFromReflectionType(\ReflectionType $reflectionType, object $reflector): Type {...}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
both \ReflectionMethod
and \ReflectionProperty
implements Reflector
interface, so you can declare it like this:
/**
* @param \ReflectionMethod|\ReflectionProperty $reflector
*/
private function extractFromReflectionType(\ReflectionType $reflectionType, \Reflector $reflector): Type {...}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, both implements \Reflector
but \Reflector
doesn't have the necessary getDeclaringClass()
method. In this case, I don't think that we should use this interface as a typehint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In any case It is far better than just object
. For more specific typehint @param
declaration will be enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As both extractFromReflectionType
and resolveTypeName
are private methods, I'm good to remove any typehint for this argument and declare its type on @param
docblock. What do you think?
{ | ||
if (null === ($reflection = $reflectionMethod ?? $reflectionProperty)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
calling the variable $reflector
would be better, as that's the naming used by PHP for its interface implemented by all reflection classes (but it does not make sense to use it as typehint, as getDeclaringClass
is not part of this interface as other reflection classes don't implement it)
Could someone point me about travis? |
apcu-5.1.16 is not compatible with php 7.4, it needs to install apcu-5.1.17 |
cc @dunglas can you have a look here :) this would fix all "Example values" in API platform for input DTOs, a rather annoying bug i blindly replaced |
A similar PR has already been merged in master: #34557 Closing this one. |
The next minor version of PHP (7.4) will introduce the feature Typed Property where it'll be possible to declare strong type for class properties. With this nice addition, PropertyInfo component is able to extract property type from the property declaration.
Technical Notes
7.4-dev
version (aliased as7.4snapshot
) was configured in travis until the stable release is launch.