8000 [PropertyInfo] Implement Virtual Properties and Asymmetric Visibility support by pan93412 · Pull Request #58960 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[PropertyInfo] Implement Virtual Properties and Asymmetric Visibility support #58960

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,18 @@
return false;
}

if (\PHP_VERSION_ID >= 80400) {
// If the property is virtual and has no setter, it's not writable.
if ($writeAccessRequired && $reflectionProperty->isVirtual() && !$reflectionProperty->hasHook(\PropertyHookType::Set)) {

Check failure on line 718 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:718:66: UndefinedMethod: Method ReflectionProperty::isVirtual does not exist (see https://psalm.dev/022)

Check failure on line 718 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:718:103: UndefinedMethod: Method ReflectionProperty::hasHook does not exist (see https://psalm.dev/022)

Check failure on line 718 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedClass

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:718:111: UndefinedClass: Class, interface or enum named PropertyHookType does not exist (see https://psalm.dev/019)
return false;
}

// If the property has private or protected setter, it's not writable
if ($writeAccessRequired && ($reflectionProperty->isPrivateSet() || $reflectionProperty->isProtectedSet())) {

Check failure on line 723 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:723:67: UndefinedMethod: Method ReflectionProperty::isPrivateSet does not exist (see https://psalm.dev/022)

Check failure on line 723 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:723:106: UndefinedMethod: Method ReflectionProperty::isProtectedSet does not exist (see https://psalm.dev/022)
return false;
}
}

return (bool) ($reflectionProperty->getModifiers() & $this->propertyReflectionFlags);
} catch (\ReflectionException) {
// Return false if the property doesn't exist
Expand Down Expand Up @@ -951,6 +963,23 @@

private function getWriteVisiblityForProperty(\ReflectionProperty $reflectionProperty): string
{
if (\PHP_VERSION_ID >= 80400) {
// If the property is virtual and has no setter, it's private
if ($reflectionProperty->isVirtual() && !$reflectionProperty->hasHook(\PropertyHookType::Set)) {

Check failure on line 968 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:968:38: UndefinedMethod: Method ReflectionProperty::isVirtual does not exist (see https://psalm.dev/022)

Check failure on line 968 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:968:75: UndefinedMethod: Method ReflectionProperty::hasHook does not exist (see https://psalm.dev/022)

Check failure on line 968 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedClass

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:968:83: UndefinedClass: Class, interface or enum named PropertyHookType does not exist (see https://psalm.dev/019)
return PropertyWriteInfo::VISIBILITY_PRIVATE;
}

// If the property has private setter, it's not writable
if ($reflectionProperty->isPrivateSet()) {

Check failure on line 973 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:973:38: UndefinedMethod: Method ReflectionProperty::isPrivateSet does not exist (see https://psalm.dev/022)
return PropertyWriteInfo::VISIBILITY_PRIVATE;
}

// If the property has protected setter, it's protected
if ($reflectionProperty->isProtectedSet()) {

Check failure on line 978 in src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php:978:38: UndefinedMethod: Method ReflectionProperty::isProtectedSet does not exist (see https://psalm.dev/022)
return PropertyWriteInfo::VISIBILITY_PROTECTED;
}
}

if ($reflectionProperty->isPrivate()) {
return PropertyWriteInfo::VISIBILITY_PRIVATE;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php80Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php81Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php82Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\Php84Dummy;
use Symfony\Component\PropertyInfo\Tests\Fixtures\SnakeCaseDummy;
use Symfony\Component\PropertyInfo\Type as LegacyType;
use Symfony\Component\TypeInfo\Type;
Expand Down Expand Up @@ -383,60 +384,75 @@ public static function provideLegacyDefaultValue()
/**
* @dataProvider getReadableProperties
*/
public function testIsReadable($property, $expected)
public function testIsReadable($class, $property, $expected)
{
$this->assertSame(
$expected,
$this->extractor->isReadable('Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy', $property, [])
$this->extractor->isReadable($class, $property, [])
);
}

public static function getReadableProperties()
{
return [
['bar', false],
['baz', false],
['parent', true],
['a', true],
['b', false],
['c', true],
['d', true],
['e', false],
['f', false],
['Id', true],
['id', true],
['Guid', true],
['guid', false],
['element', false],
[Dummy::class, 'bar', false],
[Dummy::class, 'baz', false],
[Dummy::class, 'parent', true],
[Dummy::class, 'a', true],
[Dummy::class, 'b', false],
[Dummy::class, 'c', true],
[Dummy::class, 'd', true],
[Dummy::class, 'e', false],
[Dummy::class, 'f', false],
[Dummy::class, 'Id', true],
[Dummy::class, 'id', true],
[Dummy::class, 'Guid', true],
[Dummy::class, 'guid', false],
[Dummy::class, 'element', false],
[Php84Dummy::class, 'publicPrivateSet', true],
[Php84Dummy::class, 'publicProtectedSet', true],
[Php84Dummy::class, 'publicPublicSet', true],
[Php84Dummy::class, 'protectedPrivateSet', false],
[Php84Dummy::class, 'virtualNoSetHook', true],
// Set-only properties can be still read.
[Php84Dummy::class, 'virtualSetHookOnly', true],
[Php84Dummy::class, 'virtualHook', true],
];
}

/**
* @dataProvider getWritableProperties
*/
public function testIsWritable($property, $expected)
public function testIsWritable($class, $property, $expected)
{
$this->assertSame(
$expected,
$this->extractor->isWritable(Dummy::class, $property, [])
$this->extractor->isWritable($class, $property, [])
);
}

public static function getWritableProperties()
{
return [
['bar', false],
['baz', false],
['parent', true],
['a', false],
['b', true],
['c', false],
['d', false],
['e', true],
['f', true],
['Id', false],
['Guid', true],
['guid', false],
[Dummy::class, 'bar', false],
[Dummy::class, 'baz', false],
[Dummy::class, 'parent', true],
[Dummy::class, 'a', false],
[Dummy::class, 'b', true],
[Dummy::class, 'c', false],
[Dummy::class, 'd', false],
[Dummy::class, 'e', true],
[Dummy::class, 'f', true],
[Dummy::class, 'Id', false],
[Dummy::class, 'Guid', true],
[Dummy::class, 'guid', false],
[Php84Dummy::class, 'publicPrivateSet', false],
[Php84Dummy::class, 'publicProtectedSet', false],
[Php84Dummy::class, 'publicPublicSet', true],
[Php84Dummy::class, 'protectedPrivateSet', false],
[Php84Dummy::class, 'virtualNoSetHook', false],
[Php84Dummy::class, 'virtualSetHookOnly', true],
[Php84Dummy::class, 'virtualHook', true],
];
}

Expand Down Expand Up @@ -587,6 +603,13 @@ public static function readAccessorProvider(): array
[Dummy::class, 'foo', true, PropertyReadInfo::TYPE_PROPERTY, 'foo', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php71Dummy::class, 'foo', true, PropertyReadInfo::TYPE_METHOD, 'getFoo', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php71Dummy::class, 'buz', true, PropertyReadInfo::TYPE_METHOD, 'getBuz', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'publicPrivateSet', true, PropertyReadInfo::TYPE_PROPERTY, 'publicPrivateSet', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'publicProtectedSet', true, PropertyReadInfo::TYPE_PROPERTY, 'publicProtectedSet', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'publicPublicSet', true, PropertyReadInfo::TYPE_PROPERTY, 'publicPublicSet', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'protectedPrivateSet', true, PropertyReadInfo::TYPE_PROPERTY, 'protectedPrivateSet', PropertyReadInfo::VISIBILITY_PROTECTED, false],
[Php84Dummy::class, 'virtualNoSetHook', true, PropertyReadInfo::TYPE_PROPERTY, 'virtualNoSetHook', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'virtualSetHookOnly', true, PropertyReadInfo::TYPE_PROPERTY, 'virtualSetHookOnly', PropertyReadInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'virtualHook', true, PropertyReadInfo::TYPE_PROPERTY, 'virtualHook', PropertyReadInfo::VISIBILITY_PUBLIC, false],
];
}

Expand Down Expand Up @@ -655,6 +678,14 @@ public static function writeMutatorProvider(): array
[Php71DummyExtended2::class, 'string', false, false, '', '', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
[Php71DummyExtended2::class, 'string', true, false, '', '', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
[Php71DummyExtended2::class, 'baz', false, true, PropertyWriteInfo::TYPE_ADDER_AND_REMOVER, null, 'addBaz', 'removeBaz', PropertyWriteInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'publicPrivateSet', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'publicPrivateSet', null, null, PropertyWriteInfo::VISIBILITY_PRIVATE, false],
[Php84Dummy::class, 'publicProtectedSet', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'publicProtectedSet', null, null, PropertyWriteInfo::VISIBILITY_PROTECTED, false],
[Php84Dummy::class, 'publicPublicSet', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'publicPublicSet', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'protectedPrivateSet', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'protectedPrivateSet', null, null, PropertyWriteInfo::VISIBILITY_PRIVATE, false],
// if there is no setter in virtual property, the setter visibility is considered private
[Php84Dummy::class, 'virtualNoSetHook', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'virtualNoSetHook', null, null, PropertyWriteInfo::VISIBILITY_PRIVATE, false],
[Php84Dummy::class, 'virtualSetHookOnly', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'virtualSetHookOnly', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
[Php84Dummy::class, 'virtualHook', false, true, PropertyWriteInfo::TYPE_PROPERTY, 'virtualHook', null, null, PropertyWriteInfo::VISIBILITY_PUBLIC, false],
];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Symfony\Component\PropertyInfo\Tests\Fixtures;

class Php84Dummy
{
public private(set) bool $publicPrivateSet;
public protected(set) bool $publicProtectedSet;
public public(set) bool $publicPublicSet;
protected private(set) bool $protectedPrivateSet;
public bool $virtualNoSetHook { get => true; }
public bool $virtualSetHookOnly { set => $value; }
public bool $virtualHook { get => true; set => $value; }
}
Loading
0