Skip to content

Commit

Permalink
PHP 8.2 | ObjectDeclarations::getClassProperties(): add support for r…
Browse files Browse the repository at this point in the history
…eadonly classes

PHP 8.2 introduces `readonly` classes. The `readonly` keyword can be combined with the `abstract` or `final` keyword. See: https://3v4l.org/VIXgD

Includes unit tests.

Note: a similar PR has been pulled upstream - see squizlabs/PHP_CodeSniffer 3686.

Ref:
* https://wiki.php.net/rfc/readonly_classes
  • Loading branch information
jrfnl committed Oct 15, 2022
1 parent dd41a73 commit 770fcfb
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 0 deletions.
2 changes: 2 additions & 0 deletions PHPCSUtils/Tokens/Collections.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ class Collections
* DEPRECATED: Modifier keywords which can be used for a class declaration.
*
* @since 1.0.0-alpha1
* @since 1.0.0-alpha4 Added the T_READONLY token for PHP 8.2 readonly classes.
*
* @deprecated 1.0.0-alpha4 Use the {@see Collections::classModifierKeywords()} method instead.
*
Expand All @@ -172,6 +173,7 @@ class Collections
public static $classModifierKeywords = [
\T_FINAL => \T_FINAL,
\T_ABSTRACT => \T_ABSTRACT,
\T_READONLY => \T_READONLY,
];

/**
Expand Down
8 changes: 8 additions & 0 deletions PHPCSUtils/Utils/ObjectDeclarations.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,13 @@ public static function getName(File $phpcsFile, $stackPtr)
* - Handling of PHPCS annotations.
* - Handling of unorthodox docblock placement.
* - Defensive coding against incorrect calls to this method.
* - Support for PHP 8.2 readonly classes.
*
* @see \PHP_CodeSniffer\Files\File::getClassProperties() Original source.
* @see \PHPCSUtils\BackCompat\BCFile::getClassProperties() Cross-version compatible version of the original.
*
* @since 1.0.0
* @since 1.0.0-alpha4 Added support for the PHP 8.2 readonly keyword.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position in the stack of the `T_CLASS`
Expand All @@ -154,6 +156,7 @@ public static function getName(File $phpcsFile, $stackPtr)
* array(
* 'is_abstract' => false, // TRUE if the abstract keyword was found.
* 'is_final' => false, // TRUE if the final keyword was found.
* 'is_readonly' => false, // TRUE if the readonly keyword was found.
* );
* ```
*
Expand All @@ -172,6 +175,7 @@ public static function getClassProperties(File $phpcsFile, $stackPtr)
$properties = [
'is_abstract' => false,
'is_final' => false,
'is_readonly' => false,
];

for ($i = ($stackPtr - 1); $i > 0; $i--) {
Expand All @@ -187,6 +191,10 @@ public static function getClassProperties(File $phpcsFile, $stackPtr)
case \T_FINAL:
$properties['is_final'] = true;
break;

case \T_READONLY:
$properties['is_readonly'] = true;
break;
}
}

Expand Down
17 changes: 17 additions & 0 deletions Tests/Utils/ObjectDeclarations/GetClassPropertiesDiffTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ final
* @phpcs:disable Standard.Cat.SniffName -- Just because.
*/
class ClassWithModifierBeforeDocblock {}

/* testReadonlyClass */
readonly class ReadOnlyClass {}

/* testFinalReadonlyClass */
final readonly class FinalReadOnlyClass extends Foo {}

/* testReadonlyFinalClass */
readonly /*comment*/ final class ReadOnlyFinalClass {}

/* testAbstractReadonlyClass */
abstract readonly class AbstractReadOnlyClass {}

/* testReadonlyAbstractClass */
readonly
abstract
class ReadOnlyAbstractClass {}
42 changes: 42 additions & 0 deletions Tests/Utils/ObjectDeclarations/GetClassPropertiesDiffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,55 @@ public function dataGetClassProperties()
'expected' => [
'is_abstract' => false,
'is_final' => true,
'is_readonly' => false,
],
],
'unorthodox-docblock-placement' => [
'testMarker' => '/* testWithDocblockWithWeirdlyPlacedModifier */',
'expected' => [
'is_abstract' => false,
'is_final' => true,
'is_readonly' => false,
],
],
'readonly' => [
'/* testReadonlyClass */',
[
'is_abstract' => false,
'is_final' => false,
'is_readonly' => true,
],
],
'final-readonly' => [
'/* testFinalReadonlyClass */',
[
'is_abstract' => false,
'is_final' => true,
'is_readonly' => true,
],
],
'readonly-final' => [
'/* testReadonlyFinalClass */',
[
'is_abstract' => false,
'is_final' => true,
'is_readonly' => true,
],
],
'abstract-readonly' => [
'/* testAbstractReadonlyClass */',
[
'is_abstract' => true,
'is_final' => false,
'is_readonly' => true,
],
],
'readonly-abstract' => [
'/* testReadonlyAbstractClass */',
[
'is_abstract' => true,
'is_final' => false,
'is_readonly' => true,
],
],
];
Expand Down
17 changes: 17 additions & 0 deletions Tests/Utils/ObjectDeclarations/GetClassPropertiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,21 @@ public static function setUpTestFile()
self::$caseFile = \dirname(\dirname(__DIR__)) . '/BackCompat/BCFile/GetClassPropertiesTest.inc';
parent::setUpTestFile();
}

/**
* Data provider.
*
* @see testGetClassProperties() For the array format.
*
* @return array
*/
public function dataGetClassProperties()
{
$data = parent::dataGetClassProperties();
foreach ($data as $name => $dataset) {
$data[$name][1]['is_readonly'] = false;
}

return $data;
}
}

0 comments on commit 770fcfb

Please sign in to comment.