Skip to content

Commit

Permalink
Public readonly property is implicitly protected(set)
Browse files Browse the repository at this point in the history
  • Loading branch information
kukulich committed Dec 30, 2024
1 parent edf93e1 commit b3dc8b1
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 2 deletions.
2 changes: 2 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@
<code><![CDATA[isProtected]]></code>
<code><![CDATA[isProtectedSet]]></code>
<code><![CDATA[isPublic]]></code>
<code><![CDATA[isPublic]]></code>
<code><![CDATA[isPublicSet]]></code>
<code><![CDATA[isReadonly]]></code>
<code><![CDATA[isStatic]]></code>
<code><![CDATA[traitExists]]></code>
Expand Down
11 changes: 10 additions & 1 deletion src/Reflection/ReflectionProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ private function assertObject(mixed $object): object
/** @return int-mask-of<ReflectionPropertyAdapter::IS_*> */
private function computeModifiers(PropertyNode $node): int
{
$modifiers = $node->isReadonly() ? ReflectionPropertyAdapter::IS_READONLY : 0;
$modifiers = $node->isReadonly() ? CoreReflectionProperty::IS_READONLY : 0;
$modifiers += $node->isStatic() ? CoreReflectionProperty::IS_STATIC : 0;
$modifiers += $node->isPrivate() ? CoreReflectionProperty::IS_PRIVATE : 0;
$modifiers += ! $node->isPrivate() && $node->isPrivateSet() ? ReflectionPropertyAdapter::IS_PRIVATE_SET_COMPATIBILITY : 0;
Expand All @@ -683,6 +683,15 @@ private function computeModifiers(PropertyNode $node): int
$modifiers += ReflectionPropertyAdapter::IS_FINAL_COMPATIBILITY;
}

if (
! ($modifiers & (ReflectionPropertyAdapter::IS_PROTECTED_SET_COMPATIBILITY | ReflectionPropertyAdapter::IS_PRIVATE_SET_COMPATIBILITY))
&& ! $node->isPublicSet()
&& $node->isPublic()
&& ($modifiers & CoreReflectionProperty::IS_READONLY)
) {
$modifiers += ReflectionPropertyAdapter::IS_PROTECTED_SET_COMPATIBILITY;
}

/** @phpstan-ignore return.type */
return $modifiers;
}
Expand Down
15 changes: 15 additions & 0 deletions test/unit/Fixture/AsymmetricVisibilityImplicitProtectedSet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Roave\BetterReflectionTest\Fixture;

class AsymmetricVisibilityImplicitProtectedSet
{
public public(set) readonly bool $publicPublicSet;
public protected(set) readonly bool $publicProtectedSet;
public private(set) readonly bool $publicPrivateSet;

protected readonly bool $protected;
private readonly bool $private;

public readonly bool $publicImplicitProtectedSet;
}
25 changes: 24 additions & 1 deletion test/unit/Reflection/ReflectionPropertyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ public static function modifierProvider(): array
['protectedProperty', CoreReflectionProperty::IS_PROTECTED],
['privateProperty', CoreReflectionProperty::IS_PRIVATE],
['publicStaticProperty', CoreReflectionProperty::IS_PUBLIC | CoreReflectionProperty::IS_STATIC],
['readOnlyProperty', CoreReflectionProperty::IS_PUBLIC | ReflectionPropertyAdapter::IS_READONLY],
['readOnlyProperty', CoreReflectionProperty::IS_PUBLIC | ReflectionPropertyAdapter::IS_READONLY | ReflectionPropertyAdapter::IS_PROTECTED_SET_COMPATIBILITY],
['finalPublicProperty', CoreReflectionProperty::IS_PUBLIC | ReflectionPropertyAdapter::IS_FINAL_COMPATIBILITY],
];
}
Expand Down Expand Up @@ -989,6 +989,29 @@ public function testAsymmetricVisibilityImplicitFinal(string $propertyName, bool
self::assertSame($isFinal, $property->isFinal());
}

/** @return list<array{0: non-empty-string, 1: bool}> */
public static function asymmetricVisibilityImplicitProtectedSetProvider(): array
{
return [
['publicPublicSet', false],
['publicProtectedSet', true],
['publicPrivateSet', false],
['protected', false],
['publicImplicitProtectedSet', true],
];
}

/** @param non-empty-string $propertyName */
#[DataProvider('asymmetricVisibilityImplicitProtectedSetProvider')]
public function testAsymmetricVisibilityImplicitProtectedSet(string $propertyName, bool $isProtectedSet): void
{
$reflector = new DefaultReflector(new SingleFileSourceLocator(__DIR__ . '/../Fixture/AsymmetricVisibilityImplicitProtectedSet.php', $this->astLocator));
$classInfo = $reflector->reflectClass('Roave\BetterReflectionTest\Fixture\AsymmetricVisibilityImplicitProtectedSet');
$property = $classInfo->getProperty($propertyName);

self::assertSame($isProtectedSet, $property->isProtectedSet());
}

public function testIsAbstract(): void
{
$reflector = new DefaultReflector(new SingleFileSourceLocator(__DIR__ . '/../Fixture/PropertyHooks.php', $this->astLocator));
Expand Down

0 comments on commit b3dc8b1

Please sign in to comment.