diff --git a/SlevomatCodingStandard/Sniffs/Classes/ForbiddenPublicPropertySniff.php b/SlevomatCodingStandard/Sniffs/Classes/ForbiddenPublicPropertySniff.php index 58636062b..282a4104b 100644 --- a/SlevomatCodingStandard/Sniffs/Classes/ForbiddenPublicPropertySniff.php +++ b/SlevomatCodingStandard/Sniffs/Classes/ForbiddenPublicPropertySniff.php @@ -12,6 +12,8 @@ use function array_merge; use const T_PRIVATE; use const T_PROTECTED; +use const T_READONLY; +use const T_SEMICOLON; use const T_VAR; use const T_VARIABLE; @@ -20,6 +22,9 @@ final class ForbiddenPublicPropertySniff implements Sniff public const CODE_FORBIDDEN_PUBLIC_PROPERTY = 'ForbiddenPublicProperty'; + /** @var bool */ + public $allowReadonly = false; + /** @var bool */ public $checkPromoted = false; @@ -51,6 +56,10 @@ public function process(File $file, $variablePointer): void return; } + if ($this->allowReadonly && $this->isReadonlyProperty($file, $variablePointer)) { + return; + } + $errorMessage = 'Do not use public properties. Use method access instead.'; $file->addError($errorMessage, $variablePointer, self::CODE_FORBIDDEN_PUBLIC_PROPERTY); } @@ -74,4 +83,16 @@ private function getPropertyScopeModifier(File $file, int $position): array return $file->getTokens()[$scopeModifierPosition]; } + private function isReadonlyProperty(File $file, int $position): bool + { + $readonlyPosition = TokenHelper::findPrevious($file, [T_READONLY], $position - 1); + if ($readonlyPosition === null) { + return false; + } + + $semicolonPosition = TokenHelper::findNext($file, [T_SEMICOLON], $readonlyPosition + 1, $position - 1); + + return $semicolonPosition === null; + } + }