Browse Source

PHP8.1 - "readonly" property modifier support

SpacePossum 3 years ago
parent
commit
c68307577b

+ 2 - 1
.composer-require-checker.json

@@ -14,7 +14,8 @@
         "T_NAME_FULLY_QUALIFIED",
         "T_NAME_QUALIFIED",
         "T_NAME_RELATIVE",
-        "T_NULLSAFE_OBJECT_OPERATOR"
+        "T_NULLSAFE_OBJECT_OPERATOR",
+        "T_READONLY"
     ],
     "php-core-extensions" : [
         "dom", "mbstring", "Phar",

+ 7 - 1
src/AbstractDoctrineAnnotationFixer.php

@@ -218,7 +218,13 @@ abstract class AbstractDoctrineAnnotationFixer extends AbstractFixer implements
             return true;
         }
 
-        while ($tokens[$index]->isGivenKind([T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_NS_SEPARATOR, T_STRING, CT::T_NULLABLE_TYPE])) {
+        $modifierKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_FINAL, T_ABSTRACT, T_NS_SEPARATOR, T_STRING, CT::T_NULLABLE_TYPE];
+
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            $modifierKinds[] = T_READONLY;
+        }
+
+        while ($tokens[$index]->isGivenKind($modifierKinds)) {
             $index = $tokens->getNextMeaningfulToken($index);
         }
 

+ 5 - 1
src/Fixer/ClassNotation/ClassAttributesSeparationFixer.php

@@ -499,7 +499,11 @@ class Sample
 
     private function getFirstTokenIndexOfClassElement(Tokens $tokens, array $class, array $element): int
     {
-        static $modifierTypes = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_ABSTRACT, T_FINAL, T_STATIC, T_STRING, T_NS_SEPARATOR, T_VAR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION];
+        $modifierTypes = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_ABSTRACT, T_FINAL, T_STATIC, T_STRING, T_NS_SEPARATOR, T_VAR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION];
+
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            $modifierTypes[] = T_READONLY;
+        }
 
         $firstElementAttributeIndex = $element['index'];
 

+ 4 - 0
src/Fixer/ClassNotation/SingleClassElementPerStatementFixer.php

@@ -213,6 +213,10 @@ final class Example
     {
         if ('property' === $type) {
             $tokenKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_STATIC, T_VAR, T_STRING, T_NS_SEPARATOR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION];
+
+            if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+                $tokenKinds[] = T_READONLY;
+            }
         } else {
             $tokenKinds = [T_PUBLIC, T_PROTECTED, T_PRIVATE, T_CONST];
         }

+ 23 - 9
src/Fixer/ClassNotation/VisibilityRequiredFixer.php

@@ -100,6 +100,16 @@ class Sample
 
         $propertyTypeDeclarationKinds = [T_STRING, T_NS_SEPARATOR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION];
 
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            $propertyReadOnlyType = T_READONLY;
+            $propertyTypeDeclarationKinds[] = T_READONLY;
+        } else {
+            $propertyReadOnlyType = -999;
+        }
+
+        $expectedKindsGeneric = [T_ABSTRACT, T_FINAL, T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STATIC, T_VAR];
+        $expectedKindsPropertyKinds = array_merge($expectedKindsGeneric, $propertyTypeDeclarationKinds);
+
         foreach (array_reverse($tokensAnalyzer->getClassyElements(), true) as $index => $element) {
             if (!\in_array($element['type'], $this->configuration['elements'], true)) {
                 continue;
@@ -109,18 +119,20 @@ class Sample
             $visibilityIndex = null;
             $staticIndex = null;
             $typeIndex = null;
+            $readOnlyIndex = null;
             $prevIndex = $tokens->getPrevMeaningfulToken($index);
-            $expectedKinds = [T_ABSTRACT, T_FINAL, T_PRIVATE, T_PROTECTED, T_PUBLIC, T_STATIC, T_VAR];
-
-            if ('property' === $element['type']) {
-                $expectedKinds = array_merge($expectedKinds, $propertyTypeDeclarationKinds);
-            }
+            $expectedKinds = 'property' === $element['type']
+                ? $expectedKindsPropertyKinds
+                : $expectedKindsGeneric
+            ;
 
             while ($tokens[$prevIndex]->isGivenKind($expectedKinds)) {
                 if ($tokens[$prevIndex]->isGivenKind([T_ABSTRACT, T_FINAL])) {
                     $abstractFinalIndex = $prevIndex;
                 } elseif ($tokens[$prevIndex]->isGivenKind(T_STATIC)) {
                     $staticIndex = $prevIndex;
+                } elseif ($tokens[$prevIndex]->isGivenKind($propertyReadOnlyType)) {
+                    $readOnlyIndex = $prevIndex;
                 } elseif ($tokens[$prevIndex]->isGivenKind($propertyTypeDeclarationKinds)) {
                     $typeIndex = $prevIndex;
                 } else {
@@ -138,11 +150,13 @@ class Sample
                 continue;
             }
 
-            if (null !== $staticIndex) {
-                if ($this->isKeywordPlacedProperly($tokens, $staticIndex, $index)) {
-                    $index = $staticIndex;
+            $swapIndex = $staticIndex ?? $readOnlyIndex; // "static" property cannot be "readonly", so there can always be at most one swap
+
+            if (null !== $swapIndex) {
+                if ($this->isKeywordPlacedProperly($tokens, $swapIndex, $index)) {
+                    $index = $swapIndex;
                 } else {
-                    $this->moveTokenAndEnsureSingleSpaceFollows($tokens, $staticIndex, $index);
+                    $this->moveTokenAndEnsureSingleSpaceFollows($tokens, $swapIndex, $index);
                 }
             }
 

+ 4 - 0
src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php

@@ -113,6 +113,10 @@ final class SingleSpaceAfterConstructFixer extends AbstractFixer implements Conf
             self::$tokenMap['match'] = T_MATCH;
         }
 
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            self::$tokenMap['readonly'] = T_READONLY;
+        }
+
         $this->fixTokenMap = [];
 
         foreach ($this->configuration['constructs'] as $key) {

+ 8 - 0
src/Fixer/Phpdoc/NoSuperfluousPhpdocTagsFixer.php

@@ -183,6 +183,10 @@ class Foo {
 
         $kindsBeforeProperty = [T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, T_STRING, T_NS_SEPARATOR];
 
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            $kindsBeforeProperty[] = T_READONLY;
+        }
+
         $index = $tokens->getNextMeaningfulToken($docCommentIndex);
 
         if (!$tokens[$index]->isGivenKind($kindsBeforeProperty)) {
@@ -247,6 +251,10 @@ class Foo {
     {
         $propertyModifierKinds = [T_STATIC, T_PRIVATE, T_PROTECTED, T_PUBLIC];
 
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            $propertyModifierKinds[] = T_READONLY;
+        }
+
         $docBlock = new DocBlock($content);
 
         do {

+ 4 - 0
src/Fixer/Phpdoc/PhpdocLineSpanFixer.php

@@ -138,6 +138,10 @@ final class PhpdocLineSpanFixer extends AbstractFixer implements WhitespacesAwar
             CT::T_NULLABLE_TYPE,
         ];
 
+        if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+            $propertyPartKinds[] = T_READONLY;
+        }
+
         do {
             $index = $tokens->getPrevNonWhitespace($index);
         } while ($tokens[$index]->isGivenKind($propertyPartKinds));

+ 4 - 0
src/Fixer/Phpdoc/PhpdocVarWithoutNameFixer.php

@@ -97,6 +97,10 @@ final class Foo
             // We want only doc blocks that are for properties and thus have specified access modifiers next
             $propertyModifierKinds = [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR];
 
+            if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+                $propertyModifierKinds[] = T_READONLY;
+            }
+
             if (!$tokens[$nextIndex]->isGivenKind($propertyModifierKinds)) {
                 continue;
             }

+ 24 - 16
src/Tokenizer/Analyzer/CommentsAnalyzer.php

@@ -156,22 +156,30 @@ final class CommentsAnalyzer
      */
     private function isStructuralElement(Token $token): bool
     {
-        static $skip = [
-            T_PRIVATE,
-            T_PROTECTED,
-            T_PUBLIC,
-            T_VAR,
-            T_FUNCTION,
-            T_ABSTRACT,
-            T_CONST,
-            T_NAMESPACE,
-            T_REQUIRE,
-            T_REQUIRE_ONCE,
-            T_INCLUDE,
-            T_INCLUDE_ONCE,
-            T_FINAL,
-            T_STATIC,
-        ];
+        static $skip;
+
+        if (null === $skip) {
+            $skip = [
+                T_PRIVATE,
+                T_PROTECTED,
+                T_PUBLIC,
+                T_VAR,
+                T_FUNCTION,
+                T_ABSTRACT,
+                T_CONST,
+                T_NAMESPACE,
+                T_REQUIRE,
+                T_REQUIRE_ONCE,
+                T_INCLUDE,
+                T_INCLUDE_ONCE,
+                T_FINAL,
+                T_STATIC,
+            ];
+
+            if (\defined('T_READONLY')) { // @TODO: drop condition when PHP 8.1+ is required
+                $skip[] = T_READONLY;
+            }
+        }
 
         return $token->isClassy() || $token->isGivenKind($skip);
     }

Some files were not shown because too many files changed in this diff