From 33389787c98ddcf15e1eb76f58ef441ca06f4e62 Mon Sep 17 00:00:00 2001 From: Thorsten Frommen Date: Mon, 21 Oct 2024 21:12:07 +0200 Subject: [PATCH 1/2] Add support for allowing public readonly properties --- .../Classes/ForbiddenPublicPropertySniff.php | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) 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; + } + } From d17390ba5fe983bc2cc9965ca88de607c0e616e8 Mon Sep 17 00:00:00 2001 From: Thorsten Frommen Date: Mon, 21 Oct 2024 22:00:55 +0200 Subject: [PATCH 2/2] Update tests --- .../ForbiddenPublicPropertySniffTest.php | 52 +++++++++++++------ .../Classes/data/forbiddenPublicProperty.php | 15 ++++++ .../data/forbiddenPublicPropertyErrors.php | 8 --- .../data/forbiddenPublicPropertyNoErrors.php | 20 ------- .../forbiddenPublicPropertyPromotedErrors.php | 18 ------- ...orbiddenPublicPropertyPromotedNoErrors.php | 8 --- .../data/forbiddenPublicPropertyReadonly.php | 24 +++++++++ 7 files changed, 75 insertions(+), 70 deletions(-) create mode 100644 tests/Sniffs/Classes/data/forbiddenPublicProperty.php delete mode 100644 tests/Sniffs/Classes/data/forbiddenPublicPropertyErrors.php delete mode 100644 tests/Sniffs/Classes/data/forbiddenPublicPropertyNoErrors.php delete mode 100644 tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedErrors.php delete mode 100644 tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedNoErrors.php create mode 100644 tests/Sniffs/Classes/data/forbiddenPublicPropertyReadonly.php diff --git a/tests/Sniffs/Classes/ForbiddenPublicPropertySniffTest.php b/tests/Sniffs/Classes/ForbiddenPublicPropertySniffTest.php index cba0619a1..bf016bb61 100644 --- a/tests/Sniffs/Classes/ForbiddenPublicPropertySniffTest.php +++ b/tests/Sniffs/Classes/ForbiddenPublicPropertySniffTest.php @@ -7,42 +7,62 @@ class ForbiddenPublicPropertySniffTest extends TestCase { - public function testNoErrors(): void + public function testDefault(): void { - $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyNoErrors.php'); - self::assertNoSniffErrorInFile($report); + $report = self::checkFile(__DIR__ . '/data/forbiddenPublicProperty.php'); + + self::assertSniffError($report, 5, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 6, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); } - public function testErrors(): void + public function testReadonly(): void { - $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyErrors.php'); - - self::assertSame(3, $report->getErrorCount()); + $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyReadonly.php'); self::assertSniffError($report, 5, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); self::assertSniffError($report, 6, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); self::assertSniffError($report, 7, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 8, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 9, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); } - public function testPromotedNoErrors(): void + public function testReadonlyPromoted(): void { - $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyPromotedNoErrors.php', [ + $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyReadonly.php', [ 'checkPromoted' => true, ]); - self::assertNoSniffErrorInFile($report); + + self::assertSniffError($report, 5, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 6, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 7, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 8, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 9, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 12, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 13, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); } - public function testPromotedErrors(): void + public function testReadonlyAllowed(): void { - $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyPromotedErrors.php', [ - 'checkPromoted' => true, + $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyReadonly.php', [ + 'allowReadonly' => true, ]); - self::assertSame(4, $report->getErrorCount()); + self::assertSniffError($report, 5, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 6, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 9, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + } + + public function testReadonlyAllowedPromoted(): void + { + $report = self::checkFile(__DIR__ . '/data/forbiddenPublicPropertyReadonly.php', [ + 'allowReadonly' => true, + 'checkPromoted' => true, + ]); self::assertSniffError($report, 5, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); - self::assertSniffError($report, 13, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); - self::assertSniffError($report, 14, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 6, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 9, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); + self::assertSniffError($report, 12, ForbiddenPublicPropertySniff::CODE_FORBIDDEN_PUBLIC_PROPERTY); } } diff --git a/tests/Sniffs/Classes/data/forbiddenPublicProperty.php b/tests/Sniffs/Classes/data/forbiddenPublicProperty.php new file mode 100644 index 000000000..325c93a97 --- /dev/null +++ b/tests/Sniffs/Classes/data/forbiddenPublicProperty.php @@ -0,0 +1,15 @@ += 8.1 - -function add(int $a, int $b) -{ - return $a + $b; -} - -class Test -{ - private $one, $two; - - public function __construct(public int $promoted) - { - } -} - -class TestSniff -{ - private $one, $two; -} diff --git a/tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedErrors.php b/tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedErrors.php deleted file mode 100644 index 1160802f5..000000000 --- a/tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedErrors.php +++ /dev/null @@ -1,18 +0,0 @@ -= 8.1 - -class Test -{ - public function __construct(public int $promoted1, readonly public string $promoted2) - { - } -} - -class Test2 -{ - public function __construct( - public int $promoted3, - readonly public string $promoted4, - ) - { - } -} diff --git a/tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedNoErrors.php b/tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedNoErrors.php deleted file mode 100644 index 33e91c33b..000000000 --- a/tests/Sniffs/Classes/data/forbiddenPublicPropertyPromotedNoErrors.php +++ /dev/null @@ -1,8 +0,0 @@ -= 8.1 - -class Test -{ - public function __construct(protected int $promoted1, readonly private string $promoted2) - { - } -} diff --git a/tests/Sniffs/Classes/data/forbiddenPublicPropertyReadonly.php b/tests/Sniffs/Classes/data/forbiddenPublicPropertyReadonly.php new file mode 100644 index 000000000..0e465e4ee --- /dev/null +++ b/tests/Sniffs/Classes/data/forbiddenPublicPropertyReadonly.php @@ -0,0 +1,24 @@ += 8.1 + +class Test +{ + var $var; + public $pub; + readonly public string $rps; + public readonly string $prs; + public $pub2; + + public function __construct( + public int $promoted1, + readonly public string $promoted2 + ) + { + } +} + +class Test2 +{ + public function __construct(protected int $promoted1, readonly private string $promoted2) + { + } +}